Files
settled-reach/client/tests/test_atlas_agent_driver.gd
T
jpmschweitzerandClaude Fable 5 52304d3e37 fix(ui): PR #209 review round — current-screen guards, pending-aware settle, one body guard (T-971)
Every screen-targeted intent now routes through one
_require_current_screen() check and returns the structured error shape
instead of silently mutating an off-screen viewer (hoshe's finding:
scroll_rung from the reach screen fired real IPC and reported ok). The
reference driver's fixed 4-frame settle becomes is_pending()-aware with
a 600-frame bound, the keep-waiting decision extracted as a pure
testable function — restoring the proven eyeball-driver discipline. The
terrain_reference guard moves into AtlasApp._on_body_selected(), the
shared tail for double-click, Enter, AND the intent path — closing a
pre-existing click/Enter divergence hoshe caught this PR formalizing;
the intent layer pre-checks via the new SystemScreen.find_body() and
reports structured errors for unknown ids and terrain-less bodies.
after_test() resets AtlasAgentBridge.current_app (tyre's freed-pending
footgun). Suites 58/58 + 14/14; full suite 3,638.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 19:14:02 +02:00

107 lines
4.7 KiB
GDScript

## T-971 smoke test for atlas_agent_driver.gd — the reference `-s` SceneTree
## driver for AtlasAgentInterface. A gdUnit suite cannot boot a SECOND full
## Godot process (the driver's real usage mode: `godot -s
## res://tests/atlas_agent_driver.gd -- --jobs ... --output ...`) without
## spawning a nested engine instance, which is slow/fragile and inconsistent
## with how this project already runs its OTHER `-s` drivers
## (visual_capture.gd/atlas_shots.json — manual `make` targets, never inside
## the gdUnit suite tests/run-godot invokes). The feasible headless smoke
## here is the driver's own PURE helper logic: job-file loading and
## result-array writing, both `static func`s with no SceneTree dependency —
## exercising the exact code path a real run's job-list parsing and
## output-writing go through, without booting a second engine.
class_name TestAtlasAgentDriver
extends GdUnitTestSuite
const DriverScript := preload("res://tests/atlas_agent_driver.gd")
var _tmp_dir: String = ""
func before_test() -> void:
_tmp_dir = "user://test_atlas_agent_driver/%d/" % Time.get_ticks_usec()
DirAccess.make_dir_recursive_absolute(_tmp_dir)
func after_test() -> void:
var d := DirAccess.open(_tmp_dir)
if d == null:
return
for f: String in d.get_files():
d.remove(f)
DirAccess.remove_absolute(_tmp_dir.rstrip("/"))
func test_load_jobs_reads_the_shipped_smoke_fixture() -> void:
var jobs: Array = DriverScript._load_jobs(
"res://tests/fixtures/atlas_agent_smoke_jobs.json"
)
assert_int(jobs.size()).is_equal(4)
assert_str((jobs[0] as Dictionary).get("intent", "")).is_equal("open_atlas")
assert_str((jobs[1] as Dictionary).get("intent", "")).is_equal("observe")
func test_load_jobs_returns_empty_array_for_a_missing_file() -> void:
var jobs: Array = DriverScript._load_jobs("res://tests/fixtures/does_not_exist.json")
assert_int(jobs.size()).is_equal(0)
func test_write_output_round_trips_through_json() -> void:
var out_path: String = _tmp_dir + "results.json"
var results: Array = [
{"intent": "observe", "params": {}, "result": {"screen": "reach"}},
{"intent": "select_system", "params": {"system_id": "GJ1"}, "result": {"ok": true}},
]
DriverScript._write_output(out_path, results)
var f := FileAccess.open(out_path, FileAccess.READ)
assert_object(f).is_not_null()
var parsed: Variant = JSON.parse_string(f.get_as_text())
f.close()
assert_that(parsed).is_equal(results)
# =============================================================================
# PR #209 review (Hoshe finding 2) — should_keep_waiting()'s bounded-fallback
# logic. Pure/static, no SceneTree dependency, so the boundary conditions are
# directly testable without a real frame loop or a real StepCanvasRequest.
# =============================================================================
func test_should_keep_waiting_true_while_pending_and_under_the_frame_budget() -> void:
assert_bool(DriverScript.should_keep_waiting(true, 0, 600)).is_true()
assert_bool(DriverScript.should_keep_waiting(true, 599, 600)).is_true()
func test_should_keep_waiting_false_once_no_longer_pending() -> void:
assert_bool(DriverScript.should_keep_waiting(false, 0, 600)).is_false()
func test_should_keep_waiting_false_once_the_frame_budget_is_exhausted() -> void:
# Still pending, but waited has reached max_frames — the bounded fallback
# must stop here rather than hanging indefinitely on a genuinely-stuck
# request (the whole reason SETTLE_MAX_FRAMES exists).
assert_bool(DriverScript.should_keep_waiting(true, 600, 600)).is_false()
assert_bool(DriverScript.should_keep_waiting(true, 601, 600)).is_false()
## The InputSwallower inner class (T-1157 inventory item 1) — a plain Node
## subclass with no SceneTree/window dependency for its OWN logic
## (set_input_as_handled() requires a live viewport to call meaningfully, but
## the class must at least instantiate and expose the two input hooks by
## name — this is the structural smoke check; the swallowing BEHAVIOR itself
## is exercised implicitly every time atlas_agent_driver.gd actually runs,
## per its own header doc).
func test_input_swallower_inner_class_instantiates_and_has_both_input_hooks() -> void:
# A nested `class X: extends Y` inside a script is exposed as a
# script-level constant on the OUTER GDScript resource — reachable via
# plain dot access on the preloaded DriverScript, same as any other
# nested-class reference in this codebase's own suites (e.g.
# GdUnitObjectAssertImplTest.gd's MyNode/MyExtendedNode pattern), just
# via a loaded resource instead of a same-file bare identifier.
var instance: Node = DriverScript._InputSwallower.new()
assert_bool(instance.has_method("_input")).is_true()
assert_bool(instance.has_method("_unhandled_input")).is_true()
instance.free()