## T-1138 (D-226 T-1124 amendment §1-§5): tests for AtlasWindowViewer + its ## companion request/cache orchestration (atlas_window_request.gd) — pure ## logic against hand-built AtlasLayerResponse-shaped dicts, matching the ## ticket's "unit tests against hand-built response dicts" instruction. Live ## end-to-end verification against a real spawned server is separate ## (companion-run evidence, not gdUnit — this file never touches SimBridge's ## live-mode path, only the response-handling/cache/overlay logic that path ## eventually feeds). class_name TestAtlasWindowViewer extends GdUnitTestSuite # atlas_window_request.gd has no class_name (review #8 precedent throughout # this cluster) — preloaded once here, not re-load()ed per test (gdlint # duplicated-load). const AtlasWindowRequest := preload("res://ui/implant/apps/atlas/atlas_window_request.gd") ## Build a hand-authored DistrictWindowLayer dict (n=2, matching the shape ## district_grid/region_grid fixtures already use elsewhere in this suite). static func _mock_window(center: Vector2i, n: int = 2) -> Dictionary: return { "center": [center.x, center.y], "n": n, "morphology": PackedByteArray([8, 14, 0, 1]), "elev_q": PackedByteArray([40, 90, 5, 60]), "temp_dc": [120, 95, -32768, 60], "moisture_q": PackedByteArray([50, 30, 90, 20]), "vegetation": PackedByteArray([2, 1, 6, 3]), "glaciation": PackedByteArray([0, 0, 1, 2]), } static func _mock_response(body_id: String, window: Variant) -> Dictionary: return {"body_id": body_id, "status": "Ready", "district_window": window} # ============================================================================= # AtlasWindowViewer — entry + overlay defs # ============================================================================= func test_enter_with_no_response_leaves_window_null_and_pending() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20)) assert_that(v.get_district_window()).is_null() ## Feeding a matching Ready response (via the SAME SimBridge.atlas_layers_received ## routing path the viewer subscribes to in _ready()) must populate the window. func test_enter_then_matching_response_populates_window() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2) var window: Dictionary = _mock_window(Vector2i(10, 20), 2) SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", window)) assert_that(v.get_district_window()).is_equal(window) ## A response for a DIFFERENT body must not populate the window — the ## body_id scoping AtlasWindowRequest.on_response() checks. func test_response_for_different_body_is_ignored() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2) var window: Dictionary = _mock_window(Vector2i(10, 20), 2) SimBridge.atlas_layers_received.emit(_mock_response("GJ_wrong_body", window)) assert_that(v.get_district_window()).is_null() ## A response whose echoed (center, n) does NOT match what was last asked for ## is stale — §2's race-condition guard. Simulates a superseded-by-a-later-pan ## response arriving after the fact. func test_response_with_mismatched_echo_is_discarded_as_stale() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2) var stale_window: Dictionary = _mock_window(Vector2i(99, 99), 2) # wrong center SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", stale_window)) assert_that(v.get_district_window()).is_null() ## §1: an as-yet-underived window rides as `district_window: None` inside a ## Ready response — this is NOT an error, the viewer just keeps waiting ## (get_district_window() stays null, no crash, no window content shown). func test_ready_response_with_null_district_window_keeps_waiting() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.enter({"body_id": "GJ380c"}, {}, Vector2i(10, 20), 2) SimBridge.atlas_layers_received.emit(_mock_response("GJ380c", null)) assert_that(v.get_district_window()).is_null() func test_overlay_defs_include_the_three_toggle_ids() -> void: var ids: Array = [] for d: Dictionary in AtlasWindowViewer.OVERLAY_DEFS: ids.append(d["id"]) assert_that(ids).contains(["gen_dw_temp", "gen_dw_moisture", "gen_dw_veg"]) ## Glaciation is explicitly NOT a toggle id (§5: "an always-on modifier, not ## a toggle") — a regression here would silently re-introduce it as a switch. func test_overlay_defs_do_not_include_glaciation() -> void: var ids: Array = [] for d: Dictionary in AtlasWindowViewer.OVERLAY_DEFS: ids.append(d["id"]) assert_that(ids).not_contains(["gen_dw_glaciation", "gen_dw_ice"]) func test_set_overlay_visible_toggles_and_is_overlay_visible_reflects_it() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) assert_bool(v.is_overlay_visible("gen_dw_temp")).is_false() v.set_overlay_visible("gen_dw_temp", true) assert_bool(v.is_overlay_visible("gen_dw_temp")).is_true() func test_set_overlay_visible_unknown_id_is_a_noop() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.set_overlay_visible("not_a_real_overlay", true) assert_bool(v.is_overlay_visible("not_a_real_overlay")).is_false() # ============================================================================= # T-1120 capture-API parity (the ticket's explicit note: must survive here too) # ============================================================================= func test_set_view_and_getters_round_trip() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.set_view(2.5, Vector2(30.0, -10.0)) assert_that(v.get_view_zoom()).is_equal_approx(2.5, 0.001) assert_that(v.get_view_offset()).is_equal(Vector2(30.0, -10.0)) func test_set_view_clamps_to_min_max_zoom() -> void: var v: AtlasWindowViewer = auto_free(AtlasWindowViewer.new()) add_child(v) v.set_view(0.01, Vector2.ZERO) assert_that(v.get_view_zoom()).is_equal_approx(AtlasWindowViewer.MIN_ZOOM, 0.001) v.set_view(1000.0, Vector2.ZERO) assert_that(v.get_view_zoom()).is_equal_approx(AtlasWindowViewer.MAX_ZOOM, 0.001) # ============================================================================= # AtlasWindowRequest — cache reuse (§4's Esc-then-re-enter / pan-back hit) # ============================================================================= func test_window_request_cache_hit_emits_synchronously_no_pending() -> void: var owner_stub := RefCounted.new() var req = auto_free(AtlasWindowRequest.new(owner_stub)) add_child(req) # Prime the cache directly (bypassing the network path) — the ticket's # own instruction: unit test against hand-built response dicts. req.get_cache().put("GJ380c", Vector2i(1, 1), 2, _mock_window(Vector2i(1, 1), 2)) var received: Array = [] req.window_ready.connect(func(w: Dictionary) -> void: received.append(w)) req.request_now("GJ380c", Vector2i(1, 1), 2) assert_int(received.size()).is_equal(1) assert_bool(req.is_pending()).override_failure_message( "a cache hit must never leave the request pending" ).is_false() func test_window_request_cache_miss_leaves_pending_true() -> void: var owner_stub := RefCounted.new() var req = auto_free(AtlasWindowRequest.new(owner_stub)) add_child(req) req.request_now("GJ380c", Vector2i(5, 5), 2) assert_bool(req.is_pending()).is_true() ## on_response() with a matching Ready+window response resolves the pending ## request AND populates the cache — verified by a second request_now() call ## for the same (center, n) becoming a cache hit with zero additional pending. func test_on_response_resolves_and_populates_cache_for_next_request() -> void: var owner_stub := RefCounted.new() var req = auto_free(AtlasWindowRequest.new(owner_stub)) add_child(req) req.request_now("GJ380c", Vector2i(2, 2), 2) assert_bool(req.is_pending()).is_true() var window: Dictionary = _mock_window(Vector2i(2, 2), 2) req.on_response(_mock_response("GJ380c", window)) assert_bool(req.is_pending()).is_false() # Re-request the SAME (body, center, n) — must be a cache hit, no pending. req.request_now("GJ380c", Vector2i(2, 2), 2) assert_bool(req.is_pending()).override_failure_message( "a second request for an already-resolved window must hit the cache" ).is_false()