test(scenes): Add test script and scene for verifying era 7 severance logic

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
autocommit 2026-04-29 09:59:55 -07:00
parent f561ec4cc9
commit 6f3cfb5b38
2 changed files with 171 additions and 0 deletions

View file

@ -0,0 +1,165 @@
extends Node
## p3-01 bullet 11: era_7 courier severance proof.
##
## Tests two sub-cases:
## 1. Era_7 courier (8 hex/turn) over a 4-hex gap with intact route delivers
## in a single step.
## 2. A courier already marked intercepted=true produces no further events
## (terminal state per spec: intercepted is non-refundable, no re-processing).
##
## NOTE: The deterministic tile-severance path (pillaging steam_track mid-route)
## requires `stamp_tile_improvement` on GdGameState, not yet exposed to GDScript.
## That path is covered by the Rust unit test
## `steam_track_pillage_intercepts_on_next_step` in mc-turn/src/courier_resolver.rs.
var _pass_count: int = 0
var _fail_count: int = 0
func _ready() -> void:
if not ClassDB.class_exists("GdTradeLedger"):
print("[SKIP] GdTradeLedger not registered — GDExtension not loaded")
get_tree().quit(0)
return
_run_proof()
_print_results()
get_tree().quit(0 if _fail_count == 0 else 1)
func _run_proof() -> void:
_test_era7_delivers_intact_route()
_test_intercepted_route_is_terminal()
func _test_era7_delivers_intact_route() -> void:
var state: RefCounted = ClassDB.instantiate("GdGameState") as RefCounted
if not _assert("era7_intact: GdGameState instantiates", state != null):
return
state.call("create_grid", 10, 10)
state.call("add_player_militarist", 0, 0)
state.call("add_player_militarist", 4, 0)
var map_view_inst: RefCounted = ClassDB.instantiate("GdCourierMapView") as RefCounted
if not _assert("era7_intact: GdCourierMapView instantiates", map_view_inst != null):
return
var gd_map_view: RefCounted = map_view_inst.call("from_game_state", state) as RefCounted
if not _assert("era7_intact: from_game_state returns non-null", gd_map_view != null):
return
var gd_trade: RefCounted = ClassDB.instantiate("GdTrade") as RefCounted
if not _assert("era7_intact: GdTrade instantiates", gd_trade != null):
return
# Era_7 moves 8 hexes/turn via straight-line; 4-hex gap → delivers in 1 step.
var ledger_inst: RefCounted = ClassDB.instantiate("GdTradeLedger") as RefCounted
var agreement_json: String = JSON.stringify({
"agreements": [
{
"SharedMap": {
"agreement_id": 0,
"partners": [0, 1],
"turn_started": 1,
"duration": 5,
"share_turns_remaining": 0,
"payment_gold": 100,
"payment_luxury": null,
"courier_route": {
"sender": 0,
"receiver": 1,
"courier_era_tier": 7,
"dispatched_turn": 1,
"position": [0, 0],
"eta_turn": null,
"delivered": false,
"intercepted": false,
"planned_path": [],
"path_step": 0
}
}
}
],
"next_agreement_id": 1
})
var ledger: RefCounted = ledger_inst.call("from_json", agreement_json) as RefCounted
if not _assert("era7_intact: ledger loads", ledger != null):
return
var events: Array = gd_trade.call("step_shared_map_agreements", ledger, gd_map_view, 2) as Array
var delivered: bool = false
for ev: Dictionary in events:
if ev.get("type") == "shared_map_delivered":
delivered = true
break
_assert("era7_intact: era_7 delivers in one step over 4-hex gap", delivered)
func _test_intercepted_route_is_terminal() -> void:
var state: RefCounted = ClassDB.instantiate("GdGameState") as RefCounted
if not _assert("era7_terminal: GdGameState instantiates", state != null):
return
state.call("create_grid", 10, 10)
state.call("add_player_militarist", 0, 0)
state.call("add_player_militarist", 8, 0)
var map_view_inst: RefCounted = ClassDB.instantiate("GdCourierMapView") as RefCounted
var gd_map_view: RefCounted = map_view_inst.call("from_game_state", state) as RefCounted
if not _assert("era7_terminal: GdCourierMapView created", gd_map_view != null):
return
var gd_trade: RefCounted = ClassDB.instantiate("GdTrade") as RefCounted
if not _assert("era7_terminal: GdTrade instantiates", gd_trade != null):
return
# Courier already in terminal intercepted=true state.
var ledger_inst: RefCounted = ClassDB.instantiate("GdTradeLedger") as RefCounted
var agreement_json: String = JSON.stringify({
"agreements": [
{
"SharedMap": {
"agreement_id": 0,
"partners": [0, 1],
"turn_started": 1,
"duration": 10,
"share_turns_remaining": 0,
"payment_gold": 100,
"payment_luxury": null,
"courier_route": {
"sender": 0,
"receiver": 1,
"courier_era_tier": 7,
"dispatched_turn": 1,
"position": [3, 0],
"eta_turn": null,
"delivered": false,
"intercepted": true,
"planned_path": [],
"path_step": 0
}
}
}
],
"next_agreement_id": 1
})
var ledger: RefCounted = ledger_inst.call("from_json", agreement_json) as RefCounted
if not _assert("era7_terminal: ledger loads", ledger != null):
return
var events: Array = gd_trade.call("step_shared_map_agreements", ledger, gd_map_view, 2) as Array
_assert("era7_terminal: no events emitted for already-intercepted courier", events.is_empty())
var sm_agreements: Array = ledger.call("iter_shared_map") as Array
_assert("era7_terminal: intercepted agreement stays in ledger", sm_agreements.size() == 1)
func _assert(label: String, condition: bool) -> bool:
if condition:
_pass_count += 1
print("[PASS] %s" % label)
else:
_fail_count += 1
push_error("[FAIL] %s" % label)
return condition
func _print_results() -> void:
print("courier_era7_severance_proof: %d passed, %d failed" % [_pass_count, _fail_count])

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://courier_era7_severance"]
[ext_resource type="Script" path="res://engine/scenes/tests/courier_era7_severance_proof.gd" id="1"]
[node name="CourierEra7SeveranceProof" type="Node"]
script = ExtResource("1")