Files
aetherbound-guild/runtime/tests/foundation/risk_commitment_page_controller_test.gd
T

144 lines
8.7 KiB
GDScript

extends SceneTree
const Controller = preload("res://entry/risk_commitment_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 formation := _fixture_formation()
var option: Dictionary = formation.risk_options[1]
var facts := _fixture_consequences()
var blocked_dependency := {"ok": false, "code": "INSUFFICIENT_COIN", "coin": 2, "cost": 4}
var valid_dependency := {"ok": true, "code": "READY", "coin": 38, "cost": 4}
var binding := {"selected_risk_id": "risk-2", "snapshot_hash": "snapshot-hash-001", "option_hash": "option-hash-001", "contract_hash": "contract-hash-001", "stale": false}
var blocked: Dictionary = controller.snapshot(company, formation, option, facts, blocked_dependency, binding, false, "en")
expect(blocked.ok and blocked.page_id == "risk_commitment", "snapshot identifies Risk Commitment")
expect(blocked.company.owned_count == 4 and blocked.company.line_capacity == 4, "snapshot copies Company capacity")
expect(blocked.contract.contract_id == "CTR-R1-01" and blocked.contract.coin == 38, "snapshot copies Contract facts")
expect(blocked.risk.risk_option_id == "risk-2" and blocked.risk.option_hash == "option-hash-001", "snapshot copies selected risk identity")
expect(blocked.consequences.count == 6 and blocked.consequences.facts[0].text == "THREAT 3 · 2 WAVES", "snapshot copies six consequence facts")
expect(blocked.party.snapshot_id == "snapshot-001" and blocked.party.front_to_rear_ids == ["r-1", "r-2", "r-3", "r-4"], "snapshot copies frozen Party identity")
expect(blocked.party.ordered_recruits[0].recruit.recruit_id == "r-1" and blocked.party.ordered_recruits[0].relation.first_threat_target, "snapshot binds Recruit and relation facts")
expect(blocked.dependency.code == "INSUFFICIENT_COIN" and blocked.binding.state == "valid", "snapshot copies dependency and binding state")
expect(blocked.confirmation.state == "required" and not blocked.commands.begin_enabled and blocked.commands.acknowledge_enabled, "blocked state requires acknowledgement/dependency")
var ready: Dictionary = controller.snapshot(company, formation, option, facts, valid_dependency, binding, true, "zh_CN")
expect(ready.ok and ready.confirmation.acknowledged and ready.commands.begin_enabled, "acknowledged valid state enables Begin Battle")
expect(not ready.commands.acknowledge_enabled, "acknowledged valid state locks duplicate acknowledgement")
var stale_binding := binding.duplicate(true)
stale_binding.stale = true
var stale: Dictionary = controller.snapshot(company, formation, option, facts, valid_dependency, stale_binding, true, "en")
expect(stale.ok and stale.binding.state == "stale" and not stale.commands.begin_enabled, "stale binding blocks Begin Battle")
var missing_option: Dictionary = controller.snapshot(company, formation, {}, facts, valid_dependency, binding)
expect(not missing_option.ok and missing_option.code == "COMMITMENT_OPTION_REQUIRED", "missing option is rejected")
var stale_option := option.duplicate(true)
stale_option.risk_option_id = "missing"
var rejected_option: Dictionary = controller.snapshot(company, formation, stale_option, facts, valid_dependency, binding)
expect(not rejected_option.ok and rejected_option.code == "COMMITMENT_OPTION_STALE", "unknown option is rejected")
var no_snapshot := _fixture_formation()
no_snapshot.accepted_party_snapshot = {}
var rejected_snapshot: Dictionary = controller.snapshot(company, no_snapshot, option, facts, valid_dependency, binding)
expect(not rejected_snapshot.ok and rejected_snapshot.code == "COMMITMENT_PARTY_SNAPSHOT_REQUIRED", "missing Party snapshot is rejected")
var short_facts: Array = facts.slice(0, 5)
var rejected_facts: Dictionary = controller.snapshot(company, formation, option, short_facts, valid_dependency, binding)
expect(not rejected_facts.ok and rejected_facts.code == "COMMITMENT_CONSEQUENCES_INVALID", "six consequence boundary is enforced")
var missing: Dictionary = controller.snapshot({}, formation, option, facts, valid_dependency, binding)
expect(not missing.ok and missing.code == "COMPANY_REQUIRED", "missing Company is rejected")
var copied_option: Dictionary = ready.risk.option
copied_option.known_threat.pressure_tags.append("changed")
ready.consequences.facts[0].text = "changed"
ready.party.relations[0].protected_by_recruit_ids.append("changed")
expect(formation.risk_options[1].known_threat.pressure_tags == ["surge"], "selected risk facts are deep copied")
expect(facts[0].text == "THREAT 3 · 2 WAVES", "consequence facts are deep copied")
expect(formation.relations[0].protected_by_recruit_ids.is_empty(), "Party relation facts are deep copied")
if failures.is_empty():
print("ABG_M24_RISK_COMMITMENT_CONTROLLER_OK company=4 party=4 option=selected consequences=6 dependency=blocked,ready confirmation=normalized binding=stale_guard 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", "profession_id": "PF-B01", "rendered_name_snapshot": {"display_name": "Ari"}},
{"recruit_id": "r-2", "profession_id": "PF-B05", "rendered_name_snapshot": {"display_name": "Bram"}},
{"recruit_id": "r-3", "profession_id": "PF-B07", "rendered_name_snapshot": {"display_name": "Cera"}},
{"recruit_id": "r-4", "profession_id": "PF-B03", "rendered_name_snapshot": {"display_name": "Dain"}},
],
"active_contract": {
"contract_id": "CTR-R1-01",
"run_id": "run-001",
"difficulty": "stormbound",
"coin": 38,
"line_capacity": 4,
"contract_hash": "contract-hash-001",
},
}
func _fixture_formation() -> Dictionary:
return {
"status": "ready",
"orientation": "rear_left_front_right",
"front_position": 0,
"line_capacity": 4,
"saved_front_to_rear_ids": ["r-1", "r-2", "r-3", "r-4"],
"relations": [
{"position": 0, "recruit_id": "r-1", "first_threat_target": true, "protected_by_recruit_ids": []},
{"position": 1, "recruit_id": "r-2", "first_threat_target": false, "protected_by_recruit_ids": ["r-1"]},
{"position": 2, "recruit_id": "r-3", "first_threat_target": false, "protected_by_recruit_ids": ["r-1"]},
{"position": 3, "recruit_id": "r-4", "first_threat_target": false, "protected_by_recruit_ids": []},
],
"coverage": {"protection": 2, "long_reach": 2, "sustain": 1, "control": 1},
"risk_options": [
{"risk_option_id": "risk-1", "risk": 0, "option_hash": "option-hash-000", "known_threat": {"enemy_count": 2, "wave_count": 1, "pressure_tags": []}, "uncertainty_id": "disclosed", "entry_cost": 0, "reward_coin": 20, "reward_class_id": "standard_supplies", "recovery_exposure_id": "low"},
{"risk_option_id": "risk-2", "risk": 1, "option_hash": "option-hash-001", "known_threat": {"enemy_count": 3, "wave_count": 2, "pressure_tags": ["surge"]}, "uncertainty_id": "one_intent_unresolved", "entry_cost": 4, "reward_coin": 27, "reward_class_id": "enhanced_choice", "recovery_exposure_id": "moderate"},
{"risk_option_id": "risk-3", "risk": 2, "option_hash": "option-hash-002", "known_threat": {"enemy_count": 4, "wave_count": 3, "pressure_tags": ["elite"]}, "uncertainty_id": "elite_response_unresolved", "entry_cost": 8, "reward_coin": 34, "reward_class_id": "elite_cache", "recovery_exposure_id": "high"},
],
"accepted_party_snapshot": {
"snapshot_id": "snapshot-001",
"snapshot_hash": "snapshot-hash-001",
"order_hash": "order-hash-001",
"front_to_rear_ids": ["r-1", "r-2", "r-3", "r-4"],
"relations": [
{"position": 0, "recruit_id": "r-1", "first_threat_target": true, "protected_by_recruit_ids": []},
{"position": 1, "recruit_id": "r-2", "first_threat_target": false, "protected_by_recruit_ids": ["r-1"]},
{"position": 2, "recruit_id": "r-3", "first_threat_target": false, "protected_by_recruit_ids": ["r-1"]},
{"position": 3, "recruit_id": "r-4", "first_threat_target": false, "protected_by_recruit_ids": []},
],
"coverage": {"protection": 2, "long_reach": 2, "sustain": 1, "control": 1},
},
}
func _fixture_consequences() -> Array:
return [
{"id": "threat", "label": "THREAT", "text": "THREAT 3 · 2 WAVES", "value": "3 · 2 WAVES"},
{"id": "uncertainty", "label": "UNCERTAINTY", "text": "ONE INTENT UNRESOLVED", "value": "ONE INTENT"},
{"id": "entry", "label": "ENTRY COIN", "text": "4 · 34 LEFT", "value": "4 · 34 LEFT"},
{"id": "reward", "label": "REWARD", "text": "27 · ENHANCED", "value": "27 · ENHANCED"},
{"id": "recovery", "label": "RECOVERY", "text": "MODERATE", "value": "MODERATE"},
{"id": "retreat", "label": "RETREAT", "text": "NEXT STAGE", "value": "NEXT STAGE"},
]
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("M24 Risk Commitment Controller: %s" % message)