extends SceneTree const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd") ## Candidate only, not compiled/executed. Normal construction, staff, save and ## load controls are exercised; the sole money fixture is an explicitly labelled ## ledger spend at 23:59 using existing valid accounts/category. No tuning edits. ## Invoke -- --read in a second process with the SAME unique test save slot. var checks := 0 var failures := 0 var main: Control var runtime: Variant var clock: Variant var slot := "" func _initialize() -> void: call_deferred("run") func expect(ok: bool, label: String) -> void: checks += 1 if not ok: failures += 1 push_error("FREE_UNIQUE_UI: " + label) func save_button() -> void: for button: Node in main.find_children("*","Button",true,false): if button.text == "保存": expect(not button.disabled,"normal save button enabled") button.pressed.emit() return expect(false,"normal save button exists") func load_menu() -> void: var menu := main.find_child("SaveLoadMenu",true,false) as MenuButton expect(menu != null and not menu.disabled,"normal load menu enabled") if menu != null and not menu.disabled: menu.get_popup().id_pressed.emit(0) func unique_hire_result() -> CommandResult: for saved: Dictionary in runtime.command_queue.create_snapshot()["processed"]: var details: Dictionary = saved.get("details",{}) if StringName(details.get(&"staff_id",&"")) == &"heroine_anri": return CommandResult.from_dictionary(saved) return null func verify_joined() -> void: var anri: StaffRuntime = runtime.staff_service.get_staff(&"heroine_anri") expect(anri != null and anri.employment_kind == StaffRuntime.EmploymentKind.UNIQUE and anri.role_id == &"innkeeper","normal read has canonical unique innkeeper") if anri == null: return expect(runtime.economy_service.get_balance(runtime.PLAYER_CASH_ACCOUNT) == 0 and runtime.upkeep_settlement.get_arrears_total() > 0,"joining preserves zero funds and actual unpaid upkeep") expect(anri.daily_wage == 0 and runtime.staff_service.get_daily_payroll_total() == 0,"unique upkeep remains zero") main.call("_set_staff_candidate_panel_open",true) var button: Button = main.staff_candidate_buttons.get(&"heroine_anri") expect(button != null and button.disabled and button.text == "加入済み","loaded normal UI cannot join unique twice") main.staff_management_toggle_button.pressed.emit() var panel := main.find_child("StaffManagementPanel",true,false) as StaffManagementPanel expect(panel != null,"normal hired staff panel present") if panel != null: panel.select_staff(&"heroine_anri") expect(panel.staff_detail_label.text.contains("アンリ") and panel.staff_detail_label.text.contains("日給 G0"),"normal management shows named character and no upkeep") var completed := unique_hire_result() expect(completed != null and completed.is_success() and completed.details.get(&"employment_kind") == &"UNIQUE","disk-persisted completed UI command has actual UNIQUE mode") var events := 0 for event: SimulationDomainEvent in runtime.domain_events.recent_events(): if event.event_type == &"STAFF_CANDIDATE_HIRED" and &"heroine_anri" in event.entity_ids: events += 1 expect(event.payload.get(&"employment_kind") == &"UNIQUE","disk-persisted joining event has corrected mode") expect(events == 1,"UI load contains one successful joining event") var before: Dictionary = runtime.create_snapshot() expect(not runtime.staff_service.retire_staff(&"heroine_anri").is_success() and not runtime.staff_service.remove_staff(&"heroine_anri",&"SOLD").is_success() and runtime.create_snapshot() == before,"loaded unique remains unsellable and non-retirable") var corrupt := before.duplicate(true) for row: Dictionary in corrupt[&"systems"][&"runtime"][&"staff"][&"staff"]: if row[&"staff_id"] == "heroine_anri": row[&"employment_kind"] = "FREE_HIRE" expect(not runtime.restore_snapshot(corrupt) and runtime.create_snapshot() == before,"save corruption cannot downgrade unique protection") func run() -> void: var explicit_slot: bool = OS.has_environment("THE_TREE_UNIQUE_FREE_UI_SLOT") slot = OS.get_environment("THE_TREE_UNIQUE_FREE_UI_SLOT") if not explicit_slot: slot = "user://tests/approved_free_unique_%d_%d/manual" % [OS.get_process_id(), Time.get_ticks_usec()] if DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(slot)): push_error("Generated test slot already exists; refuse overwrite.") quit(90) return var relative_slot := slot.trim_prefix("user://tests/") var valid_slot: bool = slot.begins_with("user://tests/") and not relative_slot.is_empty() and not relative_slot.ends_with("/") and not relative_slot.contains("\\") for segment: String in relative_slot.split("/", true): valid_slot = valid_slot and segment not in ["", ".", ".."] if not valid_slot: push_error("Explicit test slot is invalid; no production fallback.") quit(90) return runtime = root.get_node("SimulationRuntime") clock = root.get_node("GameClock") var state: Variant = root.get_node("GameState") clock.reset(1,6,0) state.reset() expect(runtime.initialize(clock,state,424242)[&"valid"],"canonical real Runtime initializes") clock.set_paused(true) root.size = Vector2i(1440,900) main = load("res://scenes/main/main.tscn").instantiate() main.manual_save_path = slot root.add_child(main) runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false}) await process_frame await process_frame if "--read" in OS.get_cmdline_user_args(): load_menu() expect(clock.absolute_minute == 1441,"second-process normal load restores exact joined minute") verify_joined() else: # Actual build palette and map handler; no direct completed-room insertion. main.call("_select_build_definition",&"reception") main.call("_on_map_cell_clicked",Vector3i(-6,0,0)) expect(runtime.construction_service.get_jobs().size() == 1,"normal UI submits reception construction") clock.advance_minutes(runtime.definition_registry.get_facility(&"reception").build_minutes) clock.advance_minutes(1439-clock.absolute_minute) var cash: int = runtime.economy_service.get_balance(runtime.PLAYER_CASH_ACCOUNT) # Explicit boundary fixture only; not a natural-spending/balance assertion. expect(runtime.economy_service.transfer(runtime.PLAYER_CASH_ACCOUNT,&"CONSTRUCTION_EXPENSE",cash,clock.absolute_minute,&"CONSTRUCTION",&"",&"acceptance.free_unique.ui_cash_fixture").is_success(),"controlled ledger spend before real upkeep close") clock.advance_one_minute() expect(clock.absolute_minute == 1440 and not runtime._operations_should_block_tick(),"ordinary first unpaid close completes without terminal/pause/failure") expect(runtime.upkeep_settlement.get_arrears_total() > 0,"real ownership upkeep supplies test debt") var economy_before: Dictionary = runtime.economy_service.create_snapshot() var arrears_before: int = runtime.upkeep_settlement.get_arrears_total() main.staff_candidate_toggle_button.pressed.emit() var paid: Button = main.staff_candidate_buttons.get(&"staff_tessa") expect(paid != null and not paid.disabled,"ordinary paid candidate still present") if paid != null: paid.pressed.emit() expect(not runtime.staff_service.has_staff(&"staff_tessa") and runtime.economy_service.create_snapshot() == economy_before,"normal paid hire still denied without ledger changes") var button: Button = main.staff_candidate_buttons.get(&"heroine_anri") expect(button != null and button.is_visible_in_tree() and not button.disabled and button.text == "無料で加入","normal staff pane offers already-approved free Anri despite unrelated debt") if button == null: var early_cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(early_cleanup.get(&"passed", false)): push_error("Approved free unique UI ordinary owner cleanup failed: %s" % str(early_cleanup)) quit(96) return quit(failures) return var before: Dictionary = runtime.create_snapshot() clock.set_paused(false) button.pressed.emit() clock.set_paused(true) expect(not runtime.staff_service.has_staff(&"heroine_anri") and button.disabled and runtime.command_queue.pending_count() == 1,"running UI queues exactly one free joining request") button.pressed.emit() expect(runtime.command_queue.pending_count() == 1,"repeated UI press cannot enqueue twice") save_button() expect(SaveService.load_snapshot(slot)[&"ok"],"actual UI stores pending free request") expect(runtime.restore_snapshot(before),"restore control before normal pending load") load_menu() expect(runtime.command_queue.pending_count() == 1 and not runtime.staff_service.has_staff(&"heroine_anri"),"normal disk load preserves pending request without premature joining") clock.advance_one_minute() expect(runtime.economy_service.create_snapshot() == economy_before and runtime.upkeep_settlement.get_arrears_total() == arrears_before,"executing restored free request does not spend or erase debt") verify_joined() save_button() # intentionally replace this explicit disposable test slot expect(SaveService.load_snapshot(slot)[&"ok"],"joined state saved for independent process read") var capture := OS.get_environment("THE_TREE_UNIQUE_FREE_UI_CAPTURE") if not capture.is_empty() and DisplayServer.get_name() != "headless": expect(capture.is_absolute_path() and not FileAccess.file_exists(capture),"new absolute rendered evidence path required") if capture.is_absolute_path() and not FileAccess.file_exists(capture): await process_frame await RenderingServer.frame_post_draw expect(root.get_texture().get_image().save_png(capture) == OK,"rendered named character and unpaid HUD captured") var cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(cleanup.get(&"passed", false)): push_error("Approved free unique UI ordinary owner cleanup failed: %s" % str(cleanup)) quit(96) return print("APPROVED_FREE_UNIQUE_UI phase=%s checks=%d failures=%d" % ["read" if "--read" in OS.get_cmdline_user_args() else "write",checks,failures]) quit(failures)