Files
aetherbound-guild/runtime/tests/p9_8/contract_state_test.gd
T

169 lines
9.6 KiB
GDScript

extends SceneTree
const LegacyService = preload("res://p9_3/core_loop_service.gd")
const LegacyState = preload("res://p9_3/core_loop_state.gd")
const P97MarketService = preload("res://p9_7/market_service.gd")
const P97BattleService = preload("res://p9_7/battle_service.gd")
const State = preload("res://p9_8/contract_state.gd")
const Service = preload("res://p9_8/contract_service.gd")
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
var market8 := prepare_market8()
verify_activation(market8)
verify_artifact_transaction(market8)
verify_artifact_market_shapes(market8)
verify_promotion_transaction(market8)
verify_rejections_are_atomic(market8)
if failures.is_empty():
print("ABG_P9_8_CONTRACT_STATE_OK migration=p9.7_market8 artifact=AR-001,AR-002 apply=market9 shapes=4x3,2x5 promotion=level4+survival3 identity_hp_equipment_order=preserved operations=idempotent conflict=blocked invalid_mutations=0")
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func verify_activation(market8: Dictionary) -> void:
var state = State.new()
expect(state.initialize_from_p97(market8), "P9.7 Market 8 activates P9.8")
expect(state.p98_activated and state.current_market == 8 and state.cleared_rounds == 7, "activation preserves threshold and opens Market 8")
expect(not state.continuation_locked and state.relay_restored, "P9.7 continuation lock becomes playable without losing Relay")
expect(state.available_recruit_offer_ids.size() == 3 and state.available_equipment_offer_ids.size() == 4 and state.available_battle_offer_ids.size() == 3, "Market 8 has exact base shape")
expect(state.artifact_draft.offer_ids == ["AR-001", "AR-002"] and state.selected_artifact_id.is_empty(), "fixed Artifact draft is visible and unselected")
expect(state.company == market8.company and state.inventory == market8.inventory and state.member_condition == market8.member_condition, "Company, inventory and HP migrate exactly")
expect(state.route_history == market8.route_history and state.operation_receipts == market8.operation_receipts, "route and operation history migrate exactly")
expect(state.invariants_hold(), "activated state satisfies P9.8 invariants")
func verify_artifact_transaction(market8: Dictionary) -> void:
var service = Service.new(market8)
var before: Dictionary = service.state.snapshot()
var selected: Dictionary = service.choose_artifact("p98-artifact-ledger", "AR-001")
expect(selected.ok and selected.code == "artifact_committed", "Open Ledger commits")
expect(service.state.selected_artifact_id == "AR-001" and service.state.artifact_receipts.size() == 1, "Artifact selection writes one state and receipt")
expect(service.state.revision == int(before.revision) + 1, "Artifact commit increments one revision")
var after: Dictionary = service.state.snapshot()
var duplicate: Dictionary = service.choose_artifact("p98-artifact-ledger", "AR-001")
expect(duplicate.ok and duplicate.duplicate and service.state.snapshot() == after, "duplicate Artifact commit is inert")
var conflict: Dictionary = service.choose_artifact("p98-artifact-ledger", "AR-002")
expect(not conflict.ok and conflict.code == "operation_id_conflict" and service.state.snapshot() == after, "Artifact operation conflict is inert")
var second: Dictionary = service.choose_artifact("p98-artifact-second", "AR-002")
expect(not second.ok and second.code == "artifact_already_selected" and service.state.snapshot() == after, "a second Artifact cannot replace the committed rule")
func verify_artifact_market_shapes(market8: Dictionary) -> void:
var service = Service.new(market8)
var base: Dictionary = service.board_preview(9)
var recruit_wide: Dictionary = service.board_preview(9, "AR-001")
var item_wide: Dictionary = service.board_preview(9, "AR-002")
var bench: Dictionary = service.board_preview(9, "AR-007")
expect(base.recruit_offer_ids.size() == 3 and base.equipment_offer_ids.size() == 4, "base next Market is 3 Recruit and 4 equipment")
expect(recruit_wide.recruit_offer_ids.size() == 4 and recruit_wide.equipment_offer_ids.size() == 3, "Open Ledger exchanges one equipment slot for one Recruit")
expect(item_wide.recruit_offer_ids.size() == 2 and item_wide.equipment_offer_ids.size() == 5, "Toolwright exchanges one Recruit slot for one equipment")
expect(int(bench.reserve_capacity_bonus) == 2 and int(bench.reserve_upkeep_per_member) == 1, "Full Bench retains its catalog capacity/upkeep rule")
expect(base.board_hash != recruit_wide.board_hash and recruit_wide.board_hash != item_wide.board_hash, "Artifact board lineage is explicit")
func verify_promotion_transaction(market8: Dictionary) -> void:
var eligible := market8.duplicate(true)
eligible.recruit_records["Hearthguard"].level = 4
eligible.recruit_records["Hearthguard"].survival_battles = 7
var service = Service.new(eligible)
var company_before: Array[String] = service.state.company.duplicate()
var deployed_before: Array[String] = service.state.deployed_recruit_ids.duplicate()
var hp_before := int(service.state.member_condition.Hearthguard)
var equipment_before := String(service.state.equipped_by_recruit.Hearthguard)
var opened: Dictionary = service.open_promotion_draft("Hearthguard")
expect(opened.ok and service.state.promotion_draft.offer_ids == ["PF-A01", "PF-A02"], "eligible Hearthguard receives both canonical branches")
var before_confirm: Dictionary = service.state.snapshot()
var promoted: Dictionary = service.confirm_promotion("p98-promote-guard", "Hearthguard", "PF-A01")
expect(promoted.ok and promoted.code == "promotion_committed", "Gatebearer promotion commits")
expect(service.state.recruit_records.Hearthguard.profession_id == "PF-A01" and service.state.recruit_records.Hearthguard.base_profession_id == "PF-B01", "promotion preserves parent and changes grammar")
expect(service.state.company == company_before and service.state.deployed_recruit_ids == deployed_before, "promotion preserves Company and line order")
expect(int(service.state.member_condition.Hearthguard) == hp_before and String(service.state.equipped_by_recruit.Hearthguard) == equipment_before, "promotion preserves HP and equipment")
expect(service.state.revision == int(before_confirm.revision) + 1 and service.state.promotion_receipts.size() == 1, "promotion writes one revision and receipt")
var after: Dictionary = service.state.snapshot()
var duplicate: Dictionary = service.confirm_promotion("p98-promote-guard", "Hearthguard", "PF-A01")
expect(duplicate.ok and duplicate.duplicate and service.state.snapshot() == after, "duplicate promotion is inert")
var conflict: Dictionary = service.confirm_promotion("p98-promote-guard", "Hearthguard", "PF-A02")
expect(not conflict.ok and conflict.code == "operation_id_conflict" and service.state.snapshot() == after, "promotion operation conflict is inert")
func verify_rejections_are_atomic(market8: Dictionary) -> void:
var service = Service.new(market8)
var checks := [
func(): return service.choose_artifact("", "AR-001"),
func(): return service.choose_artifact("bad-artifact", "AR-007"),
func(): return service.open_promotion_draft("missing"),
func(): return service.confirm_promotion("bad-promotion", "Hearthguard", "PF-A24"),
]
for check in checks:
var before: Dictionary = service.state.snapshot()
var result: Dictionary = check.call()
expect(not result.ok and service.state.snapshot() == before, "invalid Contract operation preserves the complete snapshot")
func prepare_market8() -> Dictionary:
var market = P97MarketService.new(complete_p96())
for step in [
{"market": 5, "recruit": "P97-M5-R2", "route": "P97-R5-Q0"},
{"market": 6, "recruit": "P97-M6-R2", "route": "P97-R6-Q0"},
{"market": 7, "recruit": "P97-M7-R3", "route": "P97-R7-Q0"},
]:
market.focus_recruit_offer(String(step.recruit))
market.hire("p98-fixture-hire-%d" % int(step.market), String(step.recruit))
market.commit_deployment("p98-fixture-deploy-%d" % int(step.market), market.auto_order_candidate())
market.focus_route(String(step.route))
market.commit_route("p98-fixture-route-%d" % int(step.market), String(step.route))
var battle = P97BattleService.new()
battle.start_from_market(market.state)
var guard := 0
while not battle.state.terminal and guard < 2000:
battle.advance_ticks(1)
guard += 1
expect(guard < 2000 and battle.state.outcome == "victory", "fixture Round %d wins" % int(step.market))
market.settle_battle("p98-fixture-settle-%d" % int(step.market), battle.state.snapshot())
if market.state.recovery_required:
market.recover_injured_company("p98-fixture-recover-%d" % int(step.market))
expect(market.state.current_market == 8 and market.state.continuation_locked, "fixture reaches exact P9.7 Market 8")
return market.state.snapshot()
func complete_p96() -> Dictionary:
var service = LegacyService.new()
service.enter_hall()
service.focus_offer("Farstring")
service.hire("Farstring")
complete_legacy_battle(service, "open_road")
service.focus_offer("Kindhand")
service.hire("Kindhand")
complete_legacy_battle(service, "crosswind")
service.focus_equipment("EQ-002")
service.equip_focused("Hearthguard")
complete_legacy_battle(service, "bell_breach")
service.choose_rift_tide_plan("press_on")
complete_legacy_battle(service, "rift_tide")
return service.state.snapshot()
func complete_legacy_battle(service, encounter_id: String) -> void:
service.select_encounter(encounter_id)
service.start_battle()
var guard := 0
while service.state.phase == LegacyState.Phase.BATTLE and guard < 4000:
service.advance_battle_ticks(1)
guard += 1
service.claim_and_return()
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("P9.8 Contract state: %s" % message)