extends SceneTree var _checks := 0 var _failures := 0 var _clock: Variant var _state: Variant var _runtime: Variant func _initialize() -> void: call_deferred("_run") func _run() -> void: print("S3: runtime actor hardening regressions") _test_service_affordability_is_atomic() _test_guest_moves_one_cell_per_minute() _test_unreachable_staff_task_is_not_assigned() _test_shift_end_interrupts_and_fails_service() _test_corrupt_cross_domain_snapshots_are_atomic() _cleanup() if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) func _reset_runtime(seed: int) -> void: _cleanup() _clock = load("res://autoload/game_clock.gd").new() _state = load("res://autoload/game_state.gd").new() _runtime = load("res://autoload/simulation_runtime.gd").new() _clock.reset(1, 6, 0) _state.reset() var report: Dictionary = _runtime.initialize(_clock, _state, seed) _clock.set_paused(true) _expect(bool(report.get(&"valid", false)), "runtime initializes for hardening scenario %d" % seed) func _cleanup() -> void: if _runtime != null: _runtime.free() if _clock != null: _clock.free() if _state != null: _state.free() _runtime = null _clock = null _state = null func _test_service_affordability_is_atomic() -> void: _reset_runtime(3311) var facility: FacilityInstance = _build_cafeteria() if facility == null: return _expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 3, &"hardening.stock.affordability"), "affordability scenario stocks three meals") _expect(_register_wallet(&"low_funds", 20, &"hardening.wallet.low"), "low-balance wallet registers") var available_before: int = _runtime.inventory_service.available_quantity( facility.local_inventory_id, &"ingredient/basic_meal" ) var reservations_before: int = _runtime.inventory_service.get_reservations(true).size() var low_result = _runtime.submit_command( &"create_service_request", { &"request_id": &"request_low_funds", &"facility_id": facility.instance_id, &"guest_ids": [&"low_funds"], &"service_type": &"default", &"options": {&"quoted_price": 35}, }, &"low_funds", facility.instance_id, 10, -1, &"hardening.service.low" ) _expect(not low_result.is_success(), "service request exceeding the wallet balance is rejected") _expect(_runtime.facility_service.get_request(&"request_low_funds") == null, "rejected low-balance request leaves no Facility request") _expect(_runtime.facility_service.get_queue(facility.instance_id).is_empty(), "rejected low-balance request leaves no queue entry") _expect( _runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal") == available_before, "rejected low-balance request does not reserve stock" ) _expect(_runtime.inventory_service.get_reservations(true).size() == reservations_before, "rejected low-balance request creates no active Reservation") _expect(_runtime.economy_service.get_balance(&"GUEST_WALLET:low_funds") == 20, "rejected low-balance request leaves the wallet unchanged") _expect(_register_wallet(&"committed_guest", 50, &"hardening.wallet.commitment"), "commitment-test wallet registers") var first_result = _runtime.submit_command( &"create_service_request", { &"request_id": &"request_commitment_a", &"facility_id": facility.instance_id, &"guest_ids": [&"committed_guest"], &"service_type": &"default", &"options": {&"quoted_price": 30}, }, &"committed_guest", facility.instance_id, 10, -1, &"hardening.service.commitment.a" ) _expect(first_result.is_success(), "first affordable service commitment is accepted") var available_after_first: int = _runtime.inventory_service.available_quantity( facility.local_inventory_id, &"ingredient/basic_meal" ) var active_after_first: int = _runtime.inventory_service.get_reservations(true).size() var second_result = _runtime.submit_command( &"create_service_request", { &"request_id": &"request_commitment_b", &"facility_id": facility.instance_id, &"guest_ids": [&"committed_guest"], &"service_type": &"default", &"options": {&"quoted_price": 25}, }, &"committed_guest", facility.instance_id, 10, -1, &"hardening.service.commitment.b" ) _expect(not second_result.is_success(), "combined service commitments exceeding the wallet are rejected") var queue: Array = _runtime.facility_service.get_queue(facility.instance_id) _expect(queue.size() == 1 and queue[0].request_id == &"request_commitment_a", "over-commit rejection preserves only the first queue entry") _expect(_runtime.facility_service.get_request(&"request_commitment_b") == null, "over-commit rejection leaves no second request") _expect( _runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal") == available_after_first, "over-commit rejection does not reserve another meal" ) _expect(_runtime.inventory_service.get_reservations(true).size() == active_after_first, "over-commit rejection creates no additional Reservation") _expect(_runtime.economy_service.get_balance(&"GUEST_WALLET:committed_guest") == 50, "queued commitments do not debit the wallet prematurely") func _test_guest_moves_one_cell_per_minute() -> void: _reset_runtime(3312) var facility: FacilityInstance = _build_cafeteria() if facility == null: return var opened = _runtime.submit_command( &"set_facility_conditions", {&"facility_id": facility.instance_id, &"conditions": {&"active_staff_ids": [&"cook_projection", &"server_projection"]}}, &"manager", facility.instance_id, 10, -1, &"hardening.guest.open" ) _expect(opened.is_success() and facility.state == FacilityInstance.State.OPEN, "projected staffing opens the movement target") _expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"hardening.stock.path"), "movement target has a service input") var spawn = _runtime.submit_command( &"spawn_guest", { &"guest_id": &"path_guest", &"guest_type_id": &"traveler", &"gold": 100, &"options": { &"current_cell": Vector3i(8, 0, WorldService.LAYER_FRONT), &"needs": {&"hunger": 100, &"sleep": 0, &"hygiene": 0, &"medical": 0, &"leisure": 0, &"intimacy": 0}, &"planned_exit_at": _clock.absolute_minute + 240, }, }, &"world", &"path_guest", 10, -1, &"hardening.guest.spawn" ) _expect(spawn.is_success(), "distant Guest spawns for path progression") var guest: GuestRuntime = null for _minute in range(8): _clock.advance_one_minute() guest = _runtime.guest_service.get_guest(&"path_guest") if guest != null and guest.state == GuestRuntime.State.MOVING: break _expect(guest != null and guest.state == GuestRuntime.State.MOVING, "autonomous Guest selects the reachable cafeteria") if guest == null or guest.state != GuestRuntime.State.MOVING: return var target_cell: Vector3i = guest.path_handle.get(&"target_cell", guest.current_cell) var planned_path: Array = _runtime.path_service.find_path(guest.current_cell, target_cell) _expect(planned_path.size() >= 4, "movement regression uses a path longer than one minute") var first_cell := guest.current_cell _clock.advance_one_minute() guest = _runtime.guest_service.get_guest(&"path_guest") var second_cell := guest.current_cell _expect(_cell_distance(first_cell, second_cell) == 1, "Guest advances exactly one graph cell in one minute") _expect(second_cell != target_cell, "Guest does not teleport to a distant target in one minute") _clock.advance_one_minute() guest = _runtime.guest_service.get_guest(&"path_guest") _expect(_cell_distance(second_cell, guest.current_cell) == 1, "Guest continues along the path at one cell per minute") func _test_unreachable_staff_task_is_not_assigned() -> void: _reset_runtime(3313) var registered = _runtime.submit_command( &"register_staff", { &"staff_id": &"isolated_worker", &"role_id": &"worker", &"options": { &"state": StaffRuntime.State.AVAILABLE, &"current_cell": Vector3i(0, 0, WorldService.LAYER_FRONT), &"role_tags": [&"worker"], &"permitted_task_tags": [&"work"], &"skills": {&"operation": 50}, }, }, &"manager", &"isolated_worker", 10, -1, &"hardening.staff.unreachable.register" ) _expect(registered.is_success(), "available Staff registers for unreachable-task test") var created = _runtime.submit_command( &"create_staff_task", { &"task_id": &"unreachable_task", &"task_type": &"inspect", &"priority": 95, &"options": { &"target_cell": Vector3i(0, 5, WorldService.LAYER_FRONT), &"required_role_tags": [&"worker"], &"required_skill_min": {&"operation": 10}, &"task_tags": [&"work"], &"estimated_minutes": 5, }, }, &"manager", &"unreachable_task", 10, -1, &"hardening.staff.unreachable.task" ) _expect(created.is_success(), "unreachable StaffTask registers") _clock.advance_one_minute() var task: StaffTask = _runtime.staff_service.get_task(&"unreachable_task") var staff: StaffRuntime = _runtime.staff_service.get_staff(&"isolated_worker") _expect(task != null and task.state == StaffTask.State.OPEN, "unreachable StaffTask remains OPEN") _expect(task != null and task.assigned_staff_id.is_empty(), "unreachable StaffTask has no assigned Staff") _expect(staff != null and staff.current_task_id.is_empty(), "unreachable assignment does not reserve the Staff member") var unassigned: Array = _runtime.staff_service.last_dispatch_report.get(&"unassigned", []) _expect(not unassigned.is_empty() and unassigned[0].get(&"reason", &"") == &"NO_ELIGIBLE_STAFF", "dispatcher reports unreachable work as having no eligible Staff") _expect(_runtime.inventory_service.get_reservations(true).is_empty(), "unreachable work creates no inventory Reservation") func _test_shift_end_interrupts_and_fails_service() -> void: _reset_runtime(3314) var facility: FacilityInstance = _build_cafeteria(true) if facility == null: return for staff_id: StringName in [&"short_cook", &"short_server"]: var registered = _runtime.submit_command( &"register_staff", { &"staff_id": staff_id, &"role_id": &"worker", &"options": { &"state": StaffRuntime.State.OFF_DUTY, &"current_cell": Vector3i(0, 0, WorldService.LAYER_FRONT), }, }, &"manager", staff_id, 10, -1, StringName("hardening.shift.staff.%s" % staff_id) ) _expect(registered.is_success(), "short-shift Staff %s registers" % staff_id) var start_day: int = _clock.absolute_minute / 1_440 var start_minute: int = posmod(_clock.absolute_minute, 1_440) var end_minute: int = start_minute + 5 for staff_id: StringName in [&"short_cook", &"short_server"]: var shifted = _runtime.submit_command( &"create_staff_shift", { &"shift_id": StringName("shift_%s" % staff_id), &"staff_id": staff_id, &"template_id": &"FLEX", &"start_day": start_day, &"start_minute_of_day": start_minute, &"end_minute_of_day": end_minute, &"facility_id": facility.instance_id, }, &"manager", staff_id, 10, -1, StringName("hardening.shift.create.%s" % staff_id) ) _expect(shifted.is_success(), "short Shift for %s registers" % staff_id) _clock.advance_one_minute() _expect(facility.active_staff_ids == [&"short_cook", &"short_server"], "active short-shift Staff project into the Facility") _expect(facility.state == FacilityInstance.State.OPEN, "short-shift staffing opens the Facility") _expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"hardening.shift.stock"), "interruption scenario stocks one meal") _expect(_register_wallet(&"shift_guest", 100, &"hardening.shift.wallet"), "service payer registers for Shift interruption") var requested = _runtime.submit_command( &"create_service_request", { &"request_id": &"shift_service_request", &"facility_id": facility.instance_id, &"guest_ids": [&"shift_guest"], &"service_type": &"default", &"options": {&"quoted_price": 35}, }, &"shift_guest", facility.instance_id, 10, -1, &"hardening.shift.service" ) _expect(requested.is_success(), "service queues while short Shift is staffed") _clock.advance_one_minute() var active: Array = _runtime.facility_service.get_active_services(facility.instance_id) _expect(active.size() == 1, "service starts before the short Shift ends") if active.is_empty(): return var service: ServiceInstance = active[0] var service_id: StringName = service.service_id var shift_end_absolute: int = start_day * 1_440 + end_minute _clock.advance_minutes(maxi(shift_end_absolute - _clock.absolute_minute, 0)) _expect(facility.active_staff_ids.is_empty(), "Shift end removes all projected active Staff") _expect(facility.state == FacilityInstance.State.NO_STAFF, "Shift end moves the Facility to NO_STAFF") _clock.advance_one_minute() service = _runtime.facility_service.get_service(service_id) _expect(service != null and service.state == ServiceInstance.State.WAITING_FOR_STAFF, "active service waits for replacement Staff after Shift end") var grace_deadline: int = service.staff_grace_deadline if service != null else _clock.absolute_minute _expect(grace_deadline == _clock.absolute_minute + FacilityService.STAFF_REPLACEMENT_GRACE_MINUTES, "service records the deterministic Staff replacement grace deadline") _clock.advance_minutes(maxi(grace_deadline - _clock.absolute_minute, 0)) service = _runtime.facility_service.get_service(service_id) _expect(service != null and service.state == ServiceInstance.State.FAILED, "service fails when no replacement arrives before grace expiry") _expect(service != null and service.failure_reason == FacilityService.INTERRUPTION_STAFF_UNAVAILABLE, "failed service preserves the Staff-unavailable reason") _expect(_runtime.facility_service.get_active_services(facility.instance_id).is_empty(), "failed service leaves no active Facility slot") _expect(_runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal") == 1, "failed service releases its meal Reservation") func _test_corrupt_cross_domain_snapshots_are_atomic() -> void: _reset_runtime(3315) var registered = _runtime.submit_command( &"register_staff", { &"staff_id": &"snapshot_worker", &"role_id": &"worker", &"options": { &"state": StaffRuntime.State.AVAILABLE, &"current_cell": Vector3i(0, 0, WorldService.LAYER_FRONT), &"role_tags": [&"worker"], &"permitted_task_tags": [&"work"], }, }, &"manager", &"snapshot_worker", 10, -1, &"hardening.snapshot.staff" ) _expect(registered.is_success(), "snapshot Staff registers") _expect(_stock(&"staff.supplies", &"tool/snapshot_kit", 1, &"hardening.snapshot.stock"), "snapshot task tool is stocked") var created = _runtime.submit_command( &"create_staff_task", { &"task_id": &"snapshot_task", &"task_type": &"inspect", &"priority": 90, &"options": { &"target_cell": Vector3i(0, 0, WorldService.LAYER_FRONT), &"required_role_tags": [&"worker"], &"required_items": {&"tool/snapshot_kit": 1}, &"task_tags": [&"work"], &"estimated_minutes": 10, }, }, &"manager", &"snapshot_task", 10, -1, &"hardening.snapshot.task" ) _expect(created.is_success(), "snapshot StaffTask registers") _clock.advance_one_minute() var task: StaffTask = _runtime.staff_service.get_task(&"snapshot_task") _expect(task != null and task.state == StaffTask.State.WORKING and task.reservation_ids.size() == 1, "snapshot captures a working Task with an active Reservation") var valid_snapshot: Dictionary = _runtime.create_snapshot() _expect(_runtime.restore_snapshot(valid_snapshot), "valid hardening snapshot restores before corruption checks") var before_corrupt: Dictionary = _runtime.create_snapshot() var before_digest: String = SimulationDigest.compute(before_corrupt) var minute_corrupt: Dictionary = before_corrupt.duplicate(true) var runtime_data: Dictionary = minute_corrupt[&"systems"][&"runtime"] runtime_data[&"facilities"][&"current_minute"] = _clock.absolute_minute + 1 _expect(not _runtime.restore_snapshot(minute_corrupt), "snapshot with FacilityService minute diverging from the clock is rejected") _expect(_runtime.create_snapshot() == before_corrupt, "minute-mismatch restore rejection is fully atomic") _expect(SimulationDigest.compute(_runtime.create_snapshot()) == before_digest, "minute-mismatch rejection preserves the live digest") var reservation_corrupt: Dictionary = before_corrupt.duplicate(true) var task_entries: Array = reservation_corrupt[&"systems"][&"runtime"][&"staff"][&"tasks"] for raw_task: Dictionary in task_entries: if StringName(raw_task.get(&"task_id", "")) == &"snapshot_task": raw_task[&"reservation_ids"] = ["reservation_missing"] _expect(not _runtime.restore_snapshot(reservation_corrupt), "snapshot with a dangling StaffTask Reservation reference is rejected") _expect(_runtime.create_snapshot() == before_corrupt, "dangling-Reservation restore rejection is fully atomic") _expect(SimulationDigest.compute(_runtime.create_snapshot()) == before_digest, "dangling-Reservation rejection preserves the live digest") func _build_cafeteria(physical_kitchen_staff: bool = false) -> FacilityInstance: var build = _runtime.submit_command( &"build_facility", {&"definition_id": &"cafeteria", &"origin": Vector3i(-5, 0, WorldService.LAYER_FRONT)}, &"player", &"", 50, -1, StringName("hardening.build.%d" % _clock.absolute_minute) ) _expect(build.is_success(), "cafeteria construction starts") _clock.advance_minutes(180) var facilities: Array[Dictionary] = _runtime.construction_service.get_facilities() _expect(facilities.size() == 1, "cafeteria construction completes") if facilities.size() != 1: return null var facility_id := StringName(facilities[0].get(&"instance_id", "")) var facility: FacilityInstance = _runtime.facility_service.get_instance(facility_id) _expect(facility != null, "completed cafeteria registers with FacilityService") if facility != null: var kitchen_setup: Dictionary = load("res://tests/fixtures/required_kitchen_setup.gd").ensure(_runtime, _clock, facility.instance_id, physical_kitchen_staff) _expect(bool(kitchen_setup.get(&"ok", false)), "public paid kitchen link uses scenario-selected physical or logical attendance") return facility func _stock( owner_inventory_id: StringName, item_id: StringName, quantity: int, idempotency_key: StringName ) -> bool: var result = _runtime.submit_command( &"add_inventory_stack", { &"owner_inventory_id": owner_inventory_id, &"item_id": item_id, &"quantity": quantity, }, &"manager", owner_inventory_id, 10, -1, idempotency_key ) return result.is_success() func _register_wallet(guest_id: StringName, gold: int, idempotency_key: StringName) -> bool: var result = _runtime.submit_command( &"register_guest_wallet", {&"guest_id": guest_id, &"gold": gold}, &"manager", guest_id, 10, -1, idempotency_key ) return result.is_success() func _cell_distance(a: Vector3i, b: Vector3i) -> int: var delta := a - b return absi(delta.x) + absi(delta.y) + absi(delta.z) func _expect(condition: bool, label: String) -> void: _checks += 1 if condition: print(" ✓ %s" % label) return _failures += 1 push_error(" ✗ %s" % label)