Files
aetherbound-guild/runtime/tests/rsk002/risk_commitment_runtime_test.gd
T
wushenghua 64c1c6fe7e feat(rsk002): implement risk commitment runtime
Reconstruct the approved contained 16:9 commitment page with engine-owned risk facts, frozen Party actors, transparent hit areas and explicit confirmation before the idempotent Battle Opening handoff. Add focused responsive behavior coverage and update the battle UI fixture for the confirmation boundary.
2026-08-23 17:45:07 +08:00

209 lines
9.4 KiB
GDScript

extends SceneTree
const EntryShell = preload("res://entry/entry_shell.gd")
const SettingsStore = preload("res://entry/settings_store.gd")
const DraftService = preload("res://entry/initial_company_draft_service.gd")
const ContractService = preload("res://entry/guild_contract_service.gd")
const FormationService = preload("res://entry/formation_risk_service.gd")
const BattleService = preload("res://entry/battle_service.gd")
const DRAFT_PATH := "user://rsk002_runtime_draft.json"
const COMPANY_PATH := "user://rsk002_runtime_company.json"
const SEED := "2468ace013579bdffedcba9876543210"
const CONTRACT_OP := "11112222333344445555666677778888"
const HIRE_OP_1 := "aaaabbbbccccddddeeeeffff00001111"
const HIRE_OP_2 := "99998888777766665555444433332222"
const ORDER_OP := "0123456789abcdef0123456789abcdef"
const READY_OP := "fedcba9876543210fedcba9876543210"
var failures: Array[String] = []
var parsed_touches := 0
var capture_root := ""
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
capture_root = OS.get_environment("ABG_RSK002_CAPTURE_DIR")
_cleanup()
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND
for viewport_size in [Vector2i(760, 360), Vector2i(844, 390), Vector2i(1280, 720)]:
root.size = viewport_size
for locale in ["zh_CN", "en"]:
_cleanup()
var company := _create_prepared_company()
if not company.is_empty():
await _verify_page(company, locale, viewport_size)
else:
failures.append("RSK-002 Runtime: fixture creation failed at %s %s" % [locale, viewport_size])
_cleanup()
if failures.is_empty():
print("ABG_RSK002_COMMITMENT_OK explicit_confirmation=true precommit_receipts=0 receipt_count=1 idempotent=true battle_handoff=true actors=4 facts=6 targets=48px locales=zh_CN,en viewports=760x360,844x390,1280x720 text_100=true parsed_touch=%d" % parsed_touches)
quit()
return
for failure in failures:
push_error(failure)
quit(1)
return
func _verify_page(company: Dictionary, locale: String, viewport_size: Vector2i) -> void:
var settings := SettingsStore.new().defaults()
settings.setup_completed = true
settings.locale = locale
settings.text_scale = 1.0
var viewport := SubViewport.new()
viewport.size = viewport_size
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
var shell := EntryShell.new()
shell.auto_start = false
shell.initial_company_service.path = DRAFT_PATH
shell.initial_company_service.company_path = COMPANY_PATH
viewport.add_child(shell)
await _frames(3)
shell.settings = settings
shell.setup_draft = settings.duplicate(true)
shell.risk_selected_id = String(company.active_contract.formation.risk_options[1].risk_option_id)
var before: Dictionary = _draft_service().load_company()
var before_hash := String(before.company.company_hash)
shell.show_risk_commitment(company)
await _frames(3)
await _capture(shell, "%dx%d-%s-before" % [viewport_size.x, viewport_size.y, locale])
expect(shell.current_screen == "risk_commitment", "%s %s enters commitment page" % [locale, viewport_size])
expect(shell.find_child("RiskCommitmentStaticMaster", true, false) != null, "%s static master is present" % locale)
expect(shell.find_child("RiskFrozenMembers", true, false).get_child_count() == 4, "%s exact four frozen members" % locale)
expect(shell.find_child("RiskConsequenceFact0", true, false) != null and shell.find_child("RiskConsequenceFact5", true, false) != null, "%s six consequence facts" % locale)
var begin := shell.find_child("RiskBeginBattle", true, false) as Button
var acknowledge := shell.find_child("RiskCommitmentAcknowledge", true, false) as Button
var settings_button := shell.find_child("RiskCommitmentSettings", true, false) as Button
expect(begin != null and begin.disabled, "%s Begin Battle is disabled before acknowledgement" % locale)
expect(acknowledge != null and not acknowledge.disabled, "%s acknowledgement is available" % locale)
var precommit: Dictionary = _draft_service().load_company()
expect(not Dictionary(precommit.company.active_contract).has("battle_receipts"), "%s no receipt before acknowledgement" % locale)
expect(String(_draft_service().load_company().company.company_hash) == before_hash, "%s precommit state is unchanged" % locale)
for control in [settings_button, acknowledge, begin, shell.find_child("RiskCommitmentCancel", true, false) as Button]:
if control != null:
expect(control.get_global_rect().size.x >= 48.0 and control.get_global_rect().size.y >= 48.0, "%s command target meets 48px" % locale)
await _touch(acknowledge)
await _capture(shell, "%dx%d-%s-ready" % [viewport_size.x, viewport_size.y, locale])
expect(shell.find_child("RiskCommitmentAcknowledge", true, false).get_meta("confirmed", false), "%s acknowledgement state is explicit" % locale)
begin = shell.find_child("RiskBeginBattle", true, false) as Button
expect(begin != null and not begin.disabled, "%s Begin Battle enables only after acknowledgement" % locale)
await _touch(begin)
expect(shell.current_screen == "battle", "%s acknowledgement hands off to BAT-001" % locale)
var started: Dictionary = _draft_service().load_company()
expect(started.ok and Array(started.company.active_contract.get("battle_receipts", [])).size() == 1, "%s exactly one battle receipt" % locale)
var battle: Dictionary = started.company.active_contract.battle_state
var snapshot: Dictionary = company.active_contract.formation.accepted_party_snapshot
expect(Array(battle.front_to_rear_ids) == Array(snapshot.front_to_rear_ids), "%s handoff preserves Party order" % locale)
expect(String(battle.snapshot_hash) == String(snapshot.snapshot_hash), "%s handoff preserves Party snapshot" % locale)
shell._invalidate_battle_playback()
shell.begin_battle()
await _frames(3)
var duplicate: Dictionary = _draft_service().load_company()
expect(Array(duplicate.company.active_contract.get("battle_receipts", [])).size() == 1, "%s duplicate input remains idempotent" % locale)
expect(String(duplicate.company.active_contract.battle_state.operation_id) == String(battle.operation_id), "%s duplicate returns original operation" % locale)
shell.queue_free()
viewport.queue_free()
await _frames(10)
func _create_prepared_company() -> Dictionary:
var draft: Dictionary = _draft_service().begin_or_load(SEED)
if not draft.ok:
return {}
_draft_service().set_selection([String(draft.draft.offers[0].offer_id), String(draft.draft.offers[2].offer_id)])
var committed: Dictionary = _draft_service().commit_company()
if not committed.ok:
return {}
var created: Dictionary = _contract_service().create_contract(CONTRACT_OP, "CTR-R1-01", "standard")
if not created.ok:
return {}
var offers: Array = created.company.active_contract.market_board.recruit_offers
_contract_service().hire_recruit(HIRE_OP_1, String(offers[0].source_offer_id), 14)
var hired: Dictionary = _contract_service().hire_recruit(HIRE_OP_2, String(offers[1].source_offer_id), 14)
if not hired.ok:
return {}
var begun: Dictionary = _formation_service().begin_formation()
var saved: Dictionary = _formation_service().save_order(ORDER_OP, begun.formation.base_front_to_rear_ids)
var ready: Dictionary = _formation_service().accept_readiness(READY_OP, String(saved.receipt.order_hash))
return ready.company if ready.ok else {}
func _draft_service() -> RefCounted:
var service := DraftService.new()
service.path = DRAFT_PATH
service.company_path = COMPANY_PATH
return service
func _contract_service() -> RefCounted:
var service := ContractService.new()
service.company_path = COMPANY_PATH
return service
func _formation_service() -> RefCounted:
var service := FormationService.new()
service.company_path = COMPANY_PATH
return service
func _touch(control: Control) -> void:
if control == null:
expect(false, "touch target exists")
return
var center := control.get_global_rect().get_center()
var press := InputEventScreenTouch.new()
press.position = center
press.pressed = true
press.index = 0
control.get_viewport().push_input(press)
var mouse_press := InputEventMouseButton.new()
mouse_press.position = center
mouse_press.button_index = MOUSE_BUTTON_LEFT
mouse_press.pressed = true
control.get_viewport().push_input(mouse_press)
await _frames(2)
var release := InputEventScreenTouch.new()
release.position = center
release.pressed = false
release.index = 0
control.get_viewport().push_input(release)
var mouse_release := InputEventMouseButton.new()
mouse_release.position = center
mouse_release.button_index = MOUSE_BUTTON_LEFT
mouse_release.pressed = false
control.get_viewport().push_input(mouse_release)
parsed_touches += 1
await _frames(5)
func _frames(count: int) -> void:
for _index in range(count):
await process_frame
func _capture(shell: Control, name: String) -> void:
if capture_root.is_empty() or not is_instance_valid(shell):
return
DirAccess.make_dir_recursive_absolute(capture_root)
await RenderingServer.frame_post_draw
var image := shell.get_viewport().get_texture().get_image()
image.save_png(capture_root.path_join(name + ".png"))
func _cleanup() -> void:
for path in [DRAFT_PATH, "%s.bak" % DRAFT_PATH, "%s.candidate" % DRAFT_PATH, "%s.bak.candidate" % DRAFT_PATH, COMPANY_PATH, "%s.bak" % COMPANY_PATH, "%s.candidate" % COMPANY_PATH, "%s.bak.candidate" % COMPANY_PATH, "%s.corrupt" % COMPANY_PATH]:
if FileAccess.file_exists(path):
DirAccess.remove_absolute(path)
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("RSK-002 Runtime: %s" % message)