Files
aetherbound-guild/runtime/tests/fg4/contract1_boss_test.gd
T

102 lines
6.2 KiB
GDScript

extends SceneTree
const Battle = preload("res://fg4/contract1_battle_service.gd")
const Contract = preload("res://fg4/contract1_service.gd")
const Fixture = preload("res://tests/fg4/contract1_fixture.gd")
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
verify_boss_victory_record_and_handoff()
verify_boss_permanent_loss_and_recovery()
verify_boss_tamper_boundaries()
if failures.is_empty():
print("ABG_FG4_CONTRACT1_BOSS_OK boss=BO-R3-01 plans=2 weak_side=true rupture=true reward_coin=0 renown=18 record=1 next=CTR-R3-02 permanent_loss=true same_muster_recovery=true resurrection=0 exact_once=true")
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func verify_boss_victory_record_and_handoff() -> void:
var service := Contract.new(Fixture.new().boss_muster_snapshot())
expect(service.valid and service.state.current_run.phase == "boss_muster", "real twelve-Round close opens Boss Muster")
if not service.valid:
return
var muster: Dictionary = service.state.current_run.boss_muster
expect(muster.boss_id == "BO-R3-01" and muster.plan_ids == ["vent_and_brace", "strong_side_burst"], "Muster exposes exact Boss and plans")
expect(muster.weak_side == "left" and muster.rupture_tick == 72, "Muster discloses weak side and rupture clock")
var renown_before := int(service.state.guild_profile.persistent_renown)
var records_before: int = service.state.guild_profile.company_records.size()
var coin_before := int(service.state.current_run.coin)
var prepared := service.commit_boss_plan("fg4-boss-prepare", service.state.revision, String(muster.muster_hash), "vent_and_brace")
expect(prepared.ok, "vent-and-brace plan prepares")
var battle := Battle.new(service.state.snapshot()).run_to_terminal("vent_and_brace")
expect(battle.ok and battle.snapshot.outcome == "victory", "vent-and-brace defeats Furnace")
expect(has_event(battle.snapshot.event_log, "weak_side_vented") and has_event(battle.snapshot.event_log, "side_swap_forecast"), "Boss emits weak-side and phase events")
var settled := service.settle_boss("fg4-boss-settle", service.state.revision, battle.snapshot)
expect(settled.ok and service.state.current_run.phase == "contract_result", "Boss victory opens Contract result")
expect(settled.receipt.reward_coin == 0 and service.state.current_run.coin == coin_before, "Boss pays no Coin")
var close_id := String(service.state.current_run.pending_boss_result.close_id)
var closed := service.close_contract("fg4-contract-close", service.state.revision, close_id)
expect(closed.ok and service.state.current_run.phase == "contract2_handoff", "Contract closes into stable handoff")
expect(service.state.current_run.next_contract_id == "CTR-R3-02", "handoff names exact next Contract")
expect(service.state.guild_profile.persistent_renown == renown_before + 18, "first clear grants exact Renown 18")
expect(service.state.guild_profile.company_records.size() == records_before + 1, "one Company Record is written")
expect("CLEAR-RG03-C01" in service.state.guild_profile.contract_clear_ids, "clear identity persists")
var duplicate := service.close_contract("fg4-contract-close", service.state.revision - 1, close_id)
expect(duplicate.ok and duplicate.duplicate and duplicate.receipt == closed.receipt, "duplicate close is inert and returns exact receipt")
func verify_boss_permanent_loss_and_recovery() -> void:
var low_hp := Fixture.new().boss_muster_snapshot()
for recruit_id in low_hp.current_run.company:
low_hp.current_run.recruit_records[recruit_id].current_hp = 1
var service := Contract.new(low_hp)
expect(service.valid, "low-HP Boss fixture remains valid")
if not service.valid:
return
var muster_hash := String(service.state.current_run.boss_muster.muster_hash)
var company_before: int = service.state.current_run.company.size()
var prepared := service.commit_boss_plan("fg4-boss-loss-prepare", service.state.revision, muster_hash, "strong_side_burst")
expect(prepared.ok, "high-risk Boss plan prepares")
var battle := Battle.new(service.state.snapshot()).run_to_terminal("strong_side_burst")
expect(battle.ok and battle.snapshot.outcome == "defeat" and battle.snapshot.fallen_recruit_ids.size() == 1, "low-HP burst plan causes one permanent casualty")
var fallen_id := String(battle.snapshot.fallen_recruit_ids[0])
var settled := service.settle_boss("fg4-boss-loss-settle", service.state.revision, battle.snapshot)
expect(settled.ok and service.state.current_run.phase == "boss_recovery", "Boss defeat opens recovery")
expect(service.state.current_run.company.size() == company_before - 1 and fallen_id not in service.state.current_run.company, "Boss casualty is permanent")
var recovered := service.recover_boss("fg4-boss-loss-recover", service.state.revision, String(battle.snapshot.snapshot_hash), 28)
expect(recovered.ok and service.state.current_run.phase == "boss_muster", "Boss recovery returns to Muster")
expect(service.state.current_run.boss_muster.muster_hash == muster_hash, "Boss Muster never rerolls")
expect(fallen_id not in service.state.current_run.company, "Boss recovery never resurrects")
func verify_boss_tamper_boundaries() -> void:
var service := Contract.new(Fixture.new().boss_muster_snapshot())
if not service.valid:
return
var muster_hash := String(service.state.current_run.boss_muster.muster_hash)
expect(not service.commit_boss_plan("fg4-boss-bad-plan", service.state.revision, muster_hash, "unknown").ok, "unknown Boss plan rejects")
var prepared := service.commit_boss_plan("fg4-boss-tamper-prepare", service.state.revision, muster_hash, "vent_and_brace")
var battle := Battle.new(service.state.snapshot()).run_to_terminal("vent_and_brace")
var tampered: Dictionary = battle.snapshot.duplicate(true)
tampered.muster_hash = "0".repeat(64)
expect(prepared.ok and not service.settle_boss("fg4-boss-tamper-settle", service.state.revision, tampered).ok, "tampered Boss snapshot rejects")
expect(service.state.current_run.phase == "boss_prepared", "rejected Boss settlement preserves prepared state")
func has_event(events: Array, kind: String) -> bool:
return events.any(func(event): return String(event.get("kind", "")) == kind)
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("FG-4 Contract-1 Boss: %s" % message)