104 lines
6.8 KiB
GDScript
104 lines
6.8 KiB
GDScript
extends SceneTree
|
|
|
|
const Battle = preload("res://fg4/contract2_battle_service.gd")
|
|
const Contract = preload("res://fg4/contract2_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_foreman_victory_record_and_region_close()
|
|
verify_foreman_permanent_loss_and_recovery()
|
|
verify_foreman_tamper_boundaries()
|
|
if failures.is_empty():
|
|
print("ABG_FG4_CONTRACT2_BOSS_OK boss=BO-R3-02 plans=2 repeat_threshold=3 sparks=8 reward_coin=0 renown=22 clear=CLEAR-RG03-C02 records=2 unlock=RG-04_once 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_foreman_victory_record_and_region_close() -> void:
|
|
var service := Contract.new(Fixture.new().contract2_boss_muster_snapshot())
|
|
expect(service.valid and service.state.current_run.phase == "contract2_boss_muster", "twelve Contract-2 victories open Foreman Muster")
|
|
if not service.valid:
|
|
return
|
|
var muster: Dictionary = service.state.current_run.boss_muster
|
|
expect(muster.boss_id == "BO-R3-02" and muster.plan_ids == ["vary_tags", "safe_promotion"], "Muster exposes exact Boss and plans")
|
|
expect(muster.spark_count == 8 and muster.repeat_threshold == 3 and muster.next_spark_index == 4, "Muster discloses spark state")
|
|
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_contract2_boss_plan("fg4-contract2-boss-prepare", service.state.revision, String(muster.muster_hash), "vary_tags")
|
|
var battle := Battle.new(service.state.snapshot()).run_to_terminal("vary_tags")
|
|
expect(prepared.ok and battle.ok and battle.snapshot.outcome == "victory", "varied tags defeat Foreman")
|
|
expect(has_event(battle.snapshot.event_log, "repeat_counter_disclosed") and has_event(battle.snapshot.event_log, "next_spark_disclosed"), "Boss emits repeat and next-spark disclosures")
|
|
var settled := service.settle_contract2_boss("fg4-contract2-boss-settle", service.state.revision, battle.snapshot)
|
|
expect(settled.ok and service.state.current_run.phase == "region_result", "Boss victory opens RG-03 result")
|
|
expect(settled.receipt.reward_coin == 0 and service.state.current_run.coin == coin_before, "Foreman pays no Coin")
|
|
var close_id := String(service.state.current_run.pending_boss_result.close_id)
|
|
var close_revision := service.state.revision
|
|
var closed := service.close_region("fg4-rg03-close", close_revision, close_id)
|
|
expect(closed.ok and service.state.current_run.phase == "region_closed" and service.state.current_run.status == "closed", "RG-03 closes atomically")
|
|
expect(service.state.guild_profile.persistent_renown == renown_before + 22, "first clear grants exact Renown 22")
|
|
expect(service.state.guild_profile.company_records.size() == records_before + 1, "second RG-03 Company Record is written")
|
|
expect("CLEAR-RG03-C02" in service.state.guild_profile.contract_clear_ids, "second clear identity persists")
|
|
expect(service.state.guild_profile.unlocked_region_ids.count("RG-04") == 1, "RG-04 unlocks exactly once")
|
|
expect(closed.receipt.record.contract_id == "CTR-R3-02" and closed.receipt.record.route_ids.size() == 12, "record binds twelve Contract-2 routes")
|
|
var duplicate := service.close_region("fg4-rg03-close", close_revision, close_id)
|
|
expect(duplicate.ok and duplicate.duplicate and duplicate.receipt == closed.receipt, "duplicate Region close is inert")
|
|
expect(not service.close_region("fg4-rg03-close-again", service.state.revision, close_id).ok, "new close after terminal rejects")
|
|
|
|
|
|
func verify_foreman_permanent_loss_and_recovery() -> void:
|
|
var low_hp := Fixture.new().contract2_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 Foreman 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_contract2_boss_plan("fg4-contract2-boss-loss-prepare", service.state.revision, muster_hash, "safe_promotion")
|
|
var battle := Battle.new(service.state.snapshot()).run_to_terminal("safe_promotion")
|
|
expect(prepared.ok and battle.ok and battle.snapshot.outcome == "defeat", "low-HP safe-promotion plan can fail")
|
|
expect(battle.snapshot.fallen_recruit_ids.size() == 1, "Foreman defeat has one permanent casualty")
|
|
var fallen_id := String(battle.snapshot.fallen_recruit_ids[0])
|
|
var settled := service.settle_contract2_boss("fg4-contract2-boss-loss-settle", service.state.revision, battle.snapshot)
|
|
expect(settled.ok and service.state.current_run.phase == "contract2_boss_recovery", "Foreman 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_contract2_boss("fg4-contract2-boss-loss-recover", service.state.revision, String(battle.snapshot.snapshot_hash), 30)
|
|
expect(recovered.ok and service.state.current_run.phase == "contract2_boss_muster", "Boss recovery returns to same Muster")
|
|
expect(service.state.current_run.boss_muster.muster_hash == muster_hash, "Foreman Muster never rerolls")
|
|
expect(fallen_id not in service.state.current_run.company, "Boss recovery never resurrects")
|
|
|
|
|
|
func verify_foreman_tamper_boundaries() -> void:
|
|
var service := Contract.new(Fixture.new().contract2_boss_muster_snapshot())
|
|
if not service.valid:
|
|
return
|
|
var muster_hash := String(service.state.current_run.boss_muster.muster_hash)
|
|
expect(not service.commit_contract2_boss_plan("fg4-contract2-boss-bad-plan", service.state.revision, muster_hash, "unknown").ok, "unknown Boss plan rejects")
|
|
var prepared := service.commit_contract2_boss_plan("fg4-contract2-boss-tamper-prepare", service.state.revision, muster_hash, "vary_tags")
|
|
var battle := Battle.new(service.state.snapshot()).run_to_terminal("vary_tags")
|
|
var tampered: Dictionary = battle.snapshot.duplicate(true)
|
|
tampered.muster_hash = "0".repeat(64)
|
|
expect(prepared.ok and not service.settle_contract2_boss("fg4-contract2-boss-tamper-settle", service.state.revision, tampered).ok, "tampered Boss snapshot rejects")
|
|
expect(service.state.current_run.phase == "contract2_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-2 Boss: %s" % message)
|