## Tests for the Phase-4 generation overlays in the Atlas viewer (#960, D-225). ## Referencing AtlasViewer forces both it and AtlasMarkerOverlay to compile, so ## this also guards against parse errors in the overlay rendering code. class_name TestAtlasOverlays extends GdUnitTestSuite # atlas_legend_panel.gd has no class_name (matches atlas_overlay_bar.gd, review # #8), so its GENERATION_LEGEND spec table is read off the preloaded Script # resource rather than a global identifier. const LegendPanelScript := preload("res://ui/implant/apps/atlas/atlas_legend_panel.gd") func test_generation_overlays_registered() -> void: var ids: Array = [] for d: Dictionary in AtlasViewer.OVERLAY_DEFS: ids.append(d["id"]) assert_that(ids).contains( ["gen_l1_rivers", "gen_l1_basins", "gen_l1_attractors", "gen_district"] ) ## T-1046: the district/morphology grid round-trips through the viewer and the ## protocol decode surfaces it; referencing AtlasMarkerOverlay compiles the draw. func test_district_grid_round_trips() -> void: var v: AtlasViewer = auto_free(AtlasViewer.new()) assert_that(v.get_generation_district_grid()).is_null() var grid := { "cols": 2, "rows": 1, "morphology": PackedByteArray([8, 14]), "elev_q": PackedByteArray([10, 90]), } v.set_generation_district_grid(grid) assert_that(v.get_generation_district_grid()).is_equal(grid) # The decoded response dict carries the district_grid key (protocol.gd, T-1046). var decoded: Variant = Protocol.atlas_response_from_raw( {"body_id": "GJ1c", "status": "Ready", "district_grid": grid} ) assert_that((decoded as Dictionary).get("district_grid")).is_equal(grid) func test_generation_data_round_trips() -> void: var v: AtlasViewer = auto_free(AtlasViewer.new()) assert_that(v.get_generation_layer1()).is_null() var mock := { "body_id": "GJ1c", "river_network": {"river_cells": [], "confluences": [], "mouths": []}, "drainage_basins": [], "attractors": [], } v.set_generation_layer1(mock) assert_that(v.get_generation_layer1()).is_equal(mock) func test_implant_pending_start_stop() -> void: var p: ImplantPending = auto_free(ImplantPending.new()) p.start("GENERATING LAYER 1") assert_bool(p.visible).is_true() p.stop() assert_bool(p.visible).is_false() # ============================================================================= # T-960: gen_l2_roads / gen_l3_settlements overlays # ============================================================================= func test_l2_l3_overlays_registered() -> void: var ids: Array = [] for d: Dictionary in AtlasViewer.OVERLAY_DEFS: ids.append(d["id"]) assert_that(ids).contains(["gen_l2_roads", "gen_l3_settlements"]) ## RoadGraphLayer shape pinned to dudley-atlas-server's contract (2026-07-14): ## nodes carry NO `degree` (trimmed as server-internal bookkeeping — the ## overlay derives it from edge endpoints instead, see _draw_gen_roads). func test_road_graph_round_trips() -> void: var v: AtlasViewer = auto_free(AtlasViewer.new()) assert_that(v.get_generation_road_graph()).is_null() var road_graph := { "nodes": [ {"city_id": 1, "position": [10, 20], "kind": "Settlement"}, {"city_id": null, "position": [12, 22], "kind": "Waypoint"}, ], "edges": [ { "from": 0, "to": 1, "path": [[10, 20], [12, 22]], "maintenance": "Administrative", "is_rail": false, "named_route_id": null, }, ], } v.set_generation_road_graph(road_graph) assert_that(v.get_generation_road_graph()).is_equal(road_graph) # Round-trips through the protocol decode too (T-1046 district_grid precedent). var decoded: Variant = Protocol.atlas_response_from_raw( {"body_id": "GJ1c", "status": "Ready", "road_graph": road_graph} ) assert_that((decoded as Dictionary).get("road_graph")).is_equal(road_graph) ## SettlementLayer shape pinned to dudley-atlas-server's contract ## (2026-07-14): wrapped under "settlements" (not "cities"); size_class is ## categorical (Major/Standard/Minor), and is_capital is authored, not ## population-derived. func test_settlements_round_trip() -> void: var v: AtlasViewer = auto_free(AtlasViewer.new()) assert_that(v.get_generation_settlements()).is_null() var settlements := { "settlements": [ { "city_id": 1, "name": "Ridgeback", "position": [30, 40], "size_class": "Minor", "is_capital": false, "is_port": false, }, { "city_id": 2, "name": "Capital City", "position": [35, 45], "size_class": "Major", "is_capital": true, "is_port": true, }, ], } v.set_generation_settlements(settlements) assert_that(v.get_generation_settlements()).is_equal(settlements) var decoded: Variant = Protocol.atlas_response_from_raw( {"body_id": "GJ1c", "status": "Ready", "settlements": settlements} ) assert_that((decoded as Dictionary).get("settlements")).is_equal(settlements) ## Tier-2 replay: a REAL server-generated msgpack blob (server/tests/gen_fixtures.rs ## generate_atlas_layer_response_fixtures, regenerated 2026-07-14 with T-960's ## road_graph/settlements populated) decoded through the actual client path — ## the strongest check that protocol.gd's decode matches the server's wire ## encoding, not just a hand-authored Dictionary the client wrote itself. func test_atlas_response_ready_fixture_decodes_road_graph_and_settlements() -> void: var path := "res://tests/fixtures/msgpack/atlas_response_ready.msgpack" var f := FileAccess.open(path, FileAccess.READ) assert_that(f).is_not_null() var bytes := f.get_buffer(f.get_length()) f.close() var decoded: Variant = Protocol.decode_atlas_layer_response(bytes) assert_that(decoded).is_not_null() var response: Dictionary = decoded assert_str(response.get("body_id")).is_equal("GJ1c") assert_str(response.get("status")).is_equal("Ready") var road_graph: Dictionary = response.get("road_graph") assert_that(road_graph).is_not_null() assert_int((road_graph.get("nodes", []) as Array).size()).is_equal(2) var edges: Array = road_graph.get("edges", []) assert_int(edges.size()).is_equal(1) assert_str((edges[0] as Dictionary).get("maintenance")).is_equal("Administrative") assert_bool((edges[0] as Dictionary).get("is_rail")).is_false() var settlements: Dictionary = response.get("settlements") assert_that(settlements).is_not_null() var entries: Array = settlements.get("settlements", []) assert_int(entries.size()).is_equal(2) var capital: Dictionary = entries[0] assert_str(capital.get("name")).is_equal("Port Aldren") assert_str(capital.get("size_class")).is_equal("Major") assert_bool(capital.get("is_capital")).is_true() var minor: Dictionary = entries[1] assert_str(minor.get("name")).is_equal("Farmstead Rell") assert_str(minor.get("size_class")).is_equal("Minor") assert_bool(minor.get("is_capital")).is_false() # ============================================================================= # D-226 item 3: generation legend panel # ============================================================================= ## Data-driven claim check: every legend entry must reference a real overlay ## id, so a typo or a stale entry can't silently produce a dead legend ## section that never appears. func test_generation_legend_entries_reference_real_overlay_ids() -> void: var overlay_ids: Array = [] for d: Dictionary in AtlasViewer.OVERLAY_DEFS: overlay_ids.append(d["id"]) for spec: Dictionary in LegendPanelScript.GENERATION_LEGEND: assert_that(overlay_ids).contains([spec.get("overlay_id", "")]) assert_bool((spec.get("rows", []) as Array).is_empty()).override_failure_message( "legend entry '%s' has no rows" % spec.get("title", "?") ).is_false() ## Every gen_* toggleable overlay should have at least one legend entry so ## toggling it on never shows silent, unexplained map content. func test_every_gen_overlay_has_a_legend_entry() -> void: var legend_overlay_ids: Array = [] for spec: Dictionary in LegendPanelScript.GENERATION_LEGEND: legend_overlay_ids.append(spec.get("overlay_id", "")) for d: Dictionary in AtlasViewer.OVERLAY_DEFS: var overlay_id: String = d["id"] if overlay_id.begins_with("gen_"): assert_that(legend_overlay_ids).override_failure_message( "overlay '%s' has no GENERATION_LEGEND entry" % overlay_id ).contains([overlay_id]) ## Full-lifecycle test (add_child fires _ready(), which builds the legend ## panel) — invisible at rest, since no generation overlay starts active. func test_legend_panel_hidden_by_default() -> void: var v: AtlasViewer = AtlasViewer.new() add_child(v) assert_bool(v._legend_panel.visible).override_failure_message( "legend panel must be invisible when no generation overlay is active" ).is_false() v.queue_free() ## Toggling a gen_* overlay on must reveal the legend and populate its rows; ## toggling it back off must hide it again (invisible-when-not-needed, D-226). func test_legend_panel_shows_active_overlay_and_hides_when_toggled_off() -> void: var v: AtlasViewer = AtlasViewer.new() add_child(v) v.set_overlay_visible("gen_l3_settlements", true) assert_bool(v._legend_panel.visible).override_failure_message( "legend panel must show once a generation overlay is toggled on" ).is_true() assert_int(v._legend_panel.get_implant_children().size()).override_failure_message( "legend panel must have content rows once populated" ).is_greater(0) v.set_overlay_visible("gen_l3_settlements", false) assert_bool(v._legend_panel.visible).override_failure_message( "legend panel must hide again once its only active overlay is toggled off" ).is_false() v.queue_free()