144 lines
8.7 KiB
GDScript
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)
|