extends SceneTree const EntryShell = preload("res://entry/entry_shell.gd") const SettingsStore = preload("res://entry/settings_store.gd") var failures: Array[String] = [] func _initialize() -> void: call_deferred("run_test") func run_test() -> void: var settings := SettingsStore.new().defaults() settings.setup_completed = true settings.locale = "en" settings.text_scale = 1.0 var shell := EntryShell.new() shell.auto_start = false root.add_child(shell) await _frames(3) shell.settings = settings.duplicate(true) var company := _fixture_company() shell.show_guild_handoff(company) await _frames(3) expect(shell.current_screen == "guild_handoff", "Guild route renders through the Entry Shell") expect(shell.app_services.resolve_service(&"guild_page_controller") == shell.guild_page_controller, "AppServices exposes the Guild controller") expect(shell.guild_view_data.ok and shell.guild_view_data.contract.state == "empty", "Guild View consumes the empty controller snapshot") expect(shell.find_child("GuildContractBoard", true, false) != null and not shell.find_child("GuildContractBoard", true, false).disabled, "empty Guild exposes the Contract Board") expect(shell.find_child("GuildMarketUnavailable", true, false) != null and shell.find_child("GuildMarketUnavailable", true, false).disabled, "empty Guild locks Market") expect(shell.find_child("GuildParty", true, false).disabled and shell.find_child("GuildRisk", true, false).disabled, "empty Guild locks Party and Risk") var active := company.duplicate(true) active.active_contract = { "status": "market", "contract_id": "CTR-R1-01", "run_id": "run-001", "difficulty": "stormbound", "coin": 38, "line_capacity": 4, } shell.show_guild_handoff(active) await _frames(3) expect(shell.guild_view_data.contract.state == "active" and shell.guild_view_data.contract.coin == 38, "Guild View consumes active Contract facts") expect(shell.find_child("GuildContractActive", true, false) != null and shell.find_child("GuildContractActive", true, false).disabled, "active Guild blocks a second Contract") expect(shell.find_child("GuildMarket", true, false) != null and not shell.find_child("GuildMarket", true, false).disabled, "active Guild opens Market") expect(not shell.find_child("GuildParty", true, false).disabled and not shell.find_child("GuildRisk", true, false).disabled, "active Guild opens Party and Risk") expect(shell.find_child("GuildCoinStatus", true, false).text == "38", "Coin status renders controller-owned value") expect(shell.find_child("GuildAnchorCompanyStatus", true, false).text == "2", "Company status renders controller-owned count") var returned := active.duplicate(true) returned.active_contract.status = "returned" shell.show_guild_handoff(returned) await _frames(3) expect(shell.guild_view_data.contract.state == "returned", "Guild View consumes returned Contract state") expect(shell.find_child("GuildMarketUnavailable", true, false) != null and shell.find_child("GuildMarketUnavailable", true, false).disabled, "returned Guild keeps Market closed") expect(shell.find_child("GuildContractActive", true, false) != null and shell.find_child("GuildContractActive", true, false).disabled, "returned Guild keeps later Contract closed") shell.queue_free() await _frames(2) if failures.is_empty(): print("ABG_M20_GUILD_RUNTIME_OK states=empty,active,returned app_services=true destinations=contract,market,party,risk resources=coin,company callbacks=preserved") 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": "owned-001", "profession_id": "PF-B01", "rendered_name_snapshot": {"display_name": "Ari"}}, {"recruit_id": "owned-002", "profession_id": "PF-B02", "rendered_name_snapshot": {"display_name": "Bram"}}, ], "active_contract": {}, } func _frames(count: int) -> void: for index in range(count): await process_frame func expect(condition: bool, message: String) -> void: if not condition: failures.append("M20 Guild Runtime: %s" % message)