extends SceneTree ## Runtime-level purchasing and replenishment. All physical stock is purchased ## and cooked normally; failure fixtures cover payment, closed routes and expiry. class RejectingPaymentEconomy: extends EconomyService func transfer(_from: StringName, _to: StringName, _amount: int, _minute: int = -1, _category: StringName = DEFAULT_CATEGORY, _source: StringName = &"", _key: StringName = &"", _memo: StringName = &"", _related: StringName = &"", _notify := true) -> CommandResult: return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, 0, "Injected payment refusal") const PORTER := &"staff_gina" const COOK := &"staff_chika" const RETURN_REQUEST := &"supply.management_return" var _checks := 0 var _failures := 0 var _completed_groups := 0 var _runtime: Variant var _clock: Variant var _state: Variant var _ids: Dictionary = {} var _built: Dictionary = {} func _initialize() -> void: call_deferred("_run") func _run() -> void: _runtime = root.get_node("SimulationRuntime") _clock = root.get_node("GameClock") _state = root.get_node("GameState") _clock.reset(1, 6, 0) _state.reset() _expect(_runtime.initialize(_clock, _state, 553221)[&"valid"], "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)], [ &"kitchen", Vector3i(2, 0, 1)], [&"cafeteria", Vector3i(2, 0, 0)], [&"staff_door", Vector3i(0, 0, 1)], ]: _command(&"build_facility", {&"definition_id": row[0], &"origin": row[1]}) _clock.advance_minutes(180) for facility: FacilityInstance in _runtime.facility_service.get_instances(): _ids[facility.definition_id] = facility.instance_id var completed := 0 for job: ConstructionJob in _runtime.construction_service.get_jobs(): if job.state == ConstructionJob.State.COMPLETED: completed += 1 _expect(_ids.has(&"warehouse") and _ids.has(&"kitchen") and _ids.has(&"cafeteria") and completed == 4, "normal starting budget completes three supply facilities and the staff door") if not _ids.has(&"warehouse") or not _ids.has(&"kitchen") or not _ids.has(&"cafeteria"): _finish() return _command(&"set_facility_conditions", {&"facility_id": _ids[&"cafeteria"], &"conditions": {&"related_facility_id": _ids[&"kitchen"]}}) _built = _runtime.create_snapshot() _test_paid_purchase_and_rejection() _test_disabled_kitchen_still_delivers() _test_multiple_sources_choose_connected_nearest() _test_returning_cargo_reserves_actual_space() _expect(_completed_groups == 4, "all four regression scenarios reach their final assertions") _finish() func _test_paid_purchase_and_rejection() -> void: var cash: int = _runtime.economy_service.get_balance(&"PLAYER_CASH") var quote: Dictionary = _runtime.preview_supply_purchase(_ids[&"warehouse"], SupplyCatalog.RAW_FOOD, 8) _expect(bool(quote[&"ok"]), "unassigned completed warehouse accepts a paid purchase") var result := _purchase(8) _expect(result.is_success() and _quantity(&"warehouse", SupplyCatalog.RAW_FOOD) == 8, "successful purchase creates exactly eight physical units") _expect(_runtime.economy_service.get_balance(&"PLAYER_CASH") == cash - int(quote[&"total_cost"]) and _state.get_resource(&"gold") == cash - int(quote[&"total_cost"]), "ledger and player cash both reflect the exact purchase price") var entries: Array = _runtime.economy_service.get_transaction_entries(StringName(result.details.get(&"transaction_id", &""))) _expect(entries.size() == 1 and entries[0].amount == int(quote[&"total_cost"]) and entries[0].source_entity_id == _ids[&"warehouse"], "purchase records one expense attributed to its warehouse") _assert_purchase_rejected({&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 0}, &"INVALID_QUANTITY") _assert_purchase_rejected({&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 100}, &"STORAGE_FULL") _assert_purchase_rejected({&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 1000}, &"INSUFFICIENT_FUNDS") _assert_purchase_rejected({&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.BASIC_MEAL, &"quantity": 1}, &"ITEM_NOT_PURCHASABLE") _assert_purchase_rejected({&"warehouse_id": _ids[&"kitchen"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 1}, &"NOT_WAREHOUSE") _runtime.facility_service.set_operational_status(_ids[&"warehouse"], {&"operator_open": false}, _clock.absolute_minute) _assert_purchase_rejected({&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 1}, &"FACILITY_CLOSED") _runtime.facility_service.set_operational_status(_ids[&"warehouse"], {&"operator_open": true}, _clock.absolute_minute) # Exercise the rollback after stock has been inserted, rather than only # testing quote failures that reject before the first inventory write. var original_economy: EconomyService = _runtime.economy_service var rejecting := RejectingPaymentEconomy.new() _expect(rejecting.restore_snapshot(original_economy.create_snapshot()), "payment-failure fixture retains all real balances") _runtime.economy_service = rejecting _assert_purchase_rejected({&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 1}, &"PAYMENT_FAILED") _runtime.economy_service = original_economy _completed_groups += 1 func _test_disabled_kitchen_still_delivers() -> void: _expect(_runtime.restore_snapshot(_built), "reset to the paid buildings for production") _hire_and_assign(PORTER, &"warehouse") _hire_and_assign(COOK, &"kitchen") _expect(_purchase(8).is_success(), "purchase ingredients for real production") _command(&"set_kitchen_production", {&"facility_id": _ids[&"kitchen"], &"enabled": true, &"target_stock": 4}) for minute: int in 100: if _quantity(&"kitchen", SupplyCatalog.BASIC_MEAL) >= 4: break _clock.advance_minutes(1) _expect(_quantity(&"kitchen", SupplyCatalog.BASIC_MEAL) == 4, "paid food is transported and cooked into one real batch") if _quantity(&"kitchen", SupplyCatalog.BASIC_MEAL) != 4: return _expect(_meal_requests().is_empty(), "production finishes before the following logistics tick creates a meal request") _command(&"set_kitchen_production", {&"facility_id": _ids[&"kitchen"], &"enabled": false, &"target_stock": 4}) # Preserve the legacy compatibility boundary: a kitchen with no explicit # production plan must not suddenly become an automatic source. _expect(_runtime.kitchen_production.remove_plan(_ids[&"kitchen"])[&"ok"], "completed disabled kitchen can model a legacy unconfigured source") _runtime.supply_management.ensure_deliveries(_runtime, _clock.absolute_minute) _expect(_meal_requests().is_empty(), "unconfigured kitchen stock does not trigger unsolicited automatic delivery") _command(&"set_kitchen_production", {&"facility_id": _ids[&"kitchen"], &"enabled": false, &"target_stock": 4}) _clock.advance_minutes(1) _expect(_meal_requests().size() == 1 and not _runtime.kitchen_production.get_plan(_ids[&"kitchen"])[&"enabled"], "an explicitly disabled production plan still dispatches its completed meals") _expect(_quantity(&"cafeteria", SupplyCatalog.BASIC_MEAL) == 0 and _runtime.inventory_service.available_quantity(_inventory(&"kitchen"), SupplyCatalog.BASIC_MEAL) == 0, "meal request reserves stock without instantly moving it to the dining room") _runtime.supply_management.ensure_deliveries(_runtime, _clock.absolute_minute) _runtime.supply_management.ensure_deliveries(_runtime, _clock.absolute_minute) _expect(_meal_requests().size() == 1, "repeated replenishment cannot duplicate the already reserved meal shipment") for minute: int in 100: if _quantity(&"cafeteria", SupplyCatalog.BASIC_MEAL) == 4: break _clock.advance_minutes(1) _expect(_quantity(&"cafeteria", SupplyCatalog.BASIC_MEAL) == 4 and _quantity(&"kitchen", SupplyCatalog.BASIC_MEAL) == 0, "real porter completes the outgoing shipment while kitchen production remains off") _completed_groups += 1 func _test_multiple_sources_choose_connected_nearest() -> void: _expect(_runtime.restore_snapshot(_built), "reset to the paid buildings for competing suppliers") _command(&"build_facility", {&"definition_id": &"warehouse", &"origin": Vector3i(6, 0, 1)}) _clock.advance_minutes(180) var near_id: StringName = &"" for facility: FacilityInstance in _runtime.facility_service.get_instances(): if facility.definition_id == &"warehouse" and facility.instance_id != _ids[&"warehouse"]: near_id = facility.instance_id _expect(not near_id.is_empty(), "starting budget also completes a second, closer warehouse") if near_id.is_empty(): return _expect(_purchase(8).is_success(), "older distant supplier holds eight paid units") _command(&"purchase_supplies", {&"warehouse_id": near_id, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 8}) var near: FacilityInstance = _runtime.facility_service.get_instance(near_id) var kitchen: FacilityInstance = _runtime.facility_service.get_instance(_ids[&"kitchen"]) var near_route: Dictionary = _runtime.supply_logistics.find_facility_access_path(Vector3i(6, 0, 1), kitchen) var far_route: Dictionary = _runtime.supply_logistics.find_facility_access_path(Vector3i(-5, 0, 1), kitchen) _expect(bool(near_route[&"reachable"]) and bool(far_route[&"reachable"]) and int(near_route[&"steps"]) < int(far_route[&"steps"]) and String(near_id) > String(_ids[&"warehouse"]), "both actual routes connect, and the shorter route belongs to the later warehouse ID") var stocked: Dictionary = _runtime.create_snapshot() _command(&"set_kitchen_production", {&"facility_id": _ids[&"kitchen"], &"enabled": true, &"target_stock": 8}) _clock.advance_minutes(1) var requests := _raw_requests_for_kitchen() _expect(requests.size() == 1 and int(requests[0].item_requirements[SupplyCatalog.RAW_FOOD]) == 4, "two stocked suppliers create only one four-unit shipment in the first logistics tick") _expect(not requests.is_empty() and requests[0].source_inventory_id == near.local_inventory_id, "connected nearby supplier wins over the older, farther warehouse") _expect(_runtime.inventory_service.available_quantity(near.local_inventory_id, SupplyCatalog.RAW_FOOD) == 4 and _runtime.inventory_service.available_quantity(_inventory(&"warehouse"), SupplyCatalog.RAW_FOOD) == 8 and _incoming(&"kitchen", SupplyCatalog.RAW_FOOD) == 4, "exactly four units are reserved once and counted once as incoming") _clock.advance_minutes(1) requests = _raw_requests_for_kitchen() _expect(requests.size() == 2 and _incoming(&"kitchen", SupplyCatalog.RAW_FOOD) == 8, "next tick adds at most one batch to cover the remaining four-unit deficit") _clock.advance_minutes(1) _expect(_raw_requests_for_kitchen().size() == 2 and _runtime.inventory_service.available_quantity(_inventory(&"warehouse"), SupplyCatalog.RAW_FOOD) == 8, "incoming stock at target stops extra requests and leaves other supplier stock untouched") # Geometric proximity must not beat an actual connection. Closing every # access cell of the near warehouse leaves the far route into the kitchen. _expect(_runtime.restore_snapshot(stocked), "rewind to the two unreserved suppliers") near = _runtime.facility_service.get_instance(near_id) kitchen = _runtime.facility_service.get_instance(_ids[&"kitchen"]) for cell: Vector3i in near.occupied_cells: for delta: Vector3i in [Vector3i.ZERO, Vector3i.LEFT, Vector3i.RIGHT, Vector3i.UP, Vector3i.DOWN]: if _runtime.path_service.has_cell(cell + delta): _runtime.path_service.set_cell_temporary_block(cell + delta, true) far_route = _runtime.supply_logistics.find_facility_access_path(Vector3i(-5, 0, 1), kitchen) near_route = _runtime.supply_logistics.find_facility_access_path(Vector3i(6, 0, 1), kitchen) _expect(bool(far_route[&"reachable"]) and not bool(near_route[&"reachable"]), "route closure really disconnects the nearby supplier while preserving the far route") _command(&"set_kitchen_production", {&"facility_id": _ids[&"kitchen"], &"enabled": true, &"target_stock": 8}) _clock.advance_minutes(1) requests = _raw_requests_for_kitchen() _expect(requests.size() == 1 and requests[0].source_inventory_id == _inventory(&"warehouse") and int(requests[0].item_requirements[SupplyCatalog.RAW_FOOD]) == 4, "unreachable near stock cannot steal the single shipment from a connected supplier") _completed_groups += 1 func _test_returning_cargo_reserves_actual_space() -> void: _expect(_runtime.restore_snapshot(_built), "reset to the paid buildings for returning cargo") _hire_and_assign(PORTER, &"warehouse") _expect(_purchase(2).is_success() and _purchase(6).is_success(), "two paid stacks provide an eight-unit shipment") _command(&"create_logistics_request", { &"request_id": RETURN_REQUEST, &"source_inventory_id": _inventory(&"warehouse"), &"destination_inventory_id": _inventory(&"kitchen"), &"item_requirements": {SupplyCatalog.RAW_FOOD: 8}, }) var request: LogisticsRequest = _runtime.logistics_requests[RETURN_REQUEST] for minute: int in 60: if request.state == LogisticsRequest.State.DELIVERING: break _clock.advance_minutes(1) var cargo_id := StringName("cargo:%s" % PORTER) _expect(request.state == LogisticsRequest.State.DELIVERING and _runtime.inventory_service.total_quantity(cargo_id, SupplyCatalog.RAW_FOOD) == 8, "actual pickup puts purchased goods in the employee's cargo") if request.state != LogisticsRequest.State.DELIVERING: return var inventory_snapshot: Dictionary = _runtime.inventory_service.create_snapshot() var injected := false for inventory: Dictionary in inventory_snapshot[&"inventories"]: if inventory[&"owner_inventory_id"] != String(cargo_id): continue for stack: Dictionary in inventory[&"stacks"]: if int(stack[&"quantity"]) == 2: stack[&"expires_at"] = _clock.absolute_minute + 1 injected = true _expect(injected, "the original two-unit paid stack remains identifiable inside real cargo") _expect(_runtime.inventory_service.restore_snapshot(inventory_snapshot), "inject expiry of only the two-unit paid stack") _clock.advance_minutes(1) _expect(request.state == LogisticsRequest.State.DELIVERING and request.state_reason == "RETURNING_EXPIRED_CARGO" and _runtime.inventory_service.total_quantity(cargo_id, SupplyCatalog.RAW_FOOD) == 6, "partial expiry starts a real return of the six surviving units") if request.state != LogisticsRequest.State.DELIVERING or request.state_reason != "RETURNING_EXPIRED_CARGO": return for reason: String in ["RETURNING_EXPIRED_CARGO", "EXPIRED_CARGO_RETURN_BLOCKED", "CARRIER_UNAVAILABLE"]: # Both bridge states retain the same real cargo. Capacity accounting # must follow that cargo even if the return route becomes blocked. request.state_reason = reason var quote: Dictionary = _runtime.preview_supply_purchase(_ids[&"warehouse"], SupplyCatalog.RAW_FOOD, 94) _expect(bool(quote[&"ok"]) and int(quote[&"max_quantity"]) == 94, "%s reserves six actual returning units, not the original eight" % reason) _expect(_incoming(&"warehouse", SupplyCatalog.RAW_FOOD) == 6 and _incoming(&"kitchen", SupplyCatalog.RAW_FOOD) == 0, "%s counts returning goods only at the true receiving warehouse" % reason) _assert_purchase_rejected({&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 95}, &"STORAGE_FULL") request.state_reason = "RETURNING_EXPIRED_CARGO" _expect(_purchase(94).is_success(), "buyer may use the exact free space left after reserving the return") for minute: int in 30: if request.is_terminal(): break _clock.advance_minutes(1) _expect(request.state == LogisticsRequest.State.CANCELLED and request.state_reason == "CARGO_EXPIRED_RETURNED", "capacity-aware purchase leaves enough room for the real return to complete") _expect(_quantity(&"warehouse", SupplyCatalog.RAW_FOOD) == 100 and _runtime.inventory_service.total_quantity(cargo_id, SupplyCatalog.RAW_FOOD) == 0 and _incoming(&"warehouse", SupplyCatalog.RAW_FOOD) == 0, "return fills warehouse exactly once and clears incoming cargo") _expect(_runtime.inventory_service.validate_invariants()[&"ok"] and SupplyLogisticsBridge.validate_crosslinks(_runtime.logistics_requests, _runtime.staff_service, _runtime.inventory_service, _runtime.facility_service), "completed return preserves inventory and carrier ownership invariants") _completed_groups += 1 func _assert_purchase_rejected(payload: Dictionary, reason: StringName) -> void: var inventory_before: Dictionary = _runtime.inventory_service.create_snapshot() var economy_before: Dictionary = _runtime.economy_service.create_snapshot() var gold_before: int = _state.get_resource(&"gold") var result: CommandResult = _runtime.submit_command(&"purchase_supplies", payload) _expect(not result.is_success() and result.details.get(&"reason") == reason, "purchase rejects %s" % reason) _expect(_runtime.inventory_service.create_snapshot() == inventory_before and _runtime.economy_service.create_snapshot() == economy_before and _state.get_resource(&"gold") == gold_before, "%s leaves full inventory, ledger and player cash unchanged" % reason) func _command(type: StringName, payload: Dictionary) -> CommandResult: var result: CommandResult = _runtime.submit_command(type, payload) _expect(result.is_success(), "%s succeeds: %s" % [type, result.details]) return result func _purchase(quantity: int) -> CommandResult: return _runtime.submit_command(&"purchase_supplies", {&"warehouse_id": _ids[&"warehouse"], &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": quantity}) func _hire_and_assign(staff_id: StringName, facility: StringName) -> void: _command(&"hire_staff_candidate", {&"candidate_id": staff_id}) _command(&"set_staff_work_assignment", {&"staff_id": staff_id, &"facility_id": _ids[facility], &"template_id": &"DAY"}) func _inventory(definition: StringName) -> StringName: return _runtime.facility_service.get_instance(_ids[definition]).local_inventory_id func _quantity(definition: StringName, item: StringName) -> int: return _runtime.inventory_service.total_quantity(_inventory(definition), item) func _incoming(definition: StringName, item: StringName) -> int: var report: Dictionary = _runtime.get_supply_management_report() var group := &"warehouses" if definition == &"warehouse" else &"kitchens" for row: Dictionary in report[group]: if StringName(row[&"facility_id"]) != _ids[definition]: continue for stock: Dictionary in row[&"stock"]: if StringName(stock[&"item_id"]) == item: return int(stock[&"in_transit"]) return -1 func _meal_requests() -> Array[LogisticsRequest]: var requests: Array[LogisticsRequest] = [] for request: LogisticsRequest in _runtime.logistics_requests.values(): if request.item_requirements.has(SupplyCatalog.BASIC_MEAL): requests.append(request) return requests func _raw_requests_for_kitchen() -> Array[LogisticsRequest]: var requests: Array[LogisticsRequest] = [] for request: LogisticsRequest in _runtime.logistics_requests.values(): if request.destination_inventory_id == _inventory(&"kitchen") and request.item_requirements.has(SupplyCatalog.RAW_FOOD): requests.append(request) return requests func _expect(condition: bool, message: String) -> void: _checks += 1 if not condition: _failures += 1 push_error(message) func _finish() -> void: print("Supply management service: %d checks, %d failures" % [_checks, _failures]) quit(1 if _failures > 0 else 0)