114 lines
6.0 KiB
GDScript
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)
|