126 lines
6.4 KiB
GDScript
126 lines
6.4 KiB
GDScript
extends SceneTree
|
|
|
|
const Controller = preload("res://entry/battle_live_page_controller.gd")
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
var controller := Controller.new()
|
|
var company := _fixture_company()
|
|
var battle := _fixture_battle()
|
|
var observation := _fixture_observation()
|
|
var event := _fixture_event()
|
|
var ready: Dictionary = controller.snapshot(company, battle, observation, event, "event_resolved", "event_resolved", false, 2.0, "", "", {}, true, "en")
|
|
expect(ready.ok and ready.page_id == "battle_live", "snapshot identifies Live Battle")
|
|
expect(ready.company.owned_count == 4 and ready.company.line_capacity == 4, "snapshot copies Company capacity")
|
|
expect(ready.battle.operation_id == "battle-op-001" and ready.battle.phase_index == 1, "snapshot copies battle identity and phase")
|
|
expect(ready.observation.snapshot_hash == "snapshot-hash-001" and ready.observation.next_phase_index == 1, "snapshot copies observation identity")
|
|
expect(ready.event.event_id == "event-001" and ready.recent_events.size() == 1, "snapshot copies current and recent event facts")
|
|
expect(ready.party.ordered_recruits.size() == 4 and ready.enemies.count == 3, "snapshot binds Party and enemy lines")
|
|
expect(ready.state.speed == 2.0 and ready.state.pause_log_open, "snapshot copies playback presentation facts")
|
|
expect(ready.commands.pause_enabled and ready.commands.inspect_enabled and ready.commands.settings_enabled, "event-resolved commands remain eligible")
|
|
|
|
var paused: Dictionary = controller.snapshot(company, battle, observation, event, "paused", "event_resolved", true, 1.0)
|
|
expect(paused.ok and paused.state.flags.paused and paused.commands.pause_enabled and paused.commands.speed_enabled, "paused state keeps pause and speed controls")
|
|
var inspect: Dictionary = controller.snapshot(company, battle, observation, event, "inspect_handoff", "paused", true, 1.0)
|
|
expect(inspect.ok and inspect.state.flags.inspect and not inspect.commands.pause_enabled and not inspect.commands.speed_enabled, "inspect handoff freezes playback controls")
|
|
var locked_battle := battle.duplicate(true)
|
|
locked_battle.status = "outcome_locked"
|
|
var locked_observation := observation.duplicate(true)
|
|
locked_observation.status = "outcome_locked"
|
|
var locked: Dictionary = controller.snapshot(company, locked_battle, locked_observation, event, "outcome_locked", "outcome_locked", true, 1.0)
|
|
expect(locked.ok and locked.state.flags.outcome_locked and locked.commands.outcome_handoff_enabled and not locked.commands.inspect_enabled, "outcome lock exposes only OUT-001 handoff")
|
|
var interrupted: Dictionary = controller.snapshot(company, battle, observation, {}, "interrupted_background", "event_resolved", true, 1.0, "resume", "", {"snapshot_hash": "snapshot-hash-001"})
|
|
expect(interrupted.ok and interrupted.state.flags.interrupted and interrupted.commands.interruption_review_enabled and interrupted.commands.safe_exit_enabled, "interruption exposes BAT-006 and safe exit")
|
|
var invalid: Dictionary = controller.snapshot(company, battle, observation, {}, "invalid_event_data", "event_resolved", true, 1.0, "invalid", "OBSERVATION_EVENT_INVALID")
|
|
expect(invalid.ok and invalid.commands.retry_snapshot_enabled and invalid.commands.safe_exit_enabled and not invalid.commands.pause_enabled, "invalid event exposes retry and safe exit")
|
|
|
|
var copied_battle: Dictionary = ready.battle.copy
|
|
copied_battle.phase_events.append({"event_id": "changed"})
|
|
ready.observation.outcome_lock = {"changed": true}
|
|
ready.event.state_delta.amount = 99
|
|
expect(battle.phase_events.size() == 1 and String(battle.phase_events[0].get("event_id", "")) == "event-001", "battle facts are deep copied")
|
|
expect(observation.get("outcome_lock", {}).is_empty(), "observation facts are deep copied")
|
|
expect(event.state_delta.amount == 3, "event facts are deep copied")
|
|
|
|
var missing: Dictionary = controller.snapshot({}, battle, observation, event)
|
|
expect(not missing.ok and missing.code == "COMPANY_REQUIRED", "missing Company is rejected")
|
|
var no_observation: Dictionary = controller.snapshot(company, battle, {}, event)
|
|
expect(not no_observation.ok and no_observation.code == "LIVE_OBSERVATION_REQUIRED", "missing observation is rejected")
|
|
var mismatch := observation.duplicate(true)
|
|
mismatch.snapshot_hash = "different"
|
|
var rejected_mismatch: Dictionary = controller.snapshot(company, battle, mismatch, event)
|
|
expect(not rejected_mismatch.ok and rejected_mismatch.code == "LIVE_SNAPSHOT_MISMATCH", "snapshot mismatch is rejected")
|
|
|
|
if failures.is_empty():
|
|
print("ABG_M26_BATTLE_LIVE_CONTROLLER_OK company=4 party=4 enemies=3 states=waiting,event,paused,inspect,outcome,interrupted,invalid receipt=copied commands=pause,speed,inspect,retry,safe_exit copies=true")
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func _fixture_company() -> Dictionary:
|
|
return {
|
|
"company_id": "company-001",
|
|
"owned_recruits": [
|
|
{"recruit_id": "r-1", "rendered_name_snapshot": {"display_name": "Ari"}},
|
|
{"recruit_id": "r-2", "rendered_name_snapshot": {"display_name": "Bram"}},
|
|
{"recruit_id": "r-3", "rendered_name_snapshot": {"display_name": "Cera"}},
|
|
{"recruit_id": "r-4", "rendered_name_snapshot": {"display_name": "Dain"}},
|
|
],
|
|
"active_contract": {"contract_id": "CTR-R1-01", "run_id": "run-001", "line_capacity": 4, "contract_hash": "contract-hash-001"},
|
|
}
|
|
|
|
|
|
func _fixture_battle() -> Dictionary:
|
|
return {
|
|
"status": "active",
|
|
"operation_id": "battle-op-001",
|
|
"battle_hash": "battle-hash-001",
|
|
"snapshot_id": "snapshot-001",
|
|
"snapshot_hash": "snapshot-hash-001",
|
|
"front_to_rear_ids": ["r-1", "r-2", "r-3", "r-4"],
|
|
"enemy_ids": ["enemy-1", "enemy-2", "enemy-3"],
|
|
"phase_index": 1,
|
|
"total_phases": 9,
|
|
"phase_events": [_fixture_event()],
|
|
}
|
|
|
|
|
|
func _fixture_observation() -> Dictionary:
|
|
return {
|
|
"status": "waiting_for_next_event",
|
|
"operation_id": "battle-op-001",
|
|
"snapshot_id": "snapshot-001",
|
|
"snapshot_hash": "snapshot-hash-001",
|
|
"receipt_hash": "receipt-hash-001",
|
|
"next_phase_index": 1,
|
|
"event_journal_hash": "journal-hash-001",
|
|
"outcome_lock": {},
|
|
}
|
|
|
|
|
|
func _fixture_event() -> Dictionary:
|
|
return {
|
|
"event_id": "event-001",
|
|
"event_hash": "event-hash-001",
|
|
"event_tick": 1,
|
|
"kind": "damage",
|
|
"actor_id": "r-1",
|
|
"target_id": "enemy-1",
|
|
"amount": 3,
|
|
"state_delta": {"amount": 3, "direction": "loss"},
|
|
}
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("M26 Battle Live Controller: %s" % message)
|