Files
aetherbound-guild/runtime/tests/fg3/contract2_state_test.gd
T

114 lines
6.0 KiB
GDScript

extends SceneTree
const Contract1Battle = preload("res://fg3/contract1_battle_service.gd")
const Contract2Service = preload("res://fg3/contract2_service.gd")
const Fixture = preload("res://tests/fg3/contract1_fixture.gd")
const SaveV10 = preload("res://fg3/save_v10_service.gd")
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
verify_exact_handoff_activation()
verify_atomic_boundaries()
verify_save_roundtrip_and_corruption()
if failures.is_empty():
print("ABG_FG3_CONTRACT2_STATE_OK handoff=CTR-R2-01_to_CTR-R2-02 company,growth,equipment,economy,artifact,records=exact market=1 cleared=12 capacity=10 board=3,4,3 revision_bound=true duplicate=true save_v10=roundtrip battle_execution=frozen offline=zero")
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func verify_exact_handoff_activation() -> void:
var handoff := Fixture.new().contract2_handoff_snapshot()
expect(not handoff.is_empty(), "real Contract-1 clear creates handoff")
if handoff.is_empty():
return
var service := Contract2Service.new(handoff)
expect(service.valid, "handoff state validates")
var before := service.state.snapshot()
var handoff_hash := String(before.current_run.market_board.handoff_hash)
var revision_before := int(service.state.revision)
var activated := service.activate_contract(
"fg3-contract2-activate",
revision_before,
handoff_hash
)
expect(activated.ok and service.state.revision == revision_before + 1, "Contract 2 activates exactly once")
if not activated.ok:
return
var after := service.state.snapshot()
expect(after.current_run.contract_id == "CTR-R2-02" and after.current_run.phase == "contract2_market", "state enters exact Contract-2 Market phase")
expect(after.current_run.market_index == 1 and after.current_run.round_index == 1 and after.current_run.cleared_rounds == 12, "Market 1 preserves twelve global clears")
expect(after.current_run.line_capacity == 10 and after.current_run.market_board.line_capacity == 10, "surviving capacity stays at core cap")
expect(after.current_run.market_board.contract_id == "CTR-R2-02" and after.current_run.market_board.recruit_slots.size() == 3 and after.current_run.market_board.equipment_offers.size() == 4 and after.current_run.market_board.battle_offers.size() == 3, "first board is exact 3/4/3 Contract-2 authority")
expect(after.current_run.company == before.current_run.company and after.current_run.recruit_records == before.current_run.recruit_records, "Company identity, HP, level and XP are byte-equivalent")
expect(after.current_run.inventory == before.current_run.inventory and after.current_run.equipped_by_recruit == before.current_run.equipped_by_recruit, "one-slot equipment and inventory are byte-equivalent")
expect(after.current_run.coin == before.current_run.coin and after.current_run.replacement_credit == before.current_run.replacement_credit, "Coin and Credit are exact")
expect(after.current_run.active_artifact_ids == before.current_run.active_artifact_ids and after.current_run.artifact_rule_state == before.current_run.artifact_rule_state, "Artifact rule state is exact")
expect(after.current_run.route_history == before.current_run.route_history and after.guild_profile == before.guild_profile, "route history and persistent first Record are exact")
expect(after.current_run.boss_muster.is_empty() and after.current_run.pending_boss_result.is_empty(), "Contract-1 Boss transient state is cleared")
expect(not Contract1Battle.new(after).valid, "Contract-1 battle executor cannot run Contract 2")
var duplicate := service.activate_contract(
"fg3-contract2-activate",
revision_before,
handoff_hash
)
expect(duplicate.ok and duplicate.duplicate and service.state.snapshot() == after, "activation retry cannot reset Company or board")
func verify_atomic_boundaries() -> void:
var handoff := Fixture.new().contract2_handoff_snapshot()
var service := Contract2Service.new(handoff)
var before := service.state.snapshot()
var stale := service.activate_contract(
"fg3-contract2-stale",
service.state.revision + 1,
String(service.state.current_run.market_board.handoff_hash)
)
expect(not stale.ok and stale.code == "stale_revision" and service.state.snapshot() == before, "stale activation rejects without mutation")
var wrong_hash := service.activate_contract(
"fg3-contract2-wrong-hash",
service.state.revision,
"0".repeat(64)
)
expect(not wrong_hash.ok and wrong_hash.code == "handoff_hash_mismatch" and service.state.snapshot() == before, "foreign handoff hash rejects without mutation")
var missing_clear := handoff.duplicate(true)
missing_clear.guild_profile.contract_clear_ids.erase("CLEAR-RG02-C01")
expect(not Contract2Service.new(missing_clear).valid, "handoff without first clear is invalid")
func verify_save_roundtrip_and_corruption() -> void:
var handoff := Fixture.new().contract2_handoff_snapshot()
var service := Contract2Service.new(handoff)
var activated := service.activate_contract(
"fg3-contract2-save-activate",
service.state.revision,
String(service.state.current_run.market_board.handoff_hash)
)
expect(activated.ok, "save fixture activates")
if not activated.ok:
return
var saves := SaveV10.new()
var encoded := saves.encode_snapshot(service.state.snapshot(), 100)
var decoded := saves.decode_text(String(encoded.text), 100)
expect(encoded.ok and decoded.ok and decoded.snapshot == service.state.snapshot(), "Contract-2 Market 1 roundtrips byte-equivalent")
expect(decoded.offline_delta == SaveV10.OFFLINE_ZERO, "resume invents no offline value")
var wrong_contract := service.state.snapshot()
wrong_contract.current_run.contract_id = "CTR-R2-01"
expect(not saves.encode_snapshot(wrong_contract, 101).ok, "Contract identity drift rejects")
var wrong_board := service.state.snapshot()
wrong_board.current_run.market_board.pulse_disclosure.linked_count = 9
expect(not saves.encode_snapshot(wrong_board, 101).ok, "pulse authority drift rejects")
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("FG-3 Contract-2 state: %s" % message)