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.
This commit is contained in:
@@ -21,6 +21,7 @@ func _run() -> void:
|
||||
legacy_open["schema_version"] = 3
|
||||
legacy_open.erase("legacy_calendar_last_day")
|
||||
legacy_open.erase("legacy_history_last_day")
|
||||
legacy_open.erase("legacy_principal_profit_last_day")
|
||||
var legacy_restored := OperationsCoordinator.new()
|
||||
_expect(legacy_restored.restore_snapshot(legacy_open), "legacy save before its first close restores")
|
||||
legacy_restored.advance_to(1500)
|
||||
@@ -56,6 +57,10 @@ func _run() -> void:
|
||||
_expect(not unpaid.is_terminal() and unpaid.get_consecutive_insolvency_count() == 0, "profitable closes with arrears never count as losses")
|
||||
_expect(not unpaid.can_start_construction_or_investment()[&"allowed"], "unpaid wages still restrict new investment separately")
|
||||
_test_legacy()
|
||||
_test_principal_financing()
|
||||
_test_principal_repayment()
|
||||
_test_principal_command()
|
||||
_test_principal_profit_marker()
|
||||
print("%s: %d checks" % ["PASS" if _failures == 0 else "FAIL", _checks])
|
||||
quit(_failures)
|
||||
|
||||
@@ -96,3 +101,163 @@ func _expect(condition: bool, message: String) -> void:
|
||||
if not condition:
|
||||
_failures += 1
|
||||
push_error(message)
|
||||
|
||||
func _test_principal_financing() -> 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,1131009)[&"valid"],"principal fixture Runtime initializes")
|
||||
clock.set_paused(true)
|
||||
var before: Dictionary = runtime.create_snapshot()
|
||||
_expect(runtime.restore_snapshot(before) and var_to_bytes(before)==var_to_bytes(runtime.create_snapshot()),"no-loan current snapshot retains exact whole representation")
|
||||
var coordinator := OperationsCoordinator.new({&"start_minute":360})
|
||||
var loan := &"LOAN_LIABILITY:principal.fixture"
|
||||
_expect(runtime.economy_service.register_account(loan,true),"explicit fixture liability account")
|
||||
_expect(runtime.economy_service.transfer(loan,&"PLAYER_CASH",1000,clock.absolute_minute,&"LOAN_PRINCIPAL_BORROWED",&"principal.fixture",&"principal.borrow").is_success(),"real principal borrowed")
|
||||
_expect(runtime.economy_service.transfer(&"PLAYER_CASH",loan,400,clock.absolute_minute,&"LOAN_PRINCIPAL_REPAID",&"principal.fixture",&"principal.repay").is_success(),"real principal repayment")
|
||||
var report: Dictionary = runtime.call("_build_operations_daily_financials",360,361,0,[])
|
||||
_expect(report.get(&"ok",false),"valid principal ledger produces daily report")
|
||||
if report.get(&"ok",false):
|
||||
var totals: Dictionary = report[&"category_totals"]
|
||||
_expect(totals[&"revenue"]==0 and totals[&"cost_of_goods"]==0,"principal never enters operating sales or costs")
|
||||
_expect(totals[&"financing_principal_inflows"]==1000 and totals[&"financing_principal_outflows"]==400,"gross principal cash flows separately classified")
|
||||
_expect(report[&"facility_profit_loss"].is_empty(),"principal never allocated to facility profit")
|
||||
var normal: Dictionary = coordinator.call("_normalize_categories",totals)
|
||||
_expect(normal.get(&"ok",false) and normal[&"categories"]==totals,"Operations and Runtime retain identical financing categories")
|
||||
_expect(coordinator.call("_net_profit",normal[&"categories"])==-400,"original debt repayment counts as daily expense while borrowing remains outside sales")
|
||||
var expected: Variant = runtime.call("_operations_expected_category_totals",runtime.economy_service,360,361,0)
|
||||
_expect(expected==totals,"restore recomputes same full category totals")
|
||||
coordinator.configure_callbacks(func(payload: Dictionary, phase: StringName, token: Variant) -> Dictionary:
|
||||
if phase != &"apply":return {&"ok":true,&"token":token}
|
||||
var close: Dictionary = runtime.call("_build_operations_daily_financials",int(payload[&"period_start_minute"]),int(payload[&"period_end_minute_exclusive"]),0,[])
|
||||
close[&"settlement"]={&"arrears_total":0}
|
||||
return close)
|
||||
_expect(coordinator.advance_to(4320).is_success(),"three calendar days close with actual principal ledger")
|
||||
_expect(coordinator.get_day_summaries().size()==3,"three actual daily financing reports retained")
|
||||
var first: Dictionary = coordinator.get_day_summary(1)
|
||||
_expect(first[&"category_totals"][&"financing_principal_inflows"]==1000 and first[&"category_totals"][&"financing_principal_outflows"]==400 and first[&"net_profit"]==-400,"actual repayment reduces daily profit at the real day boundary")
|
||||
_expect(not coordinator.get_day_summary(2)[&"category_totals"].has(&"financing_principal_inflows") and coordinator.get_day_summary(2)[&"net_profit"]==0,"no-loan following day preserves old shape and profit")
|
||||
var days: Dictionary = coordinator.create_snapshot()
|
||||
var restored_days := OperationsCoordinator.new()
|
||||
_expect(restored_days.restore_snapshot(days) and var_to_bytes(restored_days.create_snapshot())==var_to_bytes(days),"whole three-day financing history restores byte-exact")
|
||||
var saved: Dictionary = runtime.create_snapshot()
|
||||
_expect(runtime.restore_snapshot(saved) and var_to_bytes(saved)==var_to_bytes(runtime.create_snapshot()),"whole principal snapshot restores exactly")
|
||||
var duplicate: CommandResult = runtime.economy_service.transfer(loan,&"PLAYER_CASH",1000,clock.absolute_minute,&"LOAN_PRINCIPAL_BORROWED",&"principal.fixture",&"principal.borrow")
|
||||
_expect(not duplicate.is_success() and var_to_bytes(saved)==var_to_bytes(runtime.create_snapshot()),"principal duplicate after restore remains whole state noop")
|
||||
var old_categories := {&"revenue":0,&"refunds":0,&"cost_of_goods":0,&"wages":0,&"maintenance":0,&"utilities":0,&"taxes_and_interest":0,&"fines":0,&"construction_cash_flow":0}
|
||||
var legacy: Dictionary = coordinator.call("_normalize_categories",old_categories)
|
||||
_expect(legacy[&"ok"] and var_to_bytes(legacy[&"categories"])==var_to_bytes(old_categories),"old categories retain exact shape without invented financing zeros")
|
||||
var bad := old_categories.duplicate(true);bad[&"mystery_financing"]=1
|
||||
_expect(not coordinator.call("_normalize_categories",bad)[&"ok"],"unclassified finance categories still reject")
|
||||
for value: Variant in [-1,"1000",OperationsCoordinator.MAX_FINANCIAL_AMOUNT+1]:
|
||||
bad=old_categories.duplicate(true);bad[&"financing_principal_inflows"]=value
|
||||
_expect(not coordinator.call("_normalize_categories",bad)[&"ok"],"invalid principal total rejects")
|
||||
var corrupt := EconomyService.new()
|
||||
_expect(corrupt.restore_snapshot(runtime.economy_service.create_snapshot()),"corruption fixture actual full economy")
|
||||
_expect(corrupt.transfer(loan,&"PLAYER_CASH",1,clock.absolute_minute,&"TRANSFER",&"principal.fixture",&"principal.unattested").is_success(),"explicit unattributed liability movement fixture")
|
||||
var altered := saved.duplicate(true)
|
||||
altered[&"systems"][&"runtime"][&"economy"]=corrupt.create_snapshot()
|
||||
altered[&"game_state"][&"resources"][&"gold"]=5601
|
||||
_expect(not runtime.restore_snapshot(altered) and var_to_bytes(saved)==var_to_bytes(runtime.create_snapshot()),"unattested liability movement rejects whole Runtime restore atomically")
|
||||
_expect(runtime.economy_service.get_balance(&"PLAYER_CASH")==5600 and runtime.economy_service.get_balance(loan)==-600,"exact remaining principal and cash")
|
||||
runtime.free();clock.free();state.free()
|
||||
|
||||
func _test_principal_repayment() -> 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,1161009)[&"valid"],"repayment Runtime initializes")
|
||||
clock.set_paused(true)
|
||||
runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false})
|
||||
var loan := &"LOAN_LIABILITY:repayment.fixture"
|
||||
_expect(runtime.economy_service.register_account(loan,true),"explicit existing fixture liability")
|
||||
_expect(runtime.economy_service.transfer(loan,&"PLAYER_CASH",1000,clock.absolute_minute,&"LOAN_PRINCIPAL_BORROWED",&"repayment.fixture",&"repayment.borrow").is_success(),"fixture principal borrowed in real ledger")
|
||||
var receipts: Array[Dictionary]=[]
|
||||
runtime.economy_service.transaction_posted.connect(func(id: StringName,_entries: Array) -> void:
|
||||
receipts.append({&"id":id,&"cash":runtime.economy_service.get_balance(&"PLAYER_CASH"),&"gold":state.get_resource(&"gold")}))
|
||||
for amount: Variant in [0,-1,1.5,"400",1001,OperationsCoordinator.MAX_FINANCIAL_AMOUNT+1]:
|
||||
var before: Dictionary=runtime.create_snapshot()
|
||||
_expect(not runtime.repay_loan_principal(&"repayment.fixture",amount,StringName("bad."+str(_checks))).is_success(),"invalid or excessive principal payment rejects")
|
||||
_expect(var_to_bytes(before)==var_to_bytes(runtime.create_snapshot()) and receipts.is_empty(),"rejected principal preserves whole state and notifications")
|
||||
var result: CommandResult=runtime.repay_loan_principal(&"repayment.fixture",400,&"repayment.first")
|
||||
_expect(result.is_success() and result.details[&"paid_principal"]==400 and result.details[&"outstanding_principal"]==600,"public partial repayment reports exact remaining principal")
|
||||
_expect(receipts.size()==1 and receipts[0][&"cash"]==5600 and receipts[0][&"gold"]==5600,"receipt sees committed principal and synchronized Gold")
|
||||
var saved: Dictionary=runtime.create_snapshot()
|
||||
_expect(not runtime.repay_loan_principal(&"repayment.fixture",400,&"repayment.first").is_success() and var_to_bytes(saved)==var_to_bytes(runtime.create_snapshot()) and receipts.size()==1,"principal replay is whole-state and receipt noop")
|
||||
_expect(runtime.restore_snapshot(saved) and var_to_bytes(saved)==var_to_bytes(runtime.create_snapshot()),"whole paid principal snapshot restores exact")
|
||||
_expect(not runtime.repay_loan_principal(&"repayment.fixture",400,&"repayment.first").is_success() and var_to_bytes(saved)==var_to_bytes(runtime.create_snapshot()),"restored payment key cannot charge twice")
|
||||
runtime.economy_service.begin_notification_scope()
|
||||
_expect(runtime.repay_loan_principal(&"repayment.fixture",600,&"repayment.final").is_success() and state.get_resource(&"gold")==5000,"nested final payment commits whole principal and Gold before receipt")
|
||||
runtime.economy_service.end_notification_scope()
|
||||
_expect(runtime.economy_service.get_balance(&"PLAYER_CASH")==5000 and runtime.economy_service.get_balance(loan)==0,"public final repayment extinguishes only the actual principal")
|
||||
var completed: Dictionary=runtime.create_snapshot()
|
||||
_expect(not runtime.repay_loan_principal(&"repayment.fixture",1,&"repayment.extra").is_success() and var_to_bytes(completed)==var_to_bytes(runtime.create_snapshot()),"extinguished principal cannot be overpaid")
|
||||
var report: Dictionary=runtime.call("_build_operations_daily_financials",360,361,0,[])
|
||||
_expect(report[&"ok"] and report[&"category_totals"][&"financing_principal_inflows"]==1000 and report[&"category_totals"][&"financing_principal_outflows"]==1000 and report[&"category_totals"][&"revenue"]==0 and report[&"category_totals"][&"cost_of_goods"]==0,"public repayment stays financing in actual daily report")
|
||||
runtime.free();clock.free();state.free()
|
||||
|
||||
func _test_principal_command() -> 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,1191009)[&"valid"],"principal command Runtime initializes")
|
||||
clock.set_paused(true);runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false})
|
||||
var id:=&"principal.command.fixture";var account:=&"LOAN_LIABILITY:principal.command.fixture"
|
||||
_expect(runtime.economy_service.register_account(account,true),"explicit command fixture liability")
|
||||
_expect(runtime.economy_service.transfer(account,&"PLAYER_CASH",1000,clock.absolute_minute,&"LOAN_PRINCIPAL_BORROWED",id,&"command.fixture.borrow").is_success(),"actual command fixture debt")
|
||||
var result: CommandResult=runtime.submit_command(&"repay_loan_principal",{&"loan_id":id,&"amount":400},&"player",id,0,-1,&"command.fixture.first")
|
||||
_expect(result.is_success() and result.command_id>0 and result.target_id==id and result.details[&"outstanding_principal"]==600,"ordinary paused command reports actual payment and target")
|
||||
_expect(runtime.economy_service.get_balance(&"PLAYER_CASH")==5600 and runtime.economy_service.get_balance(account)==-600 and state.get_resource(&"gold")==5600,"ordinary command commits exact principal and Gold")
|
||||
var bad_payloads: Array[Dictionary]=[{},{&"loan_id":123,&"amount":1},{&"loan_id":id,&"amount":1.5},{&"loan_id":id,&"amount":601},{&"loan_id":id,&"amount":1}]
|
||||
for i in range(bad_payloads.size()):
|
||||
var before: Dictionary=runtime.economy_service.create_snapshot();var gold: int=state.get_resource(&"gold")
|
||||
var key:=StringName("command.bad."+str(i)) if i<4 else &""
|
||||
result=runtime.submit_command(&"repay_loan_principal",bad_payloads[i],&"player",id,0,-1,key)
|
||||
_expect(not result.is_success(),"invalid principal command rejects without permissive conversion")
|
||||
_expect(var_to_bytes(before)==var_to_bytes(runtime.economy_service.create_snapshot()) and state.get_resource(&"gold")==gold,"failed command keeps exact financial state")
|
||||
var before: Dictionary=runtime.create_snapshot()
|
||||
_expect(runtime.submit_command(&"repay_loan_principal",{&"loan_id":id,&"amount":400},&"player",id,0,-1,&"command.fixture.first").code==CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY,"ordinary processed command key cannot repay twice")
|
||||
_expect(var_to_bytes(before)==var_to_bytes(runtime.create_snapshot()),"duplicate command preserves entire Runtime")
|
||||
var due: int=clock.absolute_minute+2
|
||||
_expect(runtime.submit_command(&"repay_loan_principal",{"loan_id":String(id),"amount":200},&"player",id,0,due,&"command.fixture.future").is_success(),"future principal command accepts serialized string payload")
|
||||
_expect(runtime.economy_service.get_balance(&"PLAYER_CASH")==5600,"future command has no early payment")
|
||||
var pending: Dictionary=runtime.create_snapshot();clock.advance_minutes(2)
|
||||
_expect(runtime.command_queue.get_result_by_idempotency_key(&"command.fixture.future").is_success() and runtime.economy_service.get_balance(account)==-400,"future command pays once through clock tick")
|
||||
var tx: StringName=runtime.economy_service.get_transaction_id_for_idempotency_key(&"command.fixture.future")
|
||||
var entries: Array=runtime.economy_service.get_transaction_entries(tx)
|
||||
_expect(entries.size()==1 and entries[0].absolute_minute==due,"principal ledger uses actual future execution minute")
|
||||
var continued: Dictionary=runtime.create_snapshot()
|
||||
_expect(runtime.restore_snapshot(pending) and var_to_bytes(pending)==var_to_bytes(runtime.create_snapshot()),"pending principal command restores exact")
|
||||
clock.advance_minutes(2)
|
||||
_expect(var_to_bytes(continued)==var_to_bytes(runtime.create_snapshot()),"pending principal command continuation equals original whole state")
|
||||
before=runtime.create_snapshot()
|
||||
_expect(runtime.submit_command(&"repay_loan_principal",{&"loan_id":id,&"amount":200},&"player",id,0,-1,&"command.fixture.future").code==CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY,"restored command key rejects repeat")
|
||||
_expect(var_to_bytes(before)==var_to_bytes(runtime.create_snapshot()),"restored duplicate preserves entire Runtime")
|
||||
_expect(runtime.submit_command(&"repay_loan_principal",{&"loan_id":id,&"amount":400},&"player",id,0,-1,&"command.fixture.final").is_success(),"queued remaining principal fully repays")
|
||||
_expect(runtime.economy_service.get_balance(&"PLAYER_CASH")==5000 and runtime.economy_service.get_balance(account)==0 and state.get_resource(&"gold")==5000,"queued final payment conserves exact initial cash")
|
||||
runtime.free();clock.free();state.free()
|
||||
|
||||
func _test_principal_profit_marker() -> void:
|
||||
var service := OperationsCoordinator.new()
|
||||
service.configure_callbacks(func(payload: Dictionary, phase: StringName, token: Variant) -> Dictionary:
|
||||
if phase != &"apply": return {&"ok": true, &"token": token}
|
||||
return {&"ok": true, &"category_totals": {&"financing_principal_outflows": 500}, &"facility_profit_loss": {}, &"anomalies": [], &"reconciliation_difference": 0, &"settlement": {&"arrears_total": 0}})
|
||||
_expect(service.advance_to(5000).is_success(), "principal expense closes through terminal boundary")
|
||||
_expect(service.current_minute == 4320 and service.is_terminal(), "three principal expenses stop at third midnight")
|
||||
for day in range(1, 4):
|
||||
var summary := service.get_day_summary(day)
|
||||
_expect(summary[&"net_profit"] == -500 and summary[&"consecutive_insolvency_count"] == day, "principal expense determines original daily profit and loss streak")
|
||||
var saved := service.create_snapshot()
|
||||
_expect(saved["schema_version"] == 5 and saved["legacy_principal_profit_last_day"] == 0, "new history uses explicit original profit basis")
|
||||
var restored := OperationsCoordinator.new()
|
||||
_expect(restored.restore_snapshot(saved) and restored.create_snapshot() == saved, "current original-profit terminal history restores exactly")
|
||||
for value: Variant in [-1, 4, 0.5, "0", null]:
|
||||
var bad := saved.duplicate(true)
|
||||
bad["legacy_principal_profit_last_day"] = value
|
||||
_expect(not restored.restore_snapshot(bad) and restored.create_snapshot() == saved, "invalid principal history marker rejects atomically")
|
||||
var missing := saved.duplicate(true)
|
||||
missing.erase("legacy_principal_profit_last_day")
|
||||
_expect(not restored.restore_snapshot(missing) and restored.create_snapshot() == saved, "missing current marker rejects atomically")
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
extends SceneTree
|
||||
|
||||
const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd")
|
||||
var _finishing := false
|
||||
|
||||
## Uses the real Main business controls and Runtime paid-service boundary.
|
||||
## No amount, deficit threshold or production attribute assignment is introduced.
|
||||
var _checks := 0
|
||||
@@ -211,7 +214,13 @@ func _expect(ok: bool, label: String) -> void:
|
||||
|
||||
|
||||
func _finish() -> void:
|
||||
if _finishing:
|
||||
return
|
||||
_finishing = true
|
||||
if is_instance_valid(_main):
|
||||
_main.queue_free()
|
||||
var final_cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, _main)
|
||||
if not bool(final_cleanup.get(&"passed", false)):
|
||||
_failures += 1
|
||||
push_error("ordinary Main pricing fixture audio cleanup failed")
|
||||
print("Pending service pricing report: %d checks, %d failures" % [_checks, _failures])
|
||||
quit(1 if _failures > 0 else 0)
|
||||
|
||||
@@ -19,6 +19,9 @@ func _run() -> void:
|
||||
_test_seed_424242_second_arrival_boundary()
|
||||
_test_after_mutation_notifications_are_atomic()
|
||||
_test_deferred_failure_backlog_and_exact_retry()
|
||||
_test_upcoming_reserved_request_capture_and_retry()
|
||||
_test_reserved_enqueue_rejection_is_atomic()
|
||||
_test_external_capture_rejects_invalid_state()
|
||||
_test_daily_payroll_financials_and_crosslink_tamper()
|
||||
_test_legacy_financial_calendar()
|
||||
_cleanup()
|
||||
@@ -391,6 +394,7 @@ func _test_legacy_financial_calendar() -> void:
|
||||
old[&"schema_version"] = 3
|
||||
old.erase(&"legacy_calendar_last_day")
|
||||
old.erase(&"legacy_history_last_day")
|
||||
old.erase(&"legacy_principal_profit_last_day")
|
||||
old[&"consecutive_insolvency_count"] = 0
|
||||
for row: Dictionary in old[&"day_summaries"]:
|
||||
row[&"consecutive_insolvency_count"] = 0
|
||||
@@ -420,3 +424,127 @@ func _expect(condition: bool, label: String) -> void:
|
||||
return
|
||||
_failures += 1
|
||||
push_error(" ✗ %s" % label)
|
||||
|
||||
|
||||
class RecordingInventory:
|
||||
extends InventoryService
|
||||
var successful_reservations: Array[Dictionary] = []
|
||||
func reserve_item(owner_inventory_id: StringName, item_id: StringName, quantity: int, requester_id: StringName, purpose: StringName, created_at: int = -1, expires_at: int = -1, operation_id: StringName = &"") -> Dictionary:
|
||||
var result := super.reserve_item(owner_inventory_id, item_id, quantity, requester_id, purpose, created_at, expires_at, operation_id)
|
||||
if bool(result.get(&"ok", false)):
|
||||
successful_reservations.append({&"result": result.duplicate(true), &"state": create_snapshot()})
|
||||
return result
|
||||
|
||||
|
||||
func _prepare_upcoming_inventory_fixture() -> FacilityInstance:
|
||||
_reset_runtime(424242)
|
||||
_expect(bool(_runtime.enable_production_operations().get(&"ok", false)), "upcoming inventory fixture enables Operations")
|
||||
_clock.advance_minutes(29)
|
||||
# Local integration seam uses the canonical definition and permitted Guest APIs.
|
||||
# It does not establish construction or ordinary OS business acceptance.
|
||||
_runtime._register_operational_facility({&"instance_id": &"upcoming_kitchen", &"definition_id": &"kitchen", &"completed_at": 389})
|
||||
var facility: FacilityInstance = _runtime.facility_service.get_instance(&"upcoming_kitchen")
|
||||
_expect(facility != null, "canonical kitchen registers for reservation integration")
|
||||
if facility == null: return null
|
||||
_expect(bool(_runtime.inventory_service.add_stack(facility.local_inventory_id, &"ingredient/raw_food", 8, 100, -1, 389).get(&"ok", false)), "private fixture holds canonical recipe input")
|
||||
var spawned: CommandResult = _runtime.submit_command(&"spawn_guest", {&"guest_id": &"upcoming_payer", &"guest_type_id": _runtime.get_mvp_guest_definition_ids()[0], &"options": {&"current_cell": _runtime._operations_entrance_cell()}}, &"test")
|
||||
_expect(spawned.is_success(), "dedicated reservation Guest spawns through Runtime command")
|
||||
_expect(bool(_runtime.guest_service.transition_guest(&"upcoming_payer", GuestRuntime.State.DECIDING, 389, &"TEST_FIXTURE").get(&"ok", false)), "dedicated Guest uses permitted decision transition")
|
||||
_expect(_runtime._validate_runtime_crosslinks(_runtime.facility_service, _runtime.guest_service, _runtime.economy_service, _runtime.inventory_service, _runtime.staff_service, 389, _runtime._staffing_projection_enabled), "reservation fixture starts with valid crosslinks")
|
||||
return facility
|
||||
|
||||
|
||||
func _test_upcoming_reserved_request_capture_and_retry() -> void:
|
||||
for failure_phase: StringName in [&"PREPARE", &"AFTER_FIRST_MUTATION"]:
|
||||
var facility := _prepare_upcoming_inventory_fixture()
|
||||
if facility == null: continue
|
||||
var observed := {&"token": {}, &"command_id": -1, &"completions": 0, &"guest": 0, &"economy": 0, &"arrival": 0, &"forced_failures": 0}
|
||||
_runtime.command_completed.connect(func(result: CommandResult) -> void:
|
||||
if result.command_id == observed[&"command_id"]: observed[&"completions"] += 1)
|
||||
_runtime.guest_service.guest_spawned.connect(func(_guest: GuestRuntime) -> void:
|
||||
if not observed[&"token"].is_empty(): observed[&"guest"] += 1)
|
||||
_runtime.economy_service.transaction_posted.connect(func(_id: StringName, _entries: Array) -> void:
|
||||
if not observed[&"token"].is_empty(): observed[&"economy"] += 1)
|
||||
_runtime.operations_coordinator.arrivals_committed.connect(func(_report: Dictionary) -> void:
|
||||
if not observed[&"token"].is_empty(): observed[&"arrival"] += 1)
|
||||
_runtime.set_operations_test_boundary_hook(&"ARRIVAL_PLAN", func(_payload: Dictionary, phase: StringName, token: Variant) -> Dictionary:
|
||||
if phase == &"PREPARE": observed[&"token"] = token.duplicate(true)
|
||||
if phase == failure_phase: observed[&"forced_failures"] += 1
|
||||
return {&"ok": phase != failure_phase, &"reason": &"UPCOMING_REQUEST_ROLLBACK"})
|
||||
_clock.set_paused(false)
|
||||
var queued: CommandResult = _runtime.submit_command(&"create_service_request", {&"request_id": &"upcoming_reserved", &"facility_id": facility.instance_id, &"guest_ids": [&"upcoming_payer"]}, &"test", facility.instance_id, 100, 390, &"upcoming.reserved")
|
||||
observed[&"command_id"] = queued.command_id
|
||||
_expect(queued.is_success(), "upcoming reserved request is queued for minute390")
|
||||
_clock.advance_one_minute()
|
||||
var token: Dictionary = observed[&"token"]
|
||||
_expect(observed[&"forced_failures"] == 1, "specified arrival fault phase is reached exactly once")
|
||||
_expect(not token.is_empty() and int(token.get(&"captured_minute", -1)) == 389 and int(token.get(&"captured_clock_minute", -1)) == 390, "upcoming queue preserves participant389 and captures clock390")
|
||||
_expect(_clock.paused and not _runtime.operations_coordinator.is_external_quarantined() and bool(_runtime.get_operations_report().get(&"runtime_failure_latched", false)), "forced arrival failure rolls back without quarantine")
|
||||
_expect(not token.is_empty() and var_to_bytes(token) == var_to_bytes(_runtime._build_operations_external_state()), "post-command external token restores literally including reservation queues IDs RNG and ledger")
|
||||
_expect(observed[&"guest"] == 0 and observed[&"economy"] == 0 and observed[&"arrival"] == 0, "rolled-back arrival emits no success notification")
|
||||
var request: ServiceRequest = _runtime.facility_service.get_request(&"upcoming_reserved")
|
||||
_expect(request != null and request.created_at == 390 and request.enqueued_at == 390 and request.reserved_inventory_ids.size() == 1, "request effective time and one reserved input survive rollback")
|
||||
if request != null and request.reserved_inventory_ids.size() == 1:
|
||||
var reservation: InventoryReservation = _runtime.inventory_service.get_reservation(request.reserved_inventory_ids[0])
|
||||
_expect(reservation != null and reservation.created_at == 390 and reservation.quantity == 4, "canonical raw-food reservation retains quantity and effective time")
|
||||
_runtime.set_operations_test_boundary_hook(&"ARRIVAL_PLAN", Callable())
|
||||
var retried: Dictionary = _runtime.acknowledge_operations_pause()
|
||||
_expect(bool(retried.get(&"ok", false)) and observed[&"completions"] == 1 and observed[&"arrival"] == 1 and observed[&"guest"] > 0 and observed[&"economy"] > 0, "retry commits arrivals once without replaying reserved request")
|
||||
_expect(_runtime._runtime_aggregate_committed_minute() == 390 and not bool(_runtime.get_operations_report().get(&"runtime_failure_latched", true)), "retry advances every participant suffix once")
|
||||
|
||||
|
||||
func _request_participants() -> Dictionary:
|
||||
return {&"inventory": _runtime.inventory_service.create_snapshot(), &"facilities": _runtime.facility_service.create_snapshot(), &"guests": _runtime.guest_service.create_snapshot(), &"economy": _runtime.economy_service.create_snapshot(), &"events": _runtime.domain_events.create_snapshot(), &"ids": _runtime.id_generator.create_snapshot()}
|
||||
|
||||
|
||||
func _test_reserved_enqueue_rejection_is_atomic() -> void:
|
||||
var facility := _prepare_upcoming_inventory_fixture()
|
||||
if facility == null: return
|
||||
# Zero queue space is an explicit rejection fixture, not a game value change.
|
||||
facility.queue_capacity = 0
|
||||
var recorded := RecordingInventory.new()
|
||||
_expect(recorded.restore_snapshot(_runtime.inventory_service.create_snapshot()), "reservation recording seam starts from exact inventory")
|
||||
_runtime.inventory_service = recorded
|
||||
var observed := {&"calls": 0, &"before": {}, &"after": {}, &"result": {}, &"result_code": -1, &"guest_notifications": -1}
|
||||
var notifications := {&"queue": 0, &"guest": 0}
|
||||
_runtime.unregister_command_executor(&"create_service_request")
|
||||
var installed: bool = _runtime.register_command_executor(&"create_service_request", func(command: SimulationCommand) -> CommandResult:
|
||||
observed[&"calls"] += 1
|
||||
observed[&"before"] = _request_participants()
|
||||
var guest_notifications_before: int = notifications[&"guest"]
|
||||
var result: CommandResult = _runtime._execute_create_service_request(command)
|
||||
observed[&"after"] = _request_participants()
|
||||
observed[&"result"] = result.to_dictionary()
|
||||
observed[&"result_code"] = result.code
|
||||
observed[&"guest_notifications"] = int(notifications[&"guest"]) - guest_notifications_before
|
||||
return result)
|
||||
_expect(installed, "request executor observation wrapper registers successfully")
|
||||
_runtime.facility_service.request_enqueued.connect(func(_request: ServiceRequest) -> void: notifications[&"queue"] += 1)
|
||||
_runtime.guest_service.guest_state_changed.connect(func(_id: StringName, _old: int, _new: int, _reason: StringName) -> void: notifications[&"guest"] += 1)
|
||||
_clock.set_paused(false)
|
||||
var queued: CommandResult = _runtime.submit_command(&"create_service_request", {&"facility_id": facility.instance_id, &"guest_ids": [&"upcoming_payer"]}, &"test", facility.instance_id, 100, 390, &"upcoming.rejected")
|
||||
_expect(queued.is_success(), "reservation rejection command enters normal queue")
|
||||
_clock.advance_one_minute()
|
||||
print("REJECTION_EXECUTOR_OBSERVATION ", JSON.stringify({&"calls": observed[&"calls"], &"result_code": observed[&"result_code"], &"result": observed[&"result"], &"guest_notifications": observed[&"guest_notifications"]}, "", true, true))
|
||||
_expect(observed[&"guest_notifications"] == 0, "failed executor emits no Guest state-change notification")
|
||||
_expect(observed[&"calls"] == 1 and observed[&"result_code"] == CommandResult.Code.VALIDATION_FAILED, "normal executor rejects queue after attempting request once")
|
||||
_expect(recorded.successful_reservations.size() == 1 and not recorded.successful_reservations[0][&"result"][&"reservation_ids"].is_empty(), "input reservation actually succeeds before enqueue rejection")
|
||||
_expect(var_to_bytes(observed[&"before"]) == var_to_bytes(observed[&"after"]), "enqueue rejection restores inventory facility Guest economy events and IDs literally before completion event")
|
||||
_expect(notifications[&"queue"] == 0, "failed enqueue emits no queue-success notification")
|
||||
_expect(_runtime._runtime_aggregate_committed_minute() == 390 and not _runtime.operations_coordinator.is_external_quarantined(), "rejected request leaves subsequent arrival and suffix healthy")
|
||||
|
||||
|
||||
func _test_external_capture_rejects_invalid_state() -> void:
|
||||
_reset_runtime(424242)
|
||||
_expect(bool(_runtime.enable_production_operations().get(&"ok", false)), "strict capture fixture enables Operations")
|
||||
var before := var_to_bytes(_runtime.create_snapshot())
|
||||
_expect(not _runtime._restore_operations_external_state({}) and not _runtime._restore_operations_external_state(null), "empty and invalid-type tokens are rejected")
|
||||
var valid: Dictionary = _runtime._build_operations_external_state()
|
||||
var corrupt: Dictionary = valid.duplicate(true)
|
||||
corrupt.erase(&"economy")
|
||||
_expect(not _runtime._restore_operations_external_state(corrupt), "missing required aggregate token is rejected")
|
||||
_expect(before == var_to_bytes(_runtime.create_snapshot()), "invalid token rejection preserves entire live snapshot")
|
||||
_runtime.facility_service.current_minute += 1
|
||||
_expect(_runtime._capture_operations_external_state().is_empty(), "real participant clock mismatch yields empty capture instead of success")
|
||||
_runtime.facility_service.current_minute -= 1
|
||||
_expect(before == var_to_bytes(_runtime.create_snapshot()), "restored fixture minute preserves canonical state")
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
extends SceneTree
|
||||
var checks := 0
|
||||
var failures := 0
|
||||
var runtime: Variant
|
||||
var clock: Variant
|
||||
var state: Variant
|
||||
var upper: FacilityInstance
|
||||
var front: FacilityInstance
|
||||
var lower: FacilityInstance
|
||||
func _initialize() -> void:
|
||||
call_deferred("run")
|
||||
func expect(ok: bool, label: String) -> void:
|
||||
checks += 1
|
||||
if not ok:
|
||||
failures += 1
|
||||
push_error("TOMARI_FLOOR_TROUBLE: " + label)
|
||||
func enqueue(id: StringName, context: Dictionary = {}) -> CommandResult:
|
||||
return runtime.event_service.enqueue_candidate(id, [], clock.absolute_minute, {&"context":context})
|
||||
func define(id: StringName, category: StringName, facility: StringName) -> void:
|
||||
expect(runtime.event_service.create_definition(id, {&"category":category, &"foreground":false, &"location_rule":{&"facility_id":facility}}).is_success(), "fixture definition registers")
|
||||
func run() -> void:
|
||||
runtime = load("res://autoload/simulation_runtime.gd").new()
|
||||
clock = load("res://autoload/game_clock.gd").new()
|
||||
state = load("res://autoload/game_state.gd").new()
|
||||
clock.reset(1,6,0)
|
||||
state.reset()
|
||||
expect(runtime.initialize(clock,state,42602)[&"valid"], "actual runtime initializes")
|
||||
clock.set_paused(true)
|
||||
for request: Array in [[&"reception",Vector3i(-6,0,0)],[&"warehouse",Vector3i(-6,0,1)],[&"premium_room",Vector3i(-6,2,0)],[&"warehouse",Vector3i(-6,2,1)],[&"service_stairs",Vector3i(-7,1,1)],[&"service_stairs",Vector3i(-7,2,1)]]:
|
||||
var built: CommandResult = runtime.submit_command(&"build_facility",{&"definition_id":request[0],&"origin":request[1]})
|
||||
expect(built.is_success(),"public construction succeeds")
|
||||
if not built.is_success():
|
||||
finish()
|
||||
return
|
||||
clock.advance_minutes(runtime.definition_registry.get_facility(request[0]).build_minutes)
|
||||
for facility: FacilityInstance in runtime.facility_service.get_instances():
|
||||
if facility.definition_id==&"warehouse":
|
||||
if facility.origin.y==2: upper=facility
|
||||
else: lower=facility
|
||||
if facility.definition_id==&"premium_room": front=facility
|
||||
expect(upper!=null and front!=null and lower!=null,"two floors and two layers exist")
|
||||
if upper==null or front==null or lower==null:
|
||||
finish()
|
||||
return
|
||||
# Controlled role fixture permits normal warehouse assignment. This is not recruitment evidence.
|
||||
expect(runtime.submit_command(&"register_staff",{&"staff_id":&"heroine_tomari",&"role_id":&"porter",&"options":{&"employment_kind":&"UNIQUE",&"current_cell":Vector3i(-8,0,0)}}).is_success(),"controlled unique identity registers")
|
||||
define(&"upper_trouble",&"TROUBLE",upper.instance_id)
|
||||
define(&"front_trouble",&"TROUBLE",front.instance_id)
|
||||
define(&"lower_trouble",&"TROUBLE",lower.instance_id)
|
||||
define(&"upper_flavor",&"FLAVOR",upper.instance_id)
|
||||
define(&"invalid_target",&"TROUBLE",&"unknown_fixture_target")
|
||||
expect(not runtime.is_floor_trouble_protected(2),"unassigned unique gives no floor protection")
|
||||
var queued := enqueue(&"upper_trouble")
|
||||
expect(queued.is_success(),"unassigned trouble enters actual queue")
|
||||
var pending: EventInstance = runtime.event_service.get_queued_instances()[0]
|
||||
expect(runtime.submit_command(&"set_staff_work_assignment",{&"staff_id":&"heroine_tomari",&"facility_id":upper.instance_id,&"template_id":&"NIGHT"}).is_success(),"public assignment checks role capacity and route")
|
||||
expect(runtime.is_floor_trouble_protected(2) and not runtime.is_floor_trouble_protected(0),"protection follows actual assigned floor")
|
||||
expect(not runtime.event_service.start_next_event(clock.absolute_minute).is_success(),"already queued protected trouble cannot start")
|
||||
expect(clock.paused,"pre-start placement cancellation preserves explicit player pause")
|
||||
expect(pending.state==EventInstance.State.CANCELLED and pending.failure_reason=="PLACEMENT_PROTECTION", "queued trouble canceled with explicit reason")
|
||||
expect(runtime.event_service.get_actor_locks().is_empty() and runtime.event_service.get_queued_instances().is_empty(),"cancel leaves no actor lock or waiting event")
|
||||
var before: Dictionary = runtime.create_snapshot()
|
||||
var bytes_before: PackedByteArray = var_to_bytes(before)
|
||||
for target: StringName in [&"upper_trouble",&"front_trouble",&"invalid_target"]:
|
||||
expect(not enqueue(target).is_success(),"protected or unknown target rejects enqueue")
|
||||
expect(not enqueue(&"upper_trouble",{&"facility_id":lower.instance_id}).is_success(),"conflicting target cannot bypass protection")
|
||||
expect(runtime.create_snapshot()==before and var_to_bytes(runtime.create_snapshot())==bytes_before,"rejection preserves whole live snapshot and IDs RNG queues")
|
||||
var worker: StaffRuntime = runtime.staff_service.get_staff(&"heroine_tomari")
|
||||
var old_kind := worker.employment_kind
|
||||
worker.employment_kind=StaffRuntime.EmploymentKind.FREE_HIRE
|
||||
expect(not runtime.is_floor_trouble_protected(2),"ordinary name spoof has no passive effect")
|
||||
worker.employment_kind=old_kind
|
||||
var old_state := worker.state
|
||||
worker.state=StaffRuntime.State.OFF_DUTY
|
||||
expect(runtime.is_floor_trouble_protected(2),"placement passive has no invented attendance gate")
|
||||
worker.state=old_state
|
||||
var old_operation := upper.operation_state
|
||||
upper.operation_state=FacilityInstance.OperationState.SUSPENDED
|
||||
expect(runtime.is_floor_trouble_protected(2),"placement passive has no invented workplace operation gate")
|
||||
upper.operation_state=old_operation
|
||||
expect(runtime.create_snapshot()==before and var_to_bytes(runtime.create_snapshot())==bytes_before,"observation fixtures restored byte exactly")
|
||||
expect(runtime.restore_snapshot(before),"same existing snapshot schema restores")
|
||||
expect(not enqueue(&"upper_trouble").is_success(),"restore reinstalls actual eligibility callback")
|
||||
var save_path := "user://tomari-floor-trouble.save"
|
||||
expect(runtime.save_to_path(save_path).get(&"ok",false),"actual disk save succeeds")
|
||||
expect(runtime.load_from_path(save_path).get(&"ok",false),"actual disk load succeeds")
|
||||
expect(not enqueue(&"front_trouble").is_success(),"disk load restores opposite-layer protection")
|
||||
expect(SaveService.delete_save(save_path),"owned fixture save cleaned through SaveService")
|
||||
expect(enqueue(&"lower_trouble").is_success(),"other floor remains eligible")
|
||||
expect(enqueue(&"upper_flavor").is_success(),"nontrouble on protected floor remains eligible")
|
||||
var ids: Array[StringName]=[]
|
||||
for event: EventInstance in runtime.event_service.get_queued_instances(): ids.append(event.event_instance_id)
|
||||
for id: StringName in ids: expect(runtime.event_service.cancel_event(id).is_success(),"eligible fixture queue cleanup")
|
||||
var missing: CommandResult = runtime.event_service.create_definition(&"missing_location",{&"category":&"TROUBLE",&"foreground":false})
|
||||
expect(missing.is_success() and not enqueue(&"missing_location").is_success(),"missing target is fail closed")
|
||||
filter_boundaries()
|
||||
reassignment_and_queued_restore()
|
||||
finish()
|
||||
func filter_boundaries() -> void:
|
||||
define(&"safe_replacement",&"FLAVOR",upper.instance_id)
|
||||
expect(runtime.event_service.create_definition(&"restricted_trouble",{&"category":&"TROUBLE",&"foreground":false,&"location_rule":{&"facility_id":upper.instance_id},&"content_rating":&"RESTRICTED",&"content_tags":[&"restricted_fixture"],&"filter_behavior":&"REPLACE",&"replacement_event_id":&"safe_replacement"}).is_success(),"restricted original fixture registers")
|
||||
expect(runtime.event_service.create_definition(&"restricted_flavor",{&"category":&"FLAVOR",&"foreground":false,&"content_rating":&"RESTRICTED",&"content_tags":[&"restricted_fixture"],&"filter_behavior":&"REPLACE",&"replacement_event_id":&"upper_trouble"}).is_success(),"restricted replacement fixture registers")
|
||||
runtime.event_service.configure_content_filter(EventDefinition.FilterBehavior.REPLACE,EventDefinition.ContentRating.GENERAL,[&"restricted_fixture"])
|
||||
var before: PackedByteArray=var_to_bytes(runtime.create_snapshot())
|
||||
expect(not enqueue(&"restricted_trouble").is_success(),"original trouble cannot bypass via flavor replacement")
|
||||
expect(not enqueue(&"restricted_flavor").is_success(),"resolved trouble cannot bypass via flavor original")
|
||||
expect(var_to_bytes(runtime.create_snapshot())==before,"both filter rejections mutate no live snapshot")
|
||||
func reassignment_and_queued_restore() -> void:
|
||||
clock.set_paused(true) # Normal player pause before immediate public command editing.
|
||||
expect(runtime.submit_command(&"set_staff_work_assignment",{&"staff_id":&"heroine_tomari",&"facility_id":lower.instance_id,&"template_id":&"NIGHT"}).is_success(),"public reassignment succeeds")
|
||||
expect(runtime.is_floor_trouble_protected(0) and not runtime.is_floor_trouble_protected(2),"protection transfers and releases previous floor")
|
||||
expect(not enqueue(&"lower_trouble").is_success(),"newly assigned floor is protected")
|
||||
expect(enqueue(&"restricted_trouble").is_success(),"original trouble queues before placement moves")
|
||||
expect(enqueue(&"restricted_flavor").is_success(),"replacement trouble queues before placement moves")
|
||||
expect(runtime.event_service.get_queued_instances().size()==2,"two filtered candidates waiting")
|
||||
var queued_snapshot: Dictionary=runtime.create_snapshot()
|
||||
expect(runtime.restore_snapshot(queued_snapshot),"existing schema restores waiting original and resolved IDs")
|
||||
expect(runtime.submit_command(&"set_staff_work_assignment",{&"staff_id":&"heroine_tomari",&"facility_id":upper.instance_id,&"template_id":&"NIGHT"}).is_success(),"public reassignment restores upper protection")
|
||||
var save_path := "user://tomari-queued.save"
|
||||
expect(runtime.save_to_path(save_path).get(&"ok",false) and runtime.load_from_path(save_path).get(&"ok",false),"disk roundtrip preserves already waiting candidates")
|
||||
var waiting: Array[EventInstance]=runtime.event_service.get_queued_instances()
|
||||
expect(waiting.size()==2,"disk roundtrip retains both candidate source identities")
|
||||
var counts := {&"started":0}
|
||||
runtime.event_service.event_started.connect(func(_event: EventInstance) -> void: counts[&"started"]+=1)
|
||||
expect(not runtime.event_service.start_next_event(clock.absolute_minute).is_success(),"restored queued originals and replacements cannot start after placement")
|
||||
expect(clock.paused,"restored placement cancellation preserves saved paused clock")
|
||||
for event: EventInstance in waiting:
|
||||
expect(event.state==EventInstance.State.CANCELLED and event.failure_reason=="PLACEMENT_PROTECTION", "each restored waiting candidate canceled before actions")
|
||||
expect(counts[&"started"]==0 and runtime.event_service.get_actor_locks().is_empty() and runtime.event_service.get_queued_instances().is_empty(),"no start signal actions lock or queue survives")
|
||||
expect(SaveService.delete_save(save_path),"second owned disk fixture cleaned")
|
||||
func finish() -> void:
|
||||
runtime.free()
|
||||
clock.free()
|
||||
state.free()
|
||||
print("TOMARI_FLOOR_TROUBLE checks=%d failures=%d" % [checks,failures])
|
||||
quit(failures)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bpx6dc10x2kr1
|
||||
@@ -30,7 +30,8 @@ func _run() -> void:
|
||||
data[&"operations"][&"coordinator"][&"schema_version"] = 3
|
||||
data[&"operations"][&"coordinator"].erase(&"legacy_calendar_last_day")
|
||||
data[&"operations"][&"coordinator"].erase(&"legacy_history_last_day")
|
||||
expect(runtime.restore_snapshot(legacy), "genuine v9/v3 calendar migrates before midnight")
|
||||
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})
|
||||
|
||||
Reference in New Issue
Block a user