extends SceneTree const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd") ## Normal build/product controls exercise drafts, a real paid tick, queued Save, ## normal load, duplicate prevention and removal-free disabled restart state. var checks := 0 var failures := 0 var main: Control var panel: SupplyManagementPanel var runtime: Variant var clock: Variant var state: Variant var warehouse: StringName = &"" var slot := "" func _initialize() -> void: call_deferred("run") func expect(ok: bool, message: String) -> void: checks += 1 if not ok: failures += 1 push_error("AUTO_PURCHASE_UI: " + message) func setup_scene(save_slot: String) -> void: runtime = root.get_node("SimulationRuntime") clock = root.get_node("GameClock") state = root.get_node("GameState") clock.set_process(false) clock.reset(1, 6, 0) state.reset() expect(runtime.initialize(clock, state, 420_502)[&"valid"], "current canonical Runtime initializes") clock.set_paused(true) root.size = Vector2i(1280, 720) main = load("res://scenes/main/main.tscn").instantiate() as Control main.manual_save_path = save_slot root.add_child(main) runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) await process_frame await process_frame panel = main.find_child("SupplyManagementPanel", true, false) as SupplyManagementPanel expect(panel != null, "ordinary scene owns product controls") if panel != null: main.call("_activate_management_mode", &"product") func build_warehouse() -> void: var count: int = runtime.construction_service.get_jobs().size() main.call("_select_build_definition", &"warehouse") main.call("_on_map_cell_clicked", Vector3i(-8, 0, 1)) expect(runtime.construction_service.get_jobs().size() == count + 1, "normal build palette/map submits paid construction") clock.advance_minutes(180) for facility: FacilityInstance in runtime.facility_service.get_instances(): if facility.definition_id == &"warehouse": warehouse = facility.instance_id expect(not warehouse.is_empty(), "completed ordinary warehouse becomes real purchase endpoint") main.call("_activate_management_mode", &"product") panel.refresh() expect(panel.selected_warehouse_id == warehouse and panel.selected_item_id == SupplyCatalog.RAW_FOOD, "ordinary product selects actual warehouse/catalog item") expect(not panel.auto_purchase_enabled.button_pressed and int(panel.auto_purchase_target.value) == 0, "new warehouse is explicitly disabled with no funded default") func stock() -> int: var facility: FacilityInstance = runtime.facility_service.get_instance(warehouse) return runtime.inventory_service.total_quantity(facility.local_inventory_id, SupplyCatalog.RAW_FOOD) if facility != null else 0 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() expect(bool(runtime.latest_save_operation_result.get(&"ok", false)), "ordinary Save reports success") 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 verify_order(target: int = 6) -> void: warehouse = panel.selected_warehouse_id var row: Dictionary = runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD) expect(row[&"enabled"] and row[&"target_stock"] == target and row[&"last_result"] == &"PURCHASED", "normal tick retains enabled player goal and paid receipt") expect(stock() == target and runtime.command_queue.pending_count() == 0, "actual warehouse has desired paid stock and no synthetic queued order") expect(runtime.economy_service.get_transaction_id_for_idempotency_key(SupplyAutoPurchasePlans.order_key(warehouse, SupplyCatalog.RAW_FOOD, row[&"last_attempt_minute"])) == StringName(row[&"last_transaction_id"]), "normal restored receipt resolves real ledger key") expect(state.get_resource(&"gold") == runtime.economy_service.get_balance(&"PLAYER_CASH"), "ordinary UI money mirror is ledger-owned") expect(panel.auto_purchase_status.text.contains("ON") and not panel.auto_purchase_button.disabled, "ordinary UI reconstructs enabled completed controls") func queue_intent(target: int = 6) -> void: var before: Dictionary = runtime.create_snapshot() panel.auto_purchase_enabled.button_pressed = true panel.auto_purchase_target.value = target expect(var_to_bytes(runtime.create_snapshot()) == var_to_bytes(before), "editing toggle/target changes draft only") expect(panel.auto_purchase_status.text.contains("未保存"), "UI distinguishes draft from active intent") clock.set_paused(false) panel.auto_purchase_button.pressed.emit() clock.set_paused(true) expect(runtime.command_queue.pending_count() == 1 and stock() == 0 and panel.auto_purchase_button.disabled, "normal Apply queues one intent without stock/payment") panel.auto_purchase_button.pressed.emit() expect(runtime.command_queue.pending_count() == 1, "disabled/repeated normal Apply cannot double queue") func run() -> void: var explicit_slot := OS.get_environment("THE_TREE_AUTO_PURCHASE_UI_SLOT") slot = explicit_slot if not explicit_slot.is_empty() else "user://tests/supply_auto_ui_%d_%d/manual" % [OS.get_process_id(), Time.get_ticks_usec()] if not valid_slot(slot) or DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(slot)): push_error("AUTO_PURCHASE_UI needs a fresh user://tests slot; player fallback forbidden") quit(90); return await setup_scene(slot) if panel == null: var _audio_cleanup_early_0: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(_audio_cleanup_early_0.get(&"passed", false)): push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_early_0)) quit(96) return quit(failures); return expect(panel.auto_purchase_button.disabled, "no warehouse is a safe empty setting state") build_warehouse() if warehouse.is_empty(): var _audio_cleanup_early_1: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(_audio_cleanup_early_1.get(&"passed", false)): push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_early_1)) quit(96) return quit(failures); return queue_intent() save_button() expect(SaveService.load_snapshot(slot)[&"ok"], "pending actual command is written to isolated disk") load_menu() panel.refresh() expect(runtime.command_queue.pending_count() == 1 and not runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)[&"enabled"] and panel.auto_purchase_button.disabled, "normal reload reconstructs one pending setting with no premature enabling") var cash: int = state.get_resource(&"gold") var next_id: int = runtime.create_snapshot()[&"systems"][&"runtime"][&"next_command_id"] clock.advance_minutes(1) panel.refresh() verify_order() expect(state.get_resource(&"gold") == cash - 18 and runtime.create_snapshot()[&"systems"][&"runtime"][&"next_command_id"] == next_id, "loaded intent charges18G once without allocating auto commandID") var paid: Dictionary = runtime.create_snapshot() panel.refresh() panel.select_tab(&"delivery") panel.select_tab(&"purchase") expect(var_to_bytes(runtime.create_snapshot()) == var_to_bytes(paid), "product navigation/refresh mutate no gameplay") panel.auto_purchase_enabled.button_pressed = false panel.auto_purchase_button.pressed.emit() expect(not runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD)[&"enabled"], "normal controls disable auto ordering") save_button() load_menu() panel.refresh() expect(not panel.auto_purchase_enabled.button_pressed and stock() == 6, "normal load preserves disabled intent and paid stock") await capture("normal-auto-purchase") SaveService.delete_save(slot) var _audio_cleanup_final: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(_audio_cleanup_final.get(&"passed", false)): push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_final)) quit(96) return await process_frame print("SUPPLY_AUTO_PURCHASE_UI checks=%d failures=%d" % [checks, failures]) quit(failures) func capture(label: String) -> void: var folder := OS.get_environment("THE_TREE_AUTO_PURCHASE_UI_CAPTURE_DIR") if folder.is_empty(): return expect(folder.is_absolute_path() and DirAccess.dir_exists_absolute(folder) and DisplayServer.get_name() != "headless", "explicit UI capture uses an existing QA output and real renderer") if not folder.is_absolute_path() or DisplayServer.get_name() == "headless": return var path := folder.path_join(label + ".png") expect(not FileAccess.file_exists(path), "UI capture is fresh") if FileAccess.file_exists(path): return await process_frame await RenderingServer.frame_post_draw expect(root.get_texture().get_image().save_png(path) == OK, "actual product image saved for ROOT visual review") func valid_slot(value: String) -> bool: if not value.begins_with("user://tests/") or value.contains("\\"): return false for segment: String in value.trim_prefix("user://tests/").split("/", true): if segment in ["", ".", ".."]: return false return value.ends_with("/manual")