Files
TheTree_mock/tests/ui/test_supply_auto_purchase_ui.gd
Takeshi b84bade045 Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
2026-10-11 15:58:00 +09:00

174 lines
9.2 KiB
GDScript

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")