102 lines
6.2 KiB
GDScript
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)
|