Files
TheTree_mock/tests/mvp/test_upkeep_legacy_midnight.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

81 lines
5.9 KiB
GDScript

extends SceneTree
var checks := 0
var failures := 0
func _initialize() -> void:
call_deferred("_run")
func expect(ok: bool, message: String) -> void:
checks += 1
if not ok:
failures += 1
push_error(message)
func _run() -> void:
var runtime: Variant = load("res://autoload/simulation_runtime.gd").new()
var clock: Variant = load("res://autoload/game_clock.gd").new()
var state: Variant = load("res://autoload/game_state.gd").new()
clock.reset(1, 6, 0)
state.reset()
expect(runtime.initialize(clock, state, 424242)[&"valid"], "initialize")
clock.set_paused(true)
expect(runtime.enable_production_operations()[&"ok"], "enable")
var definition: FacilityDefinition = runtime.definition_registry.get_facility(&"guest_corridor")
var old_rate := definition.maintenance_gold
definition.maintenance_gold = 1440
expect(runtime.submit_command(&"build_facility", {&"definition_id": &"guest_corridor", &"origin": Vector3i(4, 1, 0)}).is_success(), "build completed asset")
runtime.operations_coordinator._legacy_calendar_last_day = 1
clock.advance_minutes(1079)
var legacy: Dictionary = runtime.create_snapshot()
var data: Dictionary = legacy[&"systems"][&"runtime"]
data[&"runtime_schema_version"] = 9
preload("res://tests/fixtures/pre_upkeep_runtime.gd").strip_empty_upkeep(data)
data[&"operations"][&"coordinator"][&"schema_version"] = 3
data[&"operations"][&"coordinator"].erase(&"legacy_calendar_last_day")
data[&"operations"][&"coordinator"].erase(&"legacy_history_last_day")
data[&"operations"][&"coordinator"].erase(&"legacy_principal_profit_last_day")
expect(runtime.restore_snapshot(legacy), "constructed v9/v3 calendar fixture migrates before midnight")
var ledger_before: Dictionary = runtime.economy_service.create_snapshot()
runtime.set_operations_test_boundary_hook(&"MIDNIGHT_UPKEEP", func(_payload: Dictionary, phase: StringName, _token: Variant) -> Dictionary:
return {&"ok": phase != runtime.OPERATIONS_TEST_AFTER_FIRST_MUTATION_PHASE})
clock.advance_minutes(1)
expect(runtime.get_operations_report()[&"runtime_failure_latched"] and clock.absolute_minute == 1440, "midnight failure pauses before legacy financial close")
expect(runtime.economy_service.create_snapshot() == ledger_before and runtime.upkeep_settlement.get_settled_period_count() == 0, "midnight rollback restores cash ledger and receipt atomically")
var failed: Dictionary = runtime.create_snapshot()
expect(runtime.restore_snapshot(failed), "failed midnight checkpoint restores")
runtime.set_operations_test_boundary_hook(&"MIDNIGHT_UPKEEP", Callable())
expect(runtime.acknowledge_operations_pause()[&"ok"], "same midnight retries through existing acknowledgement")
expect(runtime.economy_service.get_balance(FacilityUpkeepSettlement.EXPENSE) == 1 and runtime.upkeep_settlement.get_settled_period_count() == 1, "only post-migration minute settles at midnight")
expect(runtime.operations_coordinator.get_day_summaries().is_empty(), "legacy payroll and financial day are not rewritten")
var midnight: Dictionary = runtime.create_snapshot()
expect(runtime.restore_snapshot(midnight) and runtime.create_snapshot() == midnight, "midnight interim payment roundtrips exactly")
clock.advance_minutes(360)
var summary: Dictionary = runtime.operations_coordinator.get_day_summary(1)
expect(not summary.is_empty() and summary[&"closed_at"] == 1800, "legacy financial close remains at 06:00")
if not summary.is_empty():
expect(summary[&"category_totals"][&"maintenance"] == 1 and runtime.economy_service.get_balance(FacilityUpkeepSettlement.EXPENSE) == 1, "06:00 reports midnight expense without a second charge")
expect(runtime.restore_snapshot(runtime.create_snapshot()), "06:00 history and earlier midnight receipt reconcile on load")
# A later failure must retain the already committed midnight payment exactly once.
expect(runtime.restore_snapshot(legacy), "restore before later-boundary failure")
runtime.set_operations_test_boundary_hook(&"ARRIVAL_PLAN", func(_payload: Dictionary, phase: StringName, _token: Variant) -> Dictionary:
return {&"ok": phase != runtime.OPERATIONS_TEST_PREPARE_PHASE})
clock.advance_minutes(1)
expect(runtime.get_operations_report()[&"runtime_failure_latched"] and runtime.economy_service.get_balance(FacilityUpkeepSettlement.EXPENSE) == 1, "later failure preserves committed midnight payment")
var after_payment_failure: Dictionary = runtime.create_snapshot()
expect(runtime.restore_snapshot(after_payment_failure), "save distinguishes committed payment from pending later boundary")
runtime.set_operations_test_boundary_hook(&"ARRIVAL_PLAN", Callable())
expect(runtime.acknowledge_operations_pause()[&"ok"] and runtime.economy_service.get_balance(FacilityUpkeepSettlement.EXPENSE) == 1 and runtime.upkeep_settlement.get_settled_period_count() == 1, "retry never duplicates earlier midnight payment")
# New interim debt blocks spending immediately, without rewriting old summaries.
expect(runtime.restore_snapshot(legacy), "restore legacy insufficient-cash branch")
var cash: int = runtime.economy_service.get_balance(&"PLAYER_CASH")
expect(runtime.economy_service.transfer(&"PLAYER_CASH", &"CONSTRUCTION_EXPENSE", cash, 1439, &"CONSTRUCTION").is_success(), "deplete cash")
clock.advance_minutes(1)
expect(runtime.get_operations_report()[&"arrears_total"] == 1 and runtime.operations_coordinator.get_arrears_total() == 0, "live debt is visible while closed financial history stays unchanged")
clock.set_paused(true)
var blocked: CommandResult = runtime.submit_command(&"build_facility", {&"definition_id": &"guest_corridor", &"origin": Vector3i(5, 1, 0)})
expect(not blocked.is_success() and blocked.details.get(&"reason") == &"UNPAID_EXPENSES", "interim upkeep debt blocks new investment at midnight")
expect(runtime.restore_snapshot(runtime.create_snapshot()), "interim debt saves and restores without a synthetic daily summary")
definition.maintenance_gold = old_rate
runtime.free()
clock.free()
state.free()
print("%s: %d checks" % ["PASS" if failures == 0 else "FAIL", checks])
quit(failures)