126 lines
6.9 KiB
GDScript
126 lines
6.9 KiB
GDScript
extends SceneTree
|
|
|
|
const Adapter = preload("res://campaign/fg4_adapter.gd")
|
|
const Contract1Battle = preload("res://fg4/contract1_battle_service.gd")
|
|
const Contract2Battle = preload("res://fg4/contract2_battle_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:
|
|
var adapter_script: Script = Adapter
|
|
var fixture_script: Script = Fixture
|
|
if not adapter_script.can_instantiate() or not fixture_script.can_instantiate():
|
|
failures.append("FG-4 campaign adapter: required script failed to compile")
|
|
finish()
|
|
return
|
|
verify_complete_region_flow()
|
|
verify_recovery_flow()
|
|
finish()
|
|
|
|
|
|
func finish() -> void:
|
|
if failures.is_empty():
|
|
print("ABG_FG4_CAMPAIGN_ADAPTER_OK rounds=24 bosses=2 records=2 terminal=RG-04 modes=market,plan,battle,recovery,record,handoff,closed valve=true heat=true construct=true repeat=true spark=true save_authority=unchanged")
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func verify_complete_region_flow() -> void:
|
|
var source := Fixture.new().market_1_snapshot()
|
|
var source_hash := Adapter.hash_value(source)
|
|
var adapter := Adapter.new()
|
|
var configured := adapter.configure(source)
|
|
expect(configured.ok and configured.view.mode == "market" and configured.view.market_index == 1, "real RG-03 Market 1 configures")
|
|
expect(Adapter.hash_value(source) == source_hash, "adapter never mutates caller snapshot")
|
|
if not configured.ok or configured.view.get("routes", []).is_empty():
|
|
return
|
|
for market_index in range(1, 13):
|
|
var route: Dictionary = adapter.view_state().routes[0]
|
|
var prepared := adapter.prepare_route(String(route.offer_id))
|
|
expect(prepared.ok and prepared.view.mode == "plan", "Contract 1 Market %d opens response plans" % market_index)
|
|
var plan_id := String(Contract1Battle.PRIMARY_PLAN[market_index])
|
|
expect(adapter.start_plan(plan_id).ok, "Contract 1 Market %d starts authoritative battle" % market_index)
|
|
var terminal := adapter.run_battle_to_terminal()
|
|
expect(terminal.ok and terminal.view.battle.terminal and terminal.view.battle.outcome == "victory", "Contract 1 Market %d reaches visible victory" % market_index)
|
|
expect(has_kind(terminal.events, "pressure_valve_disclosed") and has_kind(terminal.events, "outcome"), "Contract 1 Market %d forwards valve and result authority" % market_index)
|
|
expect(adapter.settle_terminal().ok, "Contract 1 Market %d settles" % market_index)
|
|
expect(adapter.view_state().mode == "boss_muster" and adapter.view_state().boss_id == "BO-R3-01", "Furnace Muster exposes its fixed identity")
|
|
expect(adapter.start_plan("vent_and_brace").ok, "Furnace plan starts")
|
|
var furnace := adapter.run_battle_to_terminal()
|
|
expect(furnace.view.battle.outcome == "victory", "Furnace reaches visible victory")
|
|
expect(has_kind(furnace.events, "side_swap_forecast") and has_kind(furnace.events, "weak_side_vented"), "Furnace forwards pressure and vent events")
|
|
expect(adapter.settle_terminal().view.mode == "contract_record", "Furnace opens first Record")
|
|
expect(adapter.continue_result().view.mode == "contract_handoff", "first Record closes into explicit handoff")
|
|
expect(adapter.continue_result().view.mode == "market", "handoff opens Contract-2 Market 1")
|
|
for market_index in range(1, 13):
|
|
var route: Dictionary = adapter.view_state().routes[0]
|
|
expect(adapter.prepare_route(String(route.offer_id)).ok, "Contract 2 Market %d prepares" % market_index)
|
|
var plan_id := String(Contract2Battle.PRIMARY_PLAN[market_index])
|
|
expect(adapter.start_plan(plan_id).ok, "Contract 2 Market %d starts" % market_index)
|
|
var terminal := adapter.run_battle_to_terminal()
|
|
expect(terminal.view.battle.outcome == "victory", "Contract 2 Market %d reaches visible victory" % market_index)
|
|
expect(has_kind(terminal.events, "repeat_counter_disclosed"), "Contract 2 Market %d forwards repeat authority" % market_index)
|
|
expect(adapter.settle_terminal().ok, "Contract 2 Market %d settles" % market_index)
|
|
expect(adapter.view_state().mode == "boss_muster" and adapter.view_state().boss_id == "BO-R3-02", "Foreman Muster is distinct")
|
|
expect(adapter.start_plan("vary_tags").ok, "Foreman variation plan starts")
|
|
var foreman := adapter.run_battle_to_terminal()
|
|
expect(foreman.view.battle.outcome == "victory", "Foreman reaches visible victory")
|
|
expect(has_kind(foreman.events, "next_spark_disclosed") and has_kind(foreman.events, "repeat_counter_reset"), "Foreman forwards spark and repeat events")
|
|
expect(adapter.settle_terminal().view.mode == "region_record", "Foreman opens second Record")
|
|
var closed := adapter.continue_result()
|
|
expect(closed.ok and closed.view.mode == "region_closed", "Region result closes visibly")
|
|
expect(closed.view.region_records_count == 2 and closed.view.unlocked_region_ids.count("RG-04") == 1, "two RG-03 Records and RG-04 persist exactly once")
|
|
|
|
|
|
func verify_recovery_flow() -> void:
|
|
var snapshot := Fixture.new().market_1_snapshot()
|
|
var company: Array = Array(snapshot.current_run.company).duplicate()
|
|
var deployed_count := mini(4, company.size())
|
|
snapshot.current_run.deployed_recruit_ids = company.slice(0, deployed_count)
|
|
snapshot.current_run.reserve_recruit_ids = company.slice(deployed_count)
|
|
snapshot.current_run.formation_proposal = company.slice(0, deployed_count)
|
|
var adapter := Adapter.new()
|
|
var configured := adapter.configure(snapshot)
|
|
expect(configured.ok and not adapter.view_state().get("routes", []).is_empty(), "bounded recovery snapshot configures")
|
|
if not configured.ok or adapter.view_state().get("routes", []).is_empty():
|
|
return
|
|
var route: Dictionary = adapter.view_state().routes[2]
|
|
expect(adapter.prepare_route(String(route.offer_id)).ok, "risk-two recovery route prepares")
|
|
var primary := String(Contract1Battle.PRIMARY_PLAN[1])
|
|
var secondary := String(Contract1Battle.SECONDARY_PLAN[1])
|
|
var wrong_plan := ""
|
|
for candidate in Contract1Battle.PLAN_IDS:
|
|
if candidate not in [primary, secondary]:
|
|
wrong_plan = String(candidate)
|
|
break
|
|
expect(adapter.start_plan(wrong_plan).ok, "nonpreferred response starts")
|
|
var terminal := adapter.run_battle_to_terminal()
|
|
expect(terminal.view.battle.outcome == "defeat" and terminal.view.battle.fallen_recruit_ids.size() == 1, "visible defeat names one Fallen Recruit")
|
|
var settled := adapter.settle_terminal()
|
|
expect(settled.ok and settled.view.mode == "recovery", "defeat opens recovery presentation")
|
|
var survivors: int = settled.view.company.size()
|
|
var recovered := adapter.recover()
|
|
expect(recovered.ok and recovered.view.mode == "market", "recovery returns to the same Market")
|
|
expect(recovered.view.company.size() == survivors, "recovery never resurrects or auto-substitutes")
|
|
|
|
|
|
func has_kind(events: Array, kind: String) -> bool:
|
|
for event in events:
|
|
if String(event.get("kind", "")) == kind:
|
|
return true
|
|
return false
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("FG-4 campaign adapter: %s" % message)
|