extends SceneTree const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd") ## P05-02: front (表金) and hidden (裏金) funds, the A09 payment rule, the A10 rank ## tax, the A11 loan interest, the three business day reconciliation and saves. ## The policy and reconciliation scenes use explicit ledger fixtures for sales, ## debt and drains, with the production Operations closes. The repair 2 scenes ## run the production paths themselves: built and staffed facilities whose guests ## complete their services, the automatic purchase, the repayment command and the ## legacy 00:00 upkeep; a fixture there only sets a balance (a loan, a drain), never ## the payment under test. The last check opens the ledger from the game's own Main ## (F7), so the runner gives this suite the ordinary Main audio cleanup proof ## (tools/regression/suite-policy.json). const FRONT := &"PLAYER_CASH" const BACK := &"BACK_CASH" const LOAN := &"LOAN_LIABILITY:p05.fixture" const FRONT_SALES := &"P05_FIXTURE_FRONT_SALES" const BACK_SALES := &"P05_FIXTURE_BACK_SALES" const PAYER := &"P05_FIXTURE_PAYER" const SINK := &"P05_FIXTURE_SINK" var _checks := 0 var _failures := 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: print("P05-02: front and hidden funds") _test_policy_values() _test_sale_fund_routing() _test_three_business_days() _test_resume_matches_uninterrupted() _test_old_save_migration() _test_repayment_top_up() _test_purchase_top_up() _test_upkeep_top_up() _test_actual_service_funds() _test_operation_top_ups_and_day_total() _test_legacy_midnight_top_up() _test_tax_arrears_next_day() await _test_ledger_opened_from_main() await process_frame if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) func _expect(condition: bool, message: String) -> void: _checks += 1 if not condition: _failures += 1 push_error(message) func _new_fixture(seed: int) -> Dictionary: var clock: Variant = load("res://autoload/game_clock.gd").new() var state: Variant = load("res://autoload/game_state.gd").new() var runtime: Variant = load("res://autoload/simulation_runtime.gd").new() clock.reset(1, 6, 0) state.reset() _expect(bool(runtime.initialize(clock, state, seed).get(&"valid", false)), "fixture Runtime initializes") clock.set_paused(true) runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) _expect(bool(runtime.enable_production_operations().get(&"ok", false)), "fixture enables production Operations") for account: StringName in [FRONT_SALES, BACK_SALES, PAYER, SINK]: runtime.economy_service.register_account(account) runtime.economy_service.register_account(LOAN, true) # Guests' Gold enters the ledger only through explicit initialization. _expect(runtime.economy_service.initialize_account(PAYER, 100000, clock.absolute_minute, &"p05.fixture.payer").is_success(), "fixture payer funded") return {&"clock": clock, &"state": state, &"runtime": runtime} func _free_fixture(fixture: Dictionary) -> void: for key: StringName in [&"runtime", &"clock", &"state"]: if fixture.get(key) != null: fixture[key].free() # Same accelerated boundary traversal as tests/mvp/test_runtime_operations_acceptance.gd: # the fixtures hold no Facilities, Staff or logistics, only ledger postings, which # are made on 30-minute marks. func _advance(fixture: Dictionary, amount: int) -> void: var clock: Variant = fixture[&"clock"] var runtime: Variant = fixture[&"runtime"] var advanced := 0 while advanced < amount: # Every Operations boundary is on a 30-minute mark; each step ends on one. var step := mini(30 - int(clock.absolute_minute) % 30, amount - advanced) var target: int = clock.absolute_minute + step clock.absolute_minute = target clock.call("_sync_calendar_fields") clock.snapshot_version += step runtime.call("_advance_weather", target) runtime.call("_advance_facility_upkeep", target) runtime.call("_advance_operations_time", target) _align_clocks(runtime, target) advanced += step # Same as tests/mvp/test_runtime_operations_acceptance.gd: the fixture owns no # actors, cars or rides, so only the empty aggregates' clocks follow the minute. func _align_clocks(runtime: Variant, minute: int) -> void: for service: Variant in [runtime.inventory_service, runtime.facility_service, runtime.kitchen_production, runtime.guest_service, runtime.staff_service, runtime.authority_service, runtime.defense_service, runtime.construction_service, runtime.excavation_service]: service.current_minute = minute var transport_snapshot: Dictionary = runtime.elevators.create_snapshot() if not (transport_snapshot[&"shafts"] as Array).is_empty() or not (transport_snapshot[&"trips"] as Array).is_empty(): _expect(false, "accelerated fixture cannot skip live elevator movement") return transport_snapshot[&"current_minute"] = minute if not runtime.elevators.restore_snapshot(transport_snapshot): _expect(false, "empty transport clock aligns through validated snapshot restore") func _advance_to(fixture: Dictionary, minute: int) -> void: _advance(fixture, minute - int(fixture[&"clock"].absolute_minute)) func _sale(fixture: Dictionary, revenue_account: StringName, fund: StringName, amount: int, key: String) -> bool: var runtime: Variant = fixture[&"runtime"] var minute: int = fixture[&"clock"].absolute_minute var entries: Array = [ LedgerEntry.new().configure(&"", minute, revenue_account, PAYER, amount, &"SERVICE_SALE", &"p05.fixture"), LedgerEntry.new().configure(&"", minute, fund, revenue_account, amount, &"SERVICE_CASH_SWEEP", &"p05.fixture"), ] return runtime.economy_service.post(entries, StringName("p05.sale." + key)).is_success() func _move(fixture: Dictionary, from: StringName, to: StringName, amount: int, category: StringName, key: String) -> bool: var runtime: Variant = fixture[&"runtime"] return runtime.economy_service.transfer(from, to, amount, fixture[&"clock"].absolute_minute, category, &"p05.fixture", StringName("p05.move." + key)).is_success() func _fund_delta(runtime: Variant, account: StringName, after_minute: int, through_minute: int) -> int: var delta := 0 for entry: LedgerEntry in runtime.economy_service.get_entries(): if entry.absolute_minute <= after_minute or entry.absolute_minute > through_minute: continue if entry.debit_account == account: delta += entry.amount if entry.credit_account == account: delta -= entry.amount return delta func _test_policy_values() -> void: _expect(FinancePolicy.rank_tax(800) == 80 and FinancePolicy.rank_tax(109) == 10 and FinancePolicy.rank_tax(9) == 0, "A10 rank tax is 10% rounded down") _expect(FinancePolicy.rank_tax(-5) == 0, "no tax on a negative day") _expect(FinancePolicy.loan_interest(1000) == 10 and FinancePolicy.loan_interest(199) == 1 and FinancePolicy.loan_interest(99) == 0, "A11 interest is 1% per day rounded down") func _test_sale_fund_routing() -> void: var fixture := _new_fixture(5021) var runtime: Variant = fixture[&"runtime"] var back_ids: Array[StringName] = [] var front_ids: Array[StringName] = [] # Expansion definitions are read from their files; they are not registered by default. for path: String in ["res://data/facilities/adult_private_room.tres", "res://data/facilities/expansion/black_market.tres", "res://data/facilities/expansion/vip_private_salon.tres", "res://data/facilities/cafeteria.tres"]: var definition: Variant = load(path) if definition == null: continue if FinancePolicy.is_back_fund_service(definition.tags): back_ids.append(definition.id) else: front_ids.append(definition.id) _expect(back_ids.has(&"adult_private_room") and back_ids.has(&"black_market") and back_ids.has(&"vip_private_salon"), "adult, trade and private salon services earn hidden funds") _expect(front_ids.has(&"cafeteria"), "dining earns front funds") _expect(runtime.economy_service.has_account(BACK) and not runtime.economy_service.account_allows_negative(BACK), "the hidden fund is its own non-negative account") _expect(runtime.economy_service.get_balance(BACK) == 0 and runtime.economy_service.get_balance(FRONT) == 5000, "a new game starts with every Gold in front funds") _free_fixture(fixture) func _test_three_business_days() -> void: var fixture := _new_fixture(5022) var runtime: Variant = fixture[&"runtime"] var state: Variant = fixture[&"state"] var coordinator: Variant = runtime.operations_coordinator # Day 1: borrow 1000, front sale 300, hidden sale 500. _advance_to(fixture, 420) _expect(_move(fixture, LOAN, FRONT, 1000, &"LOAN_PRINCIPAL_BORROWED", "borrow"), "fixture principal borrowed") _expect(_sale(fixture, FRONT_SALES, FRONT, 300, "d1.front") and _sale(fixture, BACK_SALES, BACK, 500, "d1.back"), "day 1 sales") _advance_to(fixture, 1440) var day1: Dictionary = coordinator.get_day_summary(1) _expect(not day1.is_empty(), "day 1 closes at 00:00") var totals1: Dictionary = day1.get(&"category_totals", {}) _expect(int(totals1.get(&"revenue", -1)) == 800, "day 1 sales are front + hidden") _expect(int(totals1.get(&"taxes_and_interest", -1)) == 90, "day 1 rank tax 80 and interest 10") _expect(int(totals1.get(&"financing_principal_inflows", -1)) == 1000, "principal stays financing, outside sales") var funds1: Dictionary = day1.get(&"settlement", {}).get(&"funds", {}) _expect(int(funds1.get(&"revenue_front", -1)) == 300 and int(funds1.get(&"revenue_back", -1)) == 500, "day 1 fund report splits sales") _expect(int(funds1.get(&"rank_tax", -1)) == 80 and int(funds1.get(&"rank_tax_paid", -1)) == 80, "day 1 rank tax paid in full") _expect(int(funds1.get(&"loan_interest", -1)) == 10 and int(funds1.get(&"loan_interest_paid", -1)) == 10, "day 1 interest paid in full") _expect(int(funds1.get(&"back_top_up", -1)) == 0, "front funds alone paid day 1") _expect(runtime.economy_service.get_balance(FRONT) == 6210 and runtime.economy_service.get_balance(BACK) == 500, "day 1 balances") _expect(state.get_resource(&"gold") == runtime.economy_service.get_balance(FRONT), "Gold mirrors front funds") # The starting Gold is posted at 06:00 (minute 360); day 1 changes follow it. _expect(_fund_delta(runtime, FRONT, 360, 1440) == 1210 and _fund_delta(runtime, BACK, 360, 1440) == 500, "day 1 fund changes equal the ledger") # Day 2: front funds fall to 5, so 5 of the 10 interest comes from hidden funds. _advance_to(fixture, 1440 + 600) _expect(_move(fixture, FRONT, &"OPERATING_EXPENSE", 6205, &"OPERATING_EXPENSE", "d2.cost"), "day 2 purchase-like cost") _advance_to(fixture, 2880) var funds2: Dictionary = coordinator.get_day_summary(2).get(&"settlement", {}).get(&"funds", {}) _expect(int(funds2.get(&"back_top_up", -1)) == 5, "A09: hidden funds cover the 5 front funds cannot pay") _expect(int(funds2.get(&"loan_interest_paid", -1)) == 10 and int(funds2.get(&"rank_tax", -1)) == 0, "day 2 interest paid, no sales no tax") _expect(runtime.economy_service.get_balance(FRONT) == 0 and runtime.economy_service.get_balance(BACK) == 495, "day 2 balances") _expect(int(coordinator.get_day_summary(2)[&"category_totals"][&"revenue"]) == 0, "the top-up is not sales") _expect(_fund_delta(runtime, FRONT, 1440, 2880) == -6210 and _fund_delta(runtime, BACK, 1440, 2880) == -5, "day 2 fund changes equal the ledger") # Day 3: hidden funds are gone too, so the interest stays unpaid. _advance_to(fixture, 2880 + 600) _expect(_move(fixture, BACK, SINK, 495, &"TRANSFER", "d3.drain"), "day 3 hidden funds drained") _advance_to(fixture, 4320) var day3: Dictionary = coordinator.get_day_summary(3) var funds3: Dictionary = day3.get(&"settlement", {}).get(&"funds", {}) _expect(int(funds3.get(&"loan_interest", -1)) == 10 and int(funds3.get(&"loan_interest_paid", -1)) == 0 and int(funds3.get(&"loan_interest_arrears", -1)) == 10, "A09: unpaid interest carries over") _expect(int(day3[&"settlement"][&"arrears_total"]) == 10, "day 3 arrears include the interest") _expect(int(runtime.get_operations_report().get(&"arrears_total", -1)) == 10, "Operations report sees the interest arrears") _expect(int(day3[&"category_totals"][&"taxes_and_interest"]) == 10, "the unpaid interest is still the day's expense") # Day 4: a front sale pays the older interest first. _advance_to(fixture, 4320 + 600) _expect(_sale(fixture, FRONT_SALES, FRONT, 100, "d4.front"), "day 4 sale") _advance_to(fixture, 5760) var funds4: Dictionary = coordinator.get_day_summary(4).get(&"settlement", {}).get(&"funds", {}) _expect(int(funds4.get(&"rank_tax", -1)) == 10 and int(funds4.get(&"rank_tax_paid", -1)) == 10, "day 4 tax paid") _expect(int(funds4.get(&"loan_interest_paid", -1)) == 20 and int(funds4.get(&"loan_interest_arrears", -1)) == 0, "day 4 pays the older interest and the new one") _expect(runtime.economy_service.get_balance(FRONT) == 70, "day 4 front balance") _expect(runtime.economy_service.validate_ledger()[&"ok"], "the ledger reconciles after four closes") _check_ledger_panel(runtime, fixture[&"clock"]) var saved: Dictionary = runtime.create_snapshot() _expect(runtime.restore_snapshot(saved) and var_to_bytes(saved) == var_to_bytes(runtime.create_snapshot()), "four closed days restore exactly") _free_fixture(fixture) func _scripted_days(fixture: Dictionary, restore_at: Array[int], through_files := false) -> Dictionary: var runtime: Variant = fixture[&"runtime"] # Postings and save/restore points in time order; the last minute closes day 3. var steps: Array = [ [420, func() -> void: _move(fixture, LOAN, FRONT, 1000, &"LOAN_PRINCIPAL_BORROWED", "borrow")], [450, func() -> void: _sale(fixture, BACK_SALES, BACK, 700, "d1.back")], [1440, Callable()], [2040, func() -> void: _move(fixture, FRONT, &"OPERATING_EXPENSE", 5900, &"OPERATING_EXPENSE", "d2.cost")], [2400, Callable()], [2880, Callable()], [3510, func() -> void: _sale(fixture, FRONT_SALES, FRONT, 50, "d3.front")], [4320, Callable()], ] for step: Array in steps: var minute := int(step[0]) _advance_to(fixture, minute) if (step[1] as Callable).is_valid(): (step[1] as Callable).call() if minute in restore_at and through_files: # The ordinary save file path: write the save, then load it back. var path := "user://p05_resume_%d.save" % minute _expect(bool(runtime.save_to_path(path).get(&"ok", false)), "save file at minute %d" % minute) _expect(bool(runtime.load_from_path(path).get(&"ok", false)), "load the save file at minute %d" % minute) elif minute in restore_at: var saved: Dictionary = runtime.create_snapshot() _expect(runtime.restore_snapshot(saved), "restore at minute %d" % minute) return runtime.create_snapshot() func _test_resume_matches_uninterrupted() -> void: var straight := _new_fixture(5023) var resumed := _new_fixture(5023) var a := _scripted_days(straight, []) var b := _scripted_days(resumed, [1440, 2400, 2880]) _expect(var_to_bytes(a) == var_to_bytes(b), "saving mid-day and at 00:00 changes nothing: no double tax, interest or top-up") var runtime: Variant = resumed[&"runtime"] var keys := 0 for entry: LedgerEntry in runtime.economy_service.get_entries(): if entry.category in [FinancePolicy.RANK_TAX_CATEGORY, FinancePolicy.LOAN_INTEREST_CATEGORY]: keys += 1 # Day 1: tax 70 and interest 10; day 2: interest only (no sales); day 3: tax 5 and interest 10. _expect(keys == 5, "each tax and interest accrual is posted once (no duplicates)") # Through save files the save and load events are new, so compare the funds side only. var through_files := _new_fixture(5023) var c := _scripted_days(through_files, [1440, 2400, 2880], true) _expect(var_to_bytes(c[&"systems"][&"runtime"][&"economy"]) == var_to_bytes(a[&"systems"][&"runtime"][&"economy"]), "saving to files and loading them back leaves the ledger exactly as without saving") var straight_days: Array = straight[&"runtime"].operations_coordinator.get_day_summaries() var file_days: Array = through_files[&"runtime"].operations_coordinator.get_day_summaries() # Operations history is persisted in a transport-safe form (StringName ids become # String values), so compare as the Runtime's own restore crosslink does. var runtime_a: Variant = straight[&"runtime"] _expect(runtime_a.call("_strict_variants_equal", runtime_a.call("_operations_snapshot_comparison_value", straight_days), runtime_a.call("_operations_snapshot_comparison_value", file_days)), "the daily closes, fund reports and arrears are the same through save files") _free_fixture(straight) _free_fixture(resumed) _free_fixture(through_files) func _test_old_save_migration() -> void: var fixture := _new_fixture(5024) var runtime: Variant = fixture[&"runtime"] _advance_to(fixture, 420) var current: Dictionary = runtime.create_snapshot() var old := current.duplicate(true) var economy: Dictionary = old[&"systems"][&"runtime"][&"economy"] var kept: Array = [] for account: Dictionary in economy[&"accounts"]: if String(account[&"account_id"]) not in ["BACK_CASH", "RANK_TAX_EXPENSE", "RANK_TAX_PAYABLE", "LOAN_INTEREST_EXPENSE", "LOAN_INTEREST_PAYABLE"]: kept.append(account) economy[&"accounts"] = kept _expect(runtime.restore_snapshot(old), "A12: a save from before the split loads") _expect(runtime.economy_service.get_balance(FRONT) == 5000 and runtime.economy_service.get_balance(BACK) == 0, "A12: every Gold is front funds, hidden funds start at 0") _expect(var_to_bytes(runtime.create_snapshot()) == var_to_bytes(current), "A12: the migrated save equals a current save of the same game") var partial := old.duplicate(true) var partial_accounts: Array = partial[&"systems"][&"runtime"][&"economy"][&"accounts"] partial_accounts.append({&"account_id": "RANK_TAX_PAYABLE", &"allow_negative": true, &"system": false}) var before: Dictionary = runtime.create_snapshot() _expect(not runtime.restore_snapshot(partial) and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "a partial set of fund accounts is rejected and changes nothing") _free_fixture(fixture) func _test_repayment_top_up() -> void: var fixture := _new_fixture(5025) var runtime: Variant = fixture[&"runtime"] _advance_to(fixture, 420) _expect(_move(fixture, LOAN, FRONT, 1000, &"LOAN_PRINCIPAL_BORROWED", "borrow"), "fixture principal") _expect(_sale(fixture, BACK_SALES, BACK, 300, "back") and _move(fixture, FRONT, &"OPERATING_EXPENSE", 5980, &"OPERATING_EXPENSE", "cost"), "front 20, hidden 300") var receipts: Array = [] runtime.economy_service.transaction_posted.connect(func(id: StringName, _entries: Array) -> void: receipts.append(id)) var before: Dictionary = runtime.create_snapshot() _expect(not runtime.repay_loan_principal(&"p05.fixture", 400, &"p05.repay.too_much").is_success() and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "front + hidden short: repayment rejected, nothing changes") var paid: CommandResult = runtime.repay_loan_principal(&"p05.fixture", 200, &"p05.repay") _expect(paid.is_success() and int(paid.details.get(&"outstanding_principal", -1)) == 800, "repayment uses hidden funds for the rest") _expect(runtime.economy_service.get_balance(FRONT) == 0 and runtime.economy_service.get_balance(BACK) == 120, "front 20 first, then 180 from hidden funds") _expect(receipts.size() == 1, "payment and top-up are one transaction") var entries: Array = runtime.economy_service.get_transaction_entries(StringName(paid.details[&"transaction_id"])) _expect(entries.size() == 2 and entries[0].category == FinancePolicy.TOP_UP_CATEGORY and entries[1].category == &"LOAN_PRINCIPAL_REPAID", "top-up then principal") _expect(fixture[&"state"].get_resource(&"gold") == 0, "Gold mirrors front funds after repayment") _free_fixture(fixture) func _test_purchase_top_up() -> void: var clock: Variant = load("res://autoload/game_clock.gd").new() var state: Variant = load("res://autoload/game_state.gd").new() var runtime: Variant = load("res://autoload/simulation_runtime.gd").new() clock.reset(1, 6, 0) state.reset() _expect(bool(runtime.initialize(clock, state, 5026).get(&"valid", false)), "purchase fixture Runtime initializes") clock.set_paused(true) runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) for row: Array in [[&"warehouse", Vector3i(-8, 0, 1)], [&"staff_door", Vector3i(0, 0, 1)]]: _expect(runtime.submit_command(&"build_facility", {&"definition_id": row[0], &"origin": row[1]}).is_success(), "build %s" % row[0]) clock.advance_minutes(180) var warehouse := &"" for facility: FacilityInstance in runtime.facility_service.get_instances(): if facility.definition_id == &"warehouse": warehouse = facility.instance_id _expect(not warehouse.is_empty(), "warehouse completes") var fixture := {&"clock": clock, &"state": state, &"runtime": runtime} for account: StringName in [BACK_SALES, PAYER]: runtime.economy_service.register_account(account) _expect(runtime.economy_service.initialize_account(PAYER, 1000, clock.absolute_minute, &"p05.purchase.payer").is_success(), "fixture payer") var front: int = runtime.economy_service.get_balance(FRONT) _expect(_move(fixture, FRONT, &"OPERATING_EXPENSE", front - 2, &"OPERATING_EXPENSE", "purchase.drain") and _sale(fixture, BACK_SALES, BACK, 100, "purchase.back"), "front 2, hidden 100") var preview: Dictionary = runtime.preview_supply_purchase(warehouse, SupplyCatalog.RAW_FOOD, 5) _expect(bool(preview.get(&"ok", false)) and int(preview[&"total_cost"]) == 15 and int(preview[&"cash_after"]) == 87, "A09: the quote counts front + hidden funds") var paid: CommandResult = runtime.submit_command(&"purchase_supplies", {&"warehouse_id": warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 5}) _expect(paid.is_success(), "the purchase uses hidden funds for what front funds cannot pay") _expect(runtime.economy_service.get_balance(FRONT) == 0 and runtime.economy_service.get_balance(BACK) == 87, "front 2 first, then 13 from hidden funds") var purchase_entries: Array = runtime.economy_service.get_transaction_entries(StringName(paid.details.get(&"transaction_id", &""))) _expect(purchase_entries.size() == 1 and purchase_entries[0].credit_account == FRONT and purchase_entries[0].amount == 15, "the purchase receipt keeps its single front-fund entry") var top_ups := 0 for entry: LedgerEntry in runtime.economy_service.get_entries(): if entry.category == FinancePolicy.TOP_UP_CATEGORY: top_ups += entry.amount _expect(top_ups == 13, "one 13 Gold top-up from hidden funds") _expect(state.get_resource(&"gold") == 0, "Gold mirrors front funds after the purchase") # A refused command is still recorded as processed; the funds and stock must not move. var economy_before: Dictionary = runtime.economy_service.create_snapshot() var inventory_before: Dictionary = runtime.inventory_service.create_snapshot() var refused: CommandResult = runtime.submit_command(&"purchase_supplies", {&"warehouse_id": warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 40}) _expect(not refused.is_success() and StringName(refused.details.get(&"reason", &"")) == &"INSUFFICIENT_FUNDS", "front + hidden short: purchase refused for funds") _expect(var_to_bytes(economy_before) == var_to_bytes(runtime.economy_service.create_snapshot()) \ and var_to_bytes(inventory_before) == var_to_bytes(runtime.inventory_service.create_snapshot()), "the refused purchase moves no Gold and no stock") var saved: Dictionary = runtime.create_snapshot() _expect(runtime.restore_snapshot(saved) and var_to_bytes(saved) == var_to_bytes(runtime.create_snapshot()), "purchase with a top-up restores exactly") runtime.free() clock.free() state.free() func _test_upkeep_top_up() -> void: var fixture := _new_fixture(5027) var runtime: Variant = fixture[&"runtime"] var clock: Variant = fixture[&"clock"] _expect(runtime.submit_command(&"build_facility", {&"definition_id": &"warehouse", &"origin": Vector3i(-8, 0, 1)}).is_success(), "build a warehouse") clock.advance_minutes(180) _advance_to(fixture, 600) var front: int = runtime.economy_service.get_balance(FRONT) _expect(_move(fixture, FRONT, &"OPERATING_EXPENSE", front, &"OPERATING_EXPENSE", "upkeep.drain") and _sale(fixture, BACK_SALES, BACK, 1000, "upkeep.back"), "front 0, hidden 1000") _advance_to(fixture, 1440) var summary: Dictionary = runtime.operations_coordinator.get_day_summary(1) var maintenance := int(summary.get(&"category_totals", {}).get(&"maintenance", -1)) var funds: Dictionary = summary.get(&"settlement", {}).get(&"funds", {}) _expect(maintenance > 0, "the warehouse owes upkeep for day 1") _expect(int(funds.get(&"back_top_up", -1)) == maintenance + int(funds.get(&"rank_tax", 0)), "A09: hidden funds cover the upkeep (and tax) front funds cannot pay") _expect(runtime.upkeep_settlement.get_arrears_total() == 0 and int(summary[&"settlement"][&"arrears_total"]) == 0, "no upkeep arrears") _expect(runtime.economy_service.get_balance(FRONT) == 0 and runtime.economy_service.get_balance(BACK) == 1000 - int(funds[&"back_top_up"]), "front 0, hidden paid the rest") _free_fixture(fixture) func _command(runtime: Variant, type: StringName, payload: Dictionary) -> bool: var result: CommandResult = runtime.submit_command(type, payload) if not result.is_success(): push_error("%s: %s %s" % [type, result.message, result.details.get(&"reason", &"")]) return result.is_success() func _facility_of(runtime: Variant, definition_id: StringName) -> StringName: for facility: FacilityInstance in runtime.facility_service.get_instances(): if facility.definition_id == definition_id: return facility.instance_id return &"" func _day_funds(runtime: Variant, day: int) -> Dictionary: return runtime.operations_coordinator.get_day_summary(day).get(&"settlement", {}).get(&"funds", {}) func _closed_ledger_text(runtime: Variant, clock: Variant) -> String: var panel := OperatingLedgerPanel.new() root.add_child(panel) panel.setup(runtime, clock) panel.select_latest_close() var text: String = panel._closed_label.text panel.queue_free() return text # F1/F5 and F2 (wages) on the production paths. A front facility (massage_room, # staffed by an ordinary hire on a DAY assignment) and the registered hidden-service # facility (adult_private_room, underground, its service_attendant on a NIGHT # assignment) are built and open; the arriving guests choose and complete the # services themselves and the 00:00 closes settle the day. func _test_actual_service_funds() -> void: var fixture := _new_fixture(5031) var runtime: Variant = fixture[&"runtime"] var clock: Variant = fixture[&"clock"] var economy: Variant = runtime.economy_service # Rank 2 opens the underground, by the original room-zone suite's own route. _expect(_command(runtime, &"change_resources", {&"deltas": {&"gold": 5000, &"materials": 100}}) and _command(runtime, &"update_progression_metrics", {&"metrics": {&"reputation": 25}}) and _command(runtime, &"set_world_tree_rank", {&"rank": 2}), "rank 2 through the public commands") for x: int in range(0, 4): _expect(_command(runtime, &"start_excavation", {&"cell": Vector3i(x, -1, 0)}), "excavate (%d, -1)" % x) clock.advance_minutes(60) # Guests go down by a guest corridor and stairs, staff by a service corridor and stairs. _expect(_command(runtime, &"build_facility", {&"definition_id": &"guest_corridor", &"origin": Vector3i(0, -1, 0)}), "underground guest corridor") for x: int in range(1, 4): _expect(_command(runtime, &"build_facility", {&"definition_id": &"service_corridor", &"origin": Vector3i(x, -1, 1)}), "underground service corridor (%d, -1)" % x) clock.advance_minutes(30) _expect(_command(runtime, &"build_facility", {&"definition_id": &"guest_stairs", &"origin": Vector3i(0, 0, 0)}) and _command(runtime, &"build_facility", {&"definition_id": &"service_stairs", &"origin": Vector3i(3, 0, 1)}), "stairs down for guests and staff") clock.advance_minutes(30) _expect(not runtime.path_service.find_path(Vector3i(-2, 0, 0), Vector3i(0, -1, 0)).is_empty(), "guests can walk down to the room") _expect(_command(runtime, &"build_facility", {&"definition_id": &"adult_private_room", &"origin": Vector3i(1, -1, 0)}) and _command(runtime, &"build_facility", {&"definition_id": &"massage_room", &"origin": Vector3i(-6, 0, 0)}), "build the hidden-service and the front-service facility") clock.advance_minutes(240) var back_room := _facility_of(runtime, &"adult_private_room") var front_room := _facility_of(runtime, &"massage_room") _expect(not back_room.is_empty() and not front_room.is_empty(), "both facilities complete") _expect(_command(runtime, &"hire_staff_candidate", {&"candidate_id": &"staff_petra", &"current_cell": Vector3i(-5, 0, 0)}) and _command(runtime, &"set_staff_work_assignment", {&"staff_id": &"staff_petra", &"facility_id": front_room, &"template_id": &"DAY"}), "an ordinary hire works the massage room by day") # No hire candidate holds the room's service_attendant role (REPAIR_02.md), so it # is registered through the Runtime's register_staff command. _expect(_command(runtime, &"register_staff", {&"staff_id": &"p05_attendant", &"role_id": &"service_attendant", &"options": { &"current_cell": Vector3i(3, 0, 1), &"state": StaffRuntime.State.OFF_DUTY, &"permitted_task_tags": [&"service", &"work", &"adult_service"], &"equipment_tags": [&"uniform"], &"role_tags": [&"service_attendant"], &"skills": {&"service": 75}}}) and _command(runtime, &"set_staff_work_assignment", {&"staff_id": &"p05_attendant", &"facility_id": back_room, &"template_id": &"NIGHT"}), "the room's attendant works by night") clock.advance_minutes(721 - int(clock.absolute_minute)) _expect(runtime.facility_service.get_instance(front_room).state == FacilityInstance.State.OPEN, "the massage room opens with its day staff") clock.advance_minutes(1141 - int(clock.absolute_minute)) _expect(runtime.facility_service.get_instance(back_room).state == FacilityInstance.State.OPEN, "the room opens with its night staff") clock.advance_minutes(1440 - int(clock.absolute_minute)) # Day 1: every sale of the massage room went to front funds, of the room to hidden funds. var front_sales := 0 var back_sales := 0 var misrouted := 0 var back_payers := {} for entry: LedgerEntry in economy.get_entries(): if entry.category == &"SERVICE_SALE" and entry.source_entity_id == back_room and String(entry.credit_account).begins_with("GUEST_WALLET:guest-auto-"): back_payers[entry.credit_account] = true if entry.category != &"SERVICE_CASH_SWEEP" or entry.absolute_minute >= 1440: continue if entry.source_entity_id == front_room and entry.debit_account == FRONT: front_sales += entry.amount elif entry.source_entity_id == back_room and entry.debit_account == BACK: back_sales += entry.amount else: misrouted += 1 _expect(front_sales > 0 and back_sales > 0 and misrouted == 0, "front service sales sweep to PLAYER_CASH, hidden ones to BACK_CASH (front %d, hidden %d)" % [front_sales, back_sales]) _expect(not back_payers.is_empty(), "arriving guests went down to the room and paid for its service themselves (%d guests)" % back_payers.size()) var day1: Dictionary = runtime.operations_coordinator.get_day_summary(1) var funds1 := _day_funds(runtime, 1) _expect(int(funds1.get(&"revenue_front", -1)) == front_sales and int(funds1.get(&"revenue_back", -1)) == back_sales, "the 00:00 close reports the day's sales by fund") _expect(int(day1[&"category_totals"][&"revenue"]) == front_sales + back_sales, "sales are front + hidden") _expect(int(funds1.get(&"rank_tax", -1)) == FinancePolicy.rank_tax(front_sales + back_sales) and int(funds1.get(&"rank_tax", -1)) == (front_sales + back_sales) / 10 and int(funds1.get(&"rank_tax_paid", -1)) == int(funds1[&"rank_tax"]), "A10: the close taxes 10% of both sales, paid") _expect(int(funds1.get(&"back_top_up", -1)) == 0 and economy.get_balance(BACK) == back_sales and _fund_delta(runtime, BACK, 0, 1440) == back_sales, "hidden funds hold exactly the day's hidden sales") # Day 2: the front fund is lowered to 5 by a ledger fixture before the close, so the # close must pay the real wages, upkeep and tax with hidden funds. clock.advance_minutes(1440 + 1430 - int(clock.absolute_minute)) _expect(_move(fixture, FRONT, &"OPERATING_EXPENSE", economy.get_balance(FRONT) - 5, &"OPERATING_EXPENSE", "service.d2.drain"), "front funds lowered to 5") var back_before: int = economy.get_balance(BACK) clock.advance_minutes(2880 - int(clock.absolute_minute)) var day2: Dictionary = runtime.operations_coordinator.get_day_summary(2) var totals2: Dictionary = day2.get(&"category_totals", {}) var funds2 := _day_funds(runtime, 2) var due := int(totals2.get(&"wages", 0)) + int(totals2.get(&"maintenance", 0)) + int(totals2.get(&"taxes_and_interest", 0)) _expect(int(totals2.get(&"wages", 0)) > 0 and int(funds2.get(&"revenue_back", 0)) > 0, "day 2 has real wages and hidden sales") _expect(int(funds2.get(&"back_top_up", -1)) == due - 5, "A09: hidden funds cover the wages, upkeep and tax the 5 front funds cannot pay") _expect(int(day2[&"settlement"].get(&"unpaid_after_total", -1)) == 0 and int(day2[&"settlement"][&"arrears_total"]) == 0, "the wages are paid in full, nothing is in arrears") _expect(economy.get_balance(FRONT) == 0 and economy.get_balance(BACK) == back_before - (due - 5), "front 0, hidden funds paid the rest") var saved: Dictionary = runtime.create_snapshot() _expect(runtime.restore_snapshot(saved) and var_to_bytes(saved) == var_to_bytes(runtime.create_snapshot()), "the two service days restore exactly") _free_fixture(fixture) func _has_key(dictionary: Dictionary, key: String) -> bool: return dictionary.has(StringName(key)) or dictionary.has(key) func _set_key(dictionary: Dictionary, key: String, value: Variant) -> void: if dictionary.has(key): dictionary[key] = value else: dictionary[StringName(key)] = value func _erase_key(dictionary: Dictionary, key: String) -> void: dictionary.erase(StringName(key)) dictionary.erase(key) # The saved fund report of a day inside a Runtime snapshot; changing it changes the snapshot. func _saved_funds(snapshot: Dictionary, day: int) -> Dictionary: for summary: Dictionary in snapshot[&"systems"][&"runtime"][&"operations"][&"coordinator"][&"day_summaries"]: if int(summary.get(&"day", summary.get("day", -1))) == day: var settlement: Dictionary = summary.get(&"settlement", summary.get("settlement", {})) return settlement.get(&"funds", settlement.get("funds", {})) return {} # F2 (automatic purchase), F4 (interest after a repayment) and the day's top-up # total: the warehouse's automatic order and the repayment command run on their # production paths with too little front funds, then the 00:00 close. A report # saved before the definition mark is then loaded by its own definition. func _test_operation_top_ups_and_day_total() -> void: var fixture := _new_fixture(5032) var runtime: Variant = fixture[&"runtime"] var clock: Variant = fixture[&"clock"] var economy: Variant = runtime.economy_service _expect(_command(runtime, &"build_facility", {&"definition_id": &"warehouse", &"origin": Vector3i(-8, 0, 1)}), "build a warehouse") clock.advance_minutes(180) var warehouse := _facility_of(runtime, &"warehouse") _expect(not warehouse.is_empty(), "the warehouse completes") _expect(_move(fixture, LOAN, FRONT, 1000, &"LOAN_PRINCIPAL_BORROWED", "ops.borrow"), "fixture principal 1000") _expect(_move(fixture, FRONT, &"OPERATING_EXPENSE", economy.get_balance(FRONT) - 2, &"OPERATING_EXPENSE", "ops.drain") and _sale(fixture, BACK_SALES, BACK, 1000, "ops.back"), "front 2, hidden 1000") _expect(_command(runtime, &"set_supply_auto_purchase", {&"warehouse_id": warehouse, &"item_id": SupplyCatalog.RAW_FOOD, &"enabled": true, &"target_stock": 10}), "an automatic order for 10 raw food") clock.advance_minutes(1) var plan: Dictionary = runtime.auto_purchase_plans.get_plan(warehouse, SupplyCatalog.RAW_FOOD) var receipt := SupplyAutoPurchasePlans.receipt_from_ledger(economy, warehouse, SupplyCatalog.RAW_FOOD, clock.absolute_minute, StringName(plan.get(&"last_transaction_id", &""))) _expect(plan.get(&"last_result") == &"PURCHASED" and receipt == {&"item_id": String(SupplyCatalog.RAW_FOOD), &"quantity": 10, &"unit_price": 3, &"total_cost": 30}, "the automatic order buys 10 for 30 and its receipt verifies") var order_key := SupplyAutoPurchasePlans.order_key(warehouse, SupplyCatalog.RAW_FOOD, clock.absolute_minute) var order_top_up: Array = economy.get_transaction_entries(economy.get_transaction_id_for_idempotency_key(StringName("finance.back_top_up.purchase." + String(order_key)))) _expect(order_top_up.size() == 1 and order_top_up[0].category == FinancePolicy.TOP_UP_CATEGORY and order_top_up[0].amount == 28, "A09: hidden funds cover the 28 the automatic order's front funds cannot pay") _expect(economy.get_balance(FRONT) == 0 and economy.get_balance(BACK) == 972, "front 0, hidden 972 after the automatic order") var repaid: CommandResult = runtime.submit_command(&"repay_loan_principal", {&"loan_id": &"p05.fixture", &"amount": 200}, &"player", &"p05.fixture", 50, -1, &"p05.ops.repay") _expect(repaid.is_success() and int(repaid.details.get(&"outstanding_principal", -1)) == 800, "the repayment command pays 200 of the 1000") _expect(economy.get_balance(FRONT) == 0 and economy.get_balance(BACK) == 772, "hidden funds paid the repayment") clock.advance_minutes(1440 - int(clock.absolute_minute)) var day1: Dictionary = runtime.operations_coordinator.get_day_summary(1) var funds := _day_funds(runtime, 1) _expect(int(funds.get(&"loan_interest", -1)) == 8 and int(funds.get(&"loan_interest_paid", -1)) == 8, "A11: the close charges 1% of the 800 left after the repayment, 8, paid") _expect(int(funds.get(&"rank_tax", -1)) == 100 and int(funds.get(&"rank_tax_paid", -1)) == 100, "the hidden sale's tax is paid") var close_top_up := 0 for entry: LedgerEntry in economy.get_transaction_entries(economy.get_transaction_id_for_idempotency_key(&"finance.back_top_up.day.00000001")): close_top_up += entry.amount var every_top_up := 0 for entry: LedgerEntry in economy.get_entries(): if entry.category == FinancePolicy.TOP_UP_CATEGORY: every_top_up += entry.amount _expect(close_top_up == 8 + 100 + int(day1[&"category_totals"][&"maintenance"]), "the close tops up the interest, tax and upkeep") var total := 28 + 200 + close_top_up _expect(int(funds.get(&"back_top_up", -1)) == total and every_top_up == total, "funds.back_top_up is the day's every top-up: the order, the repayment and the close") _expect(int(funds.get(&"fund_report_version", -1)) == 2, "the report carries its definition version") _expect(economy.get_balance(BACK) == 1000 - total and economy.get_balance(FRONT) == 0, "hidden funds fell by exactly the day's top-ups") _expect(_closed_ledger_text(runtime, clock).contains("裏金から表金へ補填 %dG" % total), "the ledger's close shows the day's every top-up") var saved: Dictionary = runtime.create_snapshot() _expect(runtime.restore_snapshot(saved) and var_to_bytes(saved) == var_to_bytes(runtime.create_snapshot()), "the day with three top-ups restores exactly") # A report saved before the mark (back_top_up = the close's own top-up only) loads by that definition. var old := saved.duplicate(true) var old_funds := _saved_funds(old, 1) _expect(_has_key(old_funds, "fund_report_version"), "the saved report holds the mark") _erase_key(old_funds, "fund_report_version") _set_key(old_funds, "back_top_up", close_top_up) _expect(runtime.restore_snapshot(old), "an unmarked report with the close-only top-up loads") var loaded := _day_funds(runtime, 1) _expect(int(loaded.get(&"back_top_up", -1)) == close_top_up and not _has_key(loaded, "fund_report_version"), "the loaded old report stays as it was saved") _expect(_closed_ledger_text(runtime, clock).contains("裏金から表金へ補填(締めの時の分のみ) %dG" % close_top_up), "the ledger labels the old report's close-only top-up") # No looser check: each definition accepts only its own value. for case: Array in [[null, total, "an unmarked report with the day total"], [2, close_top_up, "a marked report with the close-only top-up"], [3, total, "an unknown mark"]]: var bad := saved.duplicate(true) var bad_funds := _saved_funds(bad, 1) if case[0] == null: _erase_key(bad_funds, "fund_report_version") else: _set_key(bad_funds, "fund_report_version", case[0]) _set_key(bad_funds, "back_top_up", case[1]) var before: Dictionary = runtime.create_snapshot() _expect(not runtime.restore_snapshot(bad) and var_to_bytes(before) == var_to_bytes(runtime.create_snapshot()), "%s is rejected and changes nothing" % case[2]) _expect(runtime.restore_snapshot(saved) and var_to_bytes(saved) == var_to_bytes(runtime.create_snapshot()), "the current save loads again") _free_fixture(fixture) # F2 (legacy calendar): in a save from before the 00:00 close the upkeep is paid at # calendar midnight and the financial day closes at 06:00 (as in # tests/mvp/test_upkeep_legacy_midnight.gd). With no front funds the midnight upkeep # is topped up from hidden funds, and that 06:00 day's report counts it. func _test_legacy_midnight_top_up() -> void: var fixture := _new_fixture(5033) var runtime: Variant = fixture[&"runtime"] var clock: Variant = fixture[&"clock"] var economy: Variant = runtime.economy_service var definition: FacilityDefinition = runtime.definition_registry.get_facility(&"guest_corridor") var old_rate := definition.maintenance_gold definition.maintenance_gold = 1440 _expect(_command(runtime, &"build_facility", {&"definition_id": &"guest_corridor", &"origin": Vector3i(4, 1, 0)}), "build a corridor with upkeep") 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), "the legacy-calendar save loads at 23:59") # The load replaces the Runtime's ledger service. economy = runtime.economy_service _expect(_move(fixture, FRONT, &"OPERATING_EXPENSE", economy.get_balance(FRONT), &"OPERATING_EXPENSE", "legacy.drain") and _sale(fixture, BACK_SALES, BACK, 500, "legacy.back"), "front 0, hidden 500") clock.advance_minutes(1) var midnight: Array = economy.get_transaction_entries(economy.get_transaction_id_for_idempotency_key(&"finance.back_top_up.midnight.1440")) _expect(midnight.size() == 1 and midnight[0].category == FinancePolicy.TOP_UP_CATEGORY and midnight[0].amount == 1, "A09: hidden funds cover the 00:00 upkeep front funds cannot pay") _expect(economy.get_balance(FacilityUpkeepSettlement.EXPENSE) == 1 and runtime.upkeep_settlement.get_arrears_total() == 0 and economy.get_balance(FRONT) == 0 and economy.get_balance(BACK) == 499, "the midnight upkeep is paid, nothing in arrears") clock.advance_minutes(360) var day1: Dictionary = runtime.operations_coordinator.get_day_summary(1) var funds := _day_funds(runtime, 1) _expect(int(day1.get(&"closed_at", -1)) == 1800 and int(funds.get(&"rank_tax", -1)) == 50 and int(funds.get(&"rank_tax_paid", -1)) == 50, "the 06:00 close taxes the hidden sale, paid") _expect(int(funds.get(&"back_top_up", -1)) == 51 and economy.get_balance(BACK) == 449, "the 06:00 day's report counts the midnight top-up and the close's") var saved: Dictionary = runtime.create_snapshot() _expect(runtime.restore_snapshot(saved) and var_to_bytes(saved) == var_to_bytes(runtime.create_snapshot()), "the legacy day restores exactly") definition.maintenance_gold = old_rate _free_fixture(fixture) # F3: the day's tax that front and hidden funds cannot pay stays payable, and the # next close pays that older tax before the new one. func _test_tax_arrears_next_day() -> void: var fixture := _new_fixture(5034) var runtime: Variant = fixture[&"runtime"] var economy: Variant = runtime.economy_service _advance_to(fixture, 420) _expect(_sale(fixture, FRONT_SALES, FRONT, 200, "tax.d1") and _move(fixture, FRONT, &"OPERATING_EXPENSE", economy.get_balance(FRONT), &"OPERATING_EXPENSE", "tax.d1.drain"), "day 1 sale 200, then no funds") _advance_to(fixture, 1440) var funds1 := _day_funds(runtime, 1) _expect(int(funds1.get(&"rank_tax", -1)) == 20 and int(funds1.get(&"rank_tax_paid", -1)) == 0 and int(funds1.get(&"rank_tax_arrears", -1)) == 20, "A09: the unpaid tax carries over") _expect(int(runtime.operations_coordinator.get_day_summary(1)[&"settlement"][&"arrears_total"]) == 20 and int(runtime.get_operations_report().get(&"arrears_total", -1)) == 20, "the arrears include the tax") _advance_to(fixture, 1440 + 600) _expect(_sale(fixture, FRONT_SALES, FRONT, 100, "tax.d2"), "day 2 sale 100") _advance_to(fixture, 2880) var funds2 := _day_funds(runtime, 2) _expect(int(funds2.get(&"rank_tax", -1)) == 10 and int(funds2.get(&"rank_tax_paid", -1)) == 30 and int(funds2.get(&"rank_tax_arrears", -1)) == 0, "day 2 pays the older 20 and the new 10") _expect(economy.get_balance(FRONT) == 70 and economy.get_balance(FinancePolicy.RANK_TAX_PAYABLE) == 0, "front 70, no tax payable left") var saved: Dictionary = runtime.create_snapshot() _expect(runtime.restore_snapshot(saved) and var_to_bytes(saved) == var_to_bytes(runtime.create_snapshot()), "the arrears days restore exactly") _free_fixture(fixture) # F7: the ordinary ledger panel reads both funds, the rank tax, the interest, the # top-ups and their arrears from the same ledger and daily closes. func _check_ledger_panel(runtime: Variant, clock: Variant) -> void: var panel := OperatingLedgerPanel.new() root.add_child(panel) panel.setup(runtime, clock) var report: Dictionary = panel.get_report() _expect(int(report.get(&"cash", -1)) == runtime.economy_service.get_balance(FRONT) and int(report.get(&"back_cash", -1)) == runtime.economy_service.get_balance(BACK), "the panel reports both funds") _expect(int(report.get(&"finance_arrears", -1)) == 0, "the panel reports no tax or interest arrears after day 4") var cash_text: String = panel._cash_label.text _expect(cash_text.contains("裏金 %dG" % runtime.economy_service.get_balance(BACK)) and cash_text.contains("本日の税") and cash_text.contains("利息"), "the cash section shows the hidden fund, tax and interest") panel.select_latest_close() var close_text: String = panel._closed_label.text _expect(close_text.contains("うち表の売上 100G / 裏の売上 0G") and close_text.contains("世界樹ランク税 10G(支払 10G)") \ and close_text.contains("利息 10G(支払 20G)") and close_text.contains("未払い 税 0G・利息 0G"), "the day 4 close shows sales by fund, tax, interest and arrears") panel.queue_free() # F7 through ordinary operation: after a real 00:00 close on the game's own # Runtime and clock, Main's 帳簿 navigation button opens the ledger. func _test_ledger_opened_from_main() -> void: var runtime: Variant = root.get_node("SimulationRuntime") var clock: Variant = root.get_node("GameClock") var state: Variant = root.get_node("GameState") clock.reset(1, 6, 0) state.reset() runtime.initialize(clock, state, 5028) clock.set_paused(true) root.size = Vector2i(1440, 900) var main: Control = load("res://scenes/main/main.tscn").instantiate() root.add_child(main) await process_frame runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) for account: StringName in [FRONT_SALES, BACK_SALES, PAYER]: runtime.economy_service.register_account(account) runtime.economy_service.register_account(LOAN, true) _expect(runtime.economy_service.initialize_account(PAYER, 100000, clock.absolute_minute, &"p05.fixture.payer").is_success(), "game Runtime fixture payer funded") clock.advance_minutes(60) var fixture := {&"clock": clock, &"state": state, &"runtime": runtime} _expect(_move(fixture, LOAN, FRONT, 1000, &"LOAN_PRINCIPAL_BORROWED", "ui.borrow") and _sale(fixture, FRONT_SALES, FRONT, 300, "ui.front") and _sale(fixture, BACK_SALES, BACK, 500, "ui.back"), "day 1 loan and sales on the game Runtime") clock.advance_minutes(1440 - int(clock.absolute_minute)) _expect(not runtime.operations_coordinator.get_day_summary(1).is_empty(), "the normal clock closes day 1 at 00:00") var ledger_button: Button = (main.get("navigation_buttons") as Dictionary)[&"ledger"] ledger_button.pressed.emit() var panel: OperatingLedgerPanel = main.get("operating_ledger_panel") _expect(panel.visible and ledger_button.button_pressed, "the 帳簿 button opens the ledger") var report: Dictionary = panel.get_report() _expect(int(report.get(&"cash", -1)) == 6210 and int(report.get(&"back_cash", -1)) == 500, "the opened ledger reports front 6210 and hidden 500") # The day 1 tax and interest are paid at the 00:00 that opens day 2. var cash_text: String = panel._cash_label.text _expect(cash_text.contains("裏金 500G(本日の入金 +0G / 表金へ補填 −0G)") and cash_text.contains("本日の税 80G / 利息 10G(未払い 0G)"), "the opened ledger shows the hidden fund, today's tax and interest") var close_text: String = panel._closed_label.text _expect(close_text.contains("うち表の売上 300G / 裏の売上 500G") and close_text.contains("世界樹ランク税 80G(支払 80G)/ 利息 10G(支払 10G)") and close_text.contains("裏金から表金へ補填 0G / 未払い 税 0G・利息 0G"), "the opened ledger shows the day 1 close by fund with tax and interest") var cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) _expect(bool(cleanup.get(&"passed", false)), "ordinary Main release with audio cleanup")