## T-949 tests: Atlas data delivery migration — StarMapRequest/StarMapResponse ## and CityNamesRequest/CityNamesResponse (protocol.gd encode/decode, ## decode_inbound's frame classification, and SimBridge signal routing). ## ## Wire shapes pinned to dudley-atlas-server's contract (2026-07-14): ## AtlasLayerResponse gains road_graph/settlements (Option, "None until ready" ## like district_grid); StarMapResponse is {status, data} (data = a verbatim ## MessagePack re-encoding of star_map_data.json, unwrapped by ## star_map_response_from_raw so callers see {"nodes": [...]} directly); ## CityNamesResponse is {body_id, status, cities: [{city_id, name, is_capital}]} ## with a SolExcluded status as the server-side Sol backstop (D-236). class_name TestAtlasDataDelivery extends GdUnitTestSuite # T-1138: REGION_TEMP_NONE_DC sentinel reused verbatim for district_window's # temp_dc field (D-226 T-1124 amendment §2 — one colorizer/sentinel scheme # across both zoom levels). No class_name on atlas_overlay_colors.gd (review # #8 precedent elsewhere in this suite) — preloaded by path. const AtlasOverlayColors := preload("res://ui/implant/apps/atlas/atlas_overlay_colors.gd") # ============================================================================= # Encode/decode round-trips # ============================================================================= func test_encode_star_map_request_carries_discriminator() -> void: var bytes := Protocol.encode_star_map_request() assert_int(bytes.size()).is_greater(0) var decoded = Messagepack.decode(bytes) assert_that(decoded.status == null).is_true() assert_that(decoded.value.get("star_map")).is_equal(true) func test_star_map_response_from_raw_unwraps_data_to_nodes() -> void: var nodes := [ {"system_id": "GJ 71", "proper_name": "Tau Ceti"}, {"system_id": "GJ 0", "proper_name": "Sol"}, ] var raw := {"status": "Ready", "data": {"_meta": {"system_count": 2}, "nodes": nodes}} var response: Variant = Protocol.star_map_response_from_raw(raw) assert_that(response).is_not_null() assert_str((response as Dictionary).get("status")).is_equal("Ready") assert_that((response as Dictionary).get("nodes")).is_equal(nodes) func test_star_map_response_from_raw_error_status_yields_no_nodes() -> void: var raw := {"status": {"Error": "db unavailable"}, "data": null} var response: Variant = Protocol.star_map_response_from_raw(raw) assert_that(response).is_not_null() assert_str((response as Dictionary).get("status")).is_equal("Error") assert_str((response as Dictionary).get("error")).is_equal("db unavailable") assert_that((response as Dictionary).get("nodes")).is_equal([]) func test_star_map_response_from_raw_rejects_shape_without_status() -> void: assert_that(Protocol.star_map_response_from_raw({"data": {}})).is_null() assert_that(Protocol.star_map_response_from_raw("not a dict")).is_null() func test_decode_star_map_response_bytes_round_trip() -> void: var raw := {"status": "Ready", "data": {"nodes": [{"system_id": "GJ 71"}]}} var encoded = Messagepack.encode(raw) var response: Variant = Protocol.decode_star_map_response(encoded.value) assert_that(response).is_not_null() assert_that((response as Dictionary).get("nodes")).is_equal(raw["data"]["nodes"]) func test_encode_city_names_request_carries_discriminator_and_body_id() -> void: var bytes := Protocol.encode_city_names_request("GJ903b") var decoded = Messagepack.decode(bytes) assert_that(decoded.status == null).is_true() assert_that(decoded.value.get("city_names")).is_equal(true) assert_that(decoded.value.get("body_id")).is_equal("GJ903b") func test_city_names_response_from_raw_round_trips_ready() -> void: var cities := [{"city_id": 1, "name": "Ridgeback", "is_capital": false}] var raw := {"body_id": "GJ903b", "status": "Ready", "cities": cities} var response: Variant = Protocol.city_names_response_from_raw(raw) assert_that(response).is_not_null() assert_str((response as Dictionary).get("body_id")).is_equal("GJ903b") assert_str((response as Dictionary).get("status")).is_equal("Ready") assert_that((response as Dictionary).get("cities")).is_equal(cities) func test_city_names_response_from_raw_sol_excluded() -> void: var raw := {"body_id": "GJ0d", "status": "SolExcluded", "cities": []} var response: Variant = Protocol.city_names_response_from_raw(raw) assert_that(response).is_not_null() assert_str((response as Dictionary).get("status")).is_equal("SolExcluded") func test_city_names_response_from_raw_requires_body_id_and_status() -> void: assert_that(Protocol.city_names_response_from_raw({"body_id": "GJ903b"})).is_null() assert_that(Protocol.city_names_response_from_raw({"status": "Ready"})).is_null() # ============================================================================= # decode_inbound classification — snapshot / atlas / starmap / citynames # ============================================================================= func test_decode_inbound_classifies_snapshot() -> void: var raw := {"tick": 1, "entities": []} var encoded = Messagepack.encode(raw) var inbound: Dictionary = Protocol.decode_inbound(encoded.value) assert_str(inbound.kind).is_equal("snapshot") func test_decode_inbound_classifies_atlas() -> void: var raw := {"body_id": "GJ1c", "status": "Ready", "layer1": null, "district_grid": null} var encoded = Messagepack.encode(raw) var inbound: Dictionary = Protocol.decode_inbound(encoded.value) assert_str(inbound.kind).is_equal("atlas") func test_decode_inbound_classifies_starmap() -> void: # StarMapResponse is the only kind with "data" and no "body_id". var raw := {"status": "Ready", "data": {"nodes": [{"system_id": "GJ 71"}]}} var encoded = Messagepack.encode(raw) var inbound: Dictionary = Protocol.decode_inbound(encoded.value) assert_str(inbound.kind).is_equal("starmap") assert_that((inbound.value as Dictionary).get("nodes")).is_equal(raw["data"]["nodes"]) func test_decode_inbound_classifies_citynames() -> void: # CityNamesResponse is the only kind with "cities" — must classify as # citynames, NOT atlas, even though both carry status + body_id. var raw := {"body_id": "GJ903b", "status": "Ready", "cities": [{"city_id": 1, "name": "Ridgeback"}]} var encoded = Messagepack.encode(raw) var inbound: Dictionary = Protocol.decode_inbound(encoded.value) assert_str(inbound.kind).is_equal("citynames") assert_that((inbound.value as Dictionary).get("body_id")).is_equal("GJ903b") func test_atlas_response_road_graph_and_settlements_passthrough() -> void: var road_graph := { "nodes": [{"position": [1, 2], "kind": "Settlement", "city_id": 5}], "edges": [ { "from": 0, "to": 1, "path": [[1, 2], [3, 4]], "maintenance": "Trade", "is_rail": false, "named_route_id": null, } ], } var settlements := { "settlements": [ { "city_id": 5, "name": "Ridgeback", "position": [1, 2], "size_class": "Minor", "is_capital": false, "is_port": false, } ] } var raw := { "body_id": "GJ1c", "status": "Ready", "road_graph": road_graph, "settlements": settlements, } var decoded: Variant = Protocol.atlas_response_from_raw(raw) assert_that(decoded).is_not_null() assert_that((decoded as Dictionary).get("road_graph")).is_equal(road_graph) assert_that((decoded as Dictionary).get("settlements")).is_equal(settlements) func test_atlas_response_road_graph_and_settlements_default_null() -> void: # A Ready response before road_graph/settlements land on a given server # build must decode cleanly with both fields absent, not error. var decoded: Variant = Protocol.atlas_response_from_raw({"body_id": "GJ1c", "status": "Ready"}) assert_that(decoded).is_not_null() assert_that((decoded as Dictionary).get("road_graph")).is_null() assert_that((decoded as Dictionary).get("settlements")).is_null() # ============================================================================= # T-1138 (D-226 T-1124 amendment): windowed district-resolution regional map # ============================================================================= ## §1: window_center/window_n are OMITTED (not sent as null) when no window is ## requested — this is what makes an old-shaped call (every whole-body-layer ## call site) byte-identical to pre-T-1138 wire traffic. func test_encode_atlas_layer_request_omits_window_fields_by_default() -> void: var bytes := Protocol.encode_atlas_layer_request("GJ1c", "Topography") var decoded = Messagepack.decode(bytes) assert_that(decoded.status == null).is_true() assert_bool(decoded.value.has("window_center")).is_false() assert_bool(decoded.value.has("window_n")).is_false() ## §1: a windowed request carries window_center as a [row, col] pair (the same ## int-pair convention every other position field on this channel already ## uses — road_graph nodes, settlements, Layer-1 river cells) and window_n ## verbatim, unclamped (the server owns the [1, DISTRICT_WINDOW_MAX_N] clamp). func test_encode_atlas_layer_request_carries_window_params() -> void: var bytes := Protocol.encode_atlas_layer_request( "GJ1c", "Topography", Vector2i(140, 260), 32 ) var decoded = Messagepack.decode(bytes) assert_that(decoded.status == null).is_true() assert_that(decoded.value.get("body_id")).is_equal("GJ1c") assert_that(decoded.value.get("window_center")).is_equal([140, 260]) assert_that(decoded.value.get("window_n")).is_equal(32) ## T-1150: window_min_wl_m is OMITTED (not sent as 0) when at its default — ## a windowed request that doesn't pass it (every pre-T-1150 window caller) ## is byte-identical to pre-T-1150 wire traffic, same contract as ## window_center/window_n's own default-omission above. func test_encode_atlas_layer_request_omits_min_wl_by_default() -> void: var bytes := Protocol.encode_atlas_layer_request( "GJ1c", "Topography", Vector2i(140, 260), 32 ) var decoded = Messagepack.decode(bytes) assert_bool(decoded.value.has("window_min_wl_m")).is_false() ## T-1150: a request with an octave cutoff carries it verbatim, unclamped ## (the server owns quantize_min_wl_m() — never trusted from the wire, same ## posture as window_n). func test_encode_atlas_layer_request_carries_min_wl() -> void: var bytes := Protocol.encode_atlas_layer_request( "GJ1c", "Topography", Vector2i(140, 260), 32, 512 ) var decoded = Messagepack.decode(bytes) assert_that(decoded.value.get("window_min_wl_m")).is_equal(512) ## T-1152/T-1153: window_granularity_v2 is OMITTED (not sent as "") when at ## its empty-string default — same byte-compatibility contract as ## window_min_wl_m's own default-omission. func test_encode_atlas_layer_request_omits_granularity_v2_by_default() -> void: var bytes := Protocol.encode_atlas_layer_request( "GJ1c", "Topography", Vector2i(140, 260), 32 ) var decoded = Messagepack.decode(bytes) assert_bool(decoded.value.has("window_granularity_v2")).is_false() ## T-1152/T-1153: a Region-rung request carries window_granularity_v2 as the ## bare string "Region" — the way to express the coarser-than-district rung ## (WindowGranularity's Rust doc), a plain rmp_serde variant-name encoding ## matching RoadNodeKind's existing wire precedent, NOT an integer ## discriminant. ## ## T-1159: the legacy `window_granularity: int` param this call used to also ## pass positionally (between window_n and window_min_wl_m) is retired — see ## atlas_map_protocol.gd's encode_atlas_layer_request doc. func test_encode_atlas_layer_request_carries_granularity_v2_region() -> void: var bytes := Protocol.encode_atlas_layer_request( "GJ1c", "Topography", Vector2i(0, 0), 6400, 0, "Region" ) var decoded = Messagepack.decode(bytes) assert_that(decoded.value.get("window_granularity_v2")).is_equal("Region") ## §2: district_window is a distinct payload (echoes center/n for the ## client's staleness guard) but the codec passthrough is the same shape as ## every sibling layer — raw.get(), no reshaping. Field types follow the ## established district_grid/region_grid convention: u8 arrays decode as ## PackedByteArray, i16 (temp_dc, REGION_TEMP_NONE_DC sentinel scheme) as a ## plain Array (test_region_grid_round_trips' mean_temp_dc precedent). func test_atlas_response_district_window_passthrough() -> void: var window := { "center": [140, 260], "n": 32, "granularity_v2": "Quarter", "min_wl_m": 512, "morphology": PackedByteArray([8, 14, 0, 5]), "elev_q": PackedByteArray([40, 62, 5, 88]), "temp_dc": [120, 95, AtlasOverlayColors.REGION_TEMP_NONE_DC, 60], "moisture_q": PackedByteArray([50, 30, 90, 20]), "vegetation": PackedByteArray([2, 1, 6, 3]), "glaciation": PackedByteArray([0, 0, 1, 2]), } var raw := {"body_id": "GJ1c", "status": "Ready", "district_window": window} var decoded: Variant = Protocol.atlas_response_from_raw(raw) assert_that(decoded).is_not_null() assert_that((decoded as Dictionary).get("district_window")).is_equal(window) ## T-1152/T-1153, design doc §6 encoding-continuity acceptance: a Region-rung ## response passes through the EXACT SAME codec path as District/Quarter — ## granularity_v2 is just another field in the same dict, no special-cased ## decode branch for the coarser rung. This is the direct regression test for ## "one colorizer family, no per-rung palettes": the wire contract itself ## draws no distinction, so nothing downstream (the overlay's ## cell_grid_side_for_window()/_cell_color()) needs a rung-specific decode ## path either. func test_atlas_response_district_window_passthrough_region_rung() -> void: var window := { "center": [0, 0], "n": 6400, "granularity_v2": "Region", "min_wl_m": 0, "morphology": PackedByteArray([8, 14, 0, 5]), "elev_q": PackedByteArray([40, 62, 5, 88]), "temp_dc": [120, 95, AtlasOverlayColors.REGION_TEMP_NONE_DC, 60], "moisture_q": PackedByteArray([50, 30, 90, 20]), "vegetation": PackedByteArray([2, 1, 6, 3]), "glaciation": PackedByteArray([0, 0, 1, 2]), } var raw := {"body_id": "GJ1c", "status": "Ready", "district_window": window} var decoded: Variant = Protocol.atlas_response_from_raw(raw) assert_that(decoded).is_not_null() var district_window: Dictionary = (decoded as Dictionary).get("district_window") assert_that(district_window).is_equal(window) assert_str(str(district_window.get("granularity_v2"))).is_equal("Region") ## A body with no window requested (or not yet derived — §1's background-queue ## serving model: the completion may not have landed yet) must decode with ## district_window absent -> null, same "layer hasn't produced yet" contract ## every other Option layer already has. func test_atlas_response_district_window_default_null() -> void: var decoded: Variant = Protocol.atlas_response_from_raw({"body_id": "GJ1c", "status": "Ready"}) assert_that(decoded).is_not_null() assert_that((decoded as Dictionary).get("district_window")).is_null() # ============================================================================= # SimBridge routing — receive_bytes emits the right signal with the right value # ============================================================================= func test_sim_bridge_routes_star_map_response_to_signal() -> void: var received: Array = [] var handler := func(response: Dictionary) -> void: received.append(response) SimBridge.star_map_received.connect(handler) var raw := {"status": "Ready", "data": {"nodes": [{"system_id": "GJ 71", "proper_name": "Tau Ceti"}]}} var encoded = Messagepack.encode(raw) SimBridge.receive_bytes(encoded.value) SimBridge.star_map_received.disconnect(handler) assert_int(received.size()).is_equal(1) assert_that(received[0].get("nodes")).is_equal(raw["data"]["nodes"]) func test_sim_bridge_routes_city_names_response_to_signal() -> void: var received: Array = [] var handler := func(response: Dictionary) -> void: received.append(response) SimBridge.city_names_received.connect(handler) var raw := { "body_id": "GJ903b", "status": "Ready", "cities": [{"city_id": 1, "name": "Ridgeback", "is_capital": false}], } var encoded = Messagepack.encode(raw) SimBridge.receive_bytes(encoded.value) SimBridge.city_names_received.disconnect(handler) assert_int(received.size()).is_equal(1) assert_that(received[0].get("body_id")).is_equal("GJ903b") func test_sim_bridge_still_routes_snapshots_after_starmap_additions() -> void: # Regression guard: adding the starmap/citynames branches must not steal # ordinary snapshot frames (the 20 Hz hot path). var raw := {"tick": 5, "entities": []} var encoded = Messagepack.encode(raw) SimBridge.receive_bytes(encoded.value) assert_that(SimBridge._last_snapshot).is_not_null() assert_int(SimBridge._last_snapshot.get("tick")).is_equal(5) func test_request_star_map_is_noop_in_test_mode() -> void: # SimBridge defaults to test_mode = true (SR_LIVE unset) — request_star_map # must not error even with no live bridge/server, but it MUST still record # the wish so replay-on-connect can fire later (review H3: this flag's # unreset leak was the cross-suite crash fixed in this same PR). SimBridge.reset_test_state() SimBridge.request_star_map() assert_bool(SimBridge._star_map_wanted).override_failure_message( "test-mode request_star_map must still set the replay flag" ).is_true() SimBridge.reset_test_state() func test_request_city_names_is_noop_in_test_mode() -> void: # Must not error — and must NOT touch the star-map replay flag (guards a # future copy-paste wiring city-names into the wrong flag). SimBridge.reset_test_state() SimBridge.request_city_names("GJ903b") assert_bool(SimBridge._star_map_wanted).is_false() class _CaptureBridge: var sent: Array = [] func send_message(bytes: PackedByteArray) -> int: sent.append(bytes) return OK func test_replay_on_connect_sends_star_map_request_live() -> void: # Review H7: the replay-on-CONNECTED path (_set_state → # _send_star_map_request) is the exact mechanism behind the cross-suite # crash fixed in this PR, and every other test runs test_mode=true where # the guard short-circuits before touching _bridge — proving only # "doesn't crash", never "actually replays". Exercise the positive case # per the test_local_bridge.gd test-mode-flip precedent. var original_test_mode: bool = SimBridge.test_mode var original_state: SimBridge.ConnectionState = SimBridge.state var original_bridge = SimBridge._bridge SimBridge.reset_test_state() var stub := _CaptureBridge.new() SimBridge.test_mode = false SimBridge._bridge = stub SimBridge.state = SimBridge.ConnectionState.CONNECTING SimBridge._star_map_wanted = true SimBridge._set_state(SimBridge.ConnectionState.CONNECTED) assert_int(stub.sent.size()).override_failure_message( "reaching CONNECTED with _star_map_wanted set must replay the request" ).is_equal(1) var decoded = Messagepack.decode(stub.sent[0]) assert_that(decoded.value).is_equal({"star_map": true}) # Restore autoload state for later suites. SimBridge.test_mode = original_test_mode SimBridge._bridge = original_bridge SimBridge.state = original_state SimBridge.reset_test_state()