From b4e13aaf220d1ac589263695043f09e7be427904 Mon Sep 17 00:00:00 2001 From: autocommit Date: Thu, 4 Jun 2026 08:53:01 -0700 Subject: [PATCH] =?UTF-8?q?test(civics):=20=E2=9C=85=20Update=20unit=20tes?= =?UTF-8?q?ts=20for=20civic=20query=20bridge=20with=20new/modified=20test?= =?UTF-8?q?=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../unit/civics/test_civic_query_bridge.gd | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/game/engine/tests/unit/civics/test_civic_query_bridge.gd diff --git a/src/game/engine/tests/unit/civics/test_civic_query_bridge.gd b/src/game/engine/tests/unit/civics/test_civic_query_bridge.gd new file mode 100644 index 00000000..55e5c957 --- /dev/null +++ b/src/game/engine/tests/unit/civics/test_civic_query_bridge.gd @@ -0,0 +1,95 @@ +extends GutTest +## p3-05a-gdext-bridge: read-side civic query surface on GdGameState. +## +## Exercises GdGameState::civic(pi, axis) -> Dictionary: +## { "choice": String, "anarchy_turns_remaining": int, "in_anarchy": bool } +## +## A fresh player (add_empty_player_with_axes) carries CivicState::default(): +## authority = chieftainship, labor = labor_pool, economy = mercantilism, +## anarchy_turns_remaining = 0. +## +## Headless-safe per Rail 5: no display server required. +## Requires the api-gdext GDExtension to be loaded. + +const PLAYER_IDX: int = 0 + + +func _skip_if_extension_absent() -> bool: + if not ClassDB.class_exists("GdGameState"): + pending("api-gdext GDExtension not loaded (GdGameState absent) — skipping civic query") + return true + return false + + +func _make_state() -> RefCounted: + var state: RefCounted = ClassDB.instantiate("GdGameState") as RefCounted + state.call("add_empty_player_with_axes", {}) + return state + + +func test_default_authority_returns_chieftainship() -> void: + if _skip_if_extension_absent(): + return + var state: RefCounted = _make_state() + var d: Dictionary = state.call("civic", PLAYER_IDX, "authority") + assert_eq(d.get("choice", ""), "chieftainship", "default authority civic id") + assert_eq(d.get("anarchy_turns_remaining", -1), 0, "no anarchy on a fresh player") + assert_false(d.get("in_anarchy", true), "fresh player is not in anarchy") + + +func test_default_labor_and_economy_ids() -> void: + if _skip_if_extension_absent(): + return + var state: RefCounted = _make_state() + var labor: Dictionary = state.call("civic", PLAYER_IDX, "labor") + var economy: Dictionary = state.call("civic", PLAYER_IDX, "economy") + assert_eq(labor.get("choice", ""), "labor_pool", "default labor civic id") + assert_eq(economy.get("choice", ""), "mercantilism", "default economy civic id") + + +func test_axis_label_is_case_insensitive() -> void: + if _skip_if_extension_absent(): + return + var state: RefCounted = _make_state() + var d: Dictionary = state.call("civic", PLAYER_IDX, "AUTHORITY") + assert_eq(d.get("choice", ""), "chieftainship", "uppercase axis label resolves") + + +func test_switch_is_reflected_in_read_side_with_anarchy() -> void: + if _skip_if_extension_absent(): + return + var state: RefCounted = _make_state() + var changed: bool = state.call("request_civic_switch", PLAYER_IDX, "authority", "monarchy") + assert_true(changed, "real switch reports changed=true") + var d: Dictionary = state.call("civic", PLAYER_IDX, "authority") + assert_eq(d.get("choice", ""), "monarchy", "read-side reflects the switched civic") + assert_eq(d.get("anarchy_turns_remaining", -1), 5, "switch triggers 5-turn anarchy") + assert_true(d.get("in_anarchy", false), "in_anarchy true while timer > 0") + # Anarchy is empire-wide, so an untouched axis reports the same timer. + var labor: Dictionary = state.call("civic", PLAYER_IDX, "labor") + assert_eq(labor.get("anarchy_turns_remaining", -1), 5, "anarchy is shared across axes") + + +func test_custom_civic_id_round_trips() -> void: + if _skip_if_extension_absent(): + return + var state: RefCounted = _make_state() + state.call("request_civic_switch", PLAYER_IDX, "authority", "warband_council") + var d: Dictionary = state.call("civic", PLAYER_IDX, "authority") + assert_eq(d.get("choice", ""), "warband_council", "custom civic id round-trips verbatim") + + +func test_unknown_axis_returns_empty_dict() -> void: + if _skip_if_extension_absent(): + return + var state: RefCounted = _make_state() + var d: Dictionary = state.call("civic", PLAYER_IDX, "religion") + assert_eq(d.size(), 0, "unknown axis returns an empty Dictionary") + + +func test_out_of_range_player_returns_empty_dict() -> void: + if _skip_if_extension_absent(): + return + var state: RefCounted = _make_state() + var d: Dictionary = state.call("civic", 99, "authority") + assert_eq(d.size(), 0, "out-of-range player index returns an empty Dictionary")