Files
TheTree_mock/tests/transport/test_transport_boundaries.gd
T

176 lines
12 KiB
GDScript

extends SceneTree
var _checks := 0
var _failures := 0
var _runtime: Variant
var _clock: Variant
var _state: Variant
var _serial := 0
const STAFF := &"staff_akane"
const ACTOR := &"staff:staff_akane"
class LateFailElevators extends ElevatorService:
var fail_next_sync := false
func synchronize(configurations: Array, minute: int) -> Dictionary:
var result := super.synchronize(configurations, minute)
if fail_next_sync and result[&"ok"]:
fail_next_sync = false
last_error = "Injected failure after actual transport mutation."
return {&"ok": false, &"reason": &"INJECTED_LATE_FAILURE"}
return result
func _initialize() -> void:
call_deferred("_run")
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, 81662)[&"valid"], "boundary fixture initializes")
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_clock.set_paused(true)
_expect(_command(&"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i.ZERO}).is_success(), "build lower lift from real starting budget")
_clock.advance_minutes(310)
_expect(_car().get(&"stops", []).size() == 2, "completed lower lift has two real stops")
_expect(_command(&"hire_staff_candidate", {&"candidate_id": STAFF}).is_success(), "hire actual ground-floor actor")
var idle: Dictionary = _runtime.create_snapshot()
var lower_id: StringName = _car()[&"module_ids"][0]
var extension := _command(&"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i(0, 2, 0)})
_expect(extension.is_success(), "idle shaft accepts contiguous extension")
var extension_id: StringName
for job: ConstructionJob in _runtime.construction_service.get_jobs():
if job.origin == Vector3i(0, 2, 0): extension_id = job.job_id
var freeze: Dictionary = _runtime.advance_staff_commute(STAFF, Vector3i(0, 1, 0), _clock.absolute_minute)
_expect(freeze.get(&"reason") == &"ELEVATOR_WAIT" and _runtime.elevators.get_report()[&"trips"].is_empty(), "in-progress extension freezes new boarding at Runtime movement boundary")
_expect(_runtime.staff_service.get_staff(STAFF).current_cell == Vector3i.ZERO, "extension freeze never walks actor vertically")
_clock.advance_minutes(5)
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "in-progress extension and frozen empty car survive save restore")
_expect(_command(&"cancel_construction", {&"job_id": extension_id}).is_success(), "public cancellation releases extension freeze")
var resumed: Dictionary = _runtime.advance_staff_commute(STAFF, Vector3i(0, 1, 0), _clock.absolute_minute)
_expect(resumed.get(&"reason") == &"ELEVATOR_WAIT" and _runtime.elevators.get_actor_trip(ACTOR).get(&"state") == PathService.ELEVATOR_REQUEST_QUEUED, "after cancellation the same real actor queues for a cabin")
_check_busy_commands(lower_id, "queued")
_check_operations_rollback("queued")
_clock.advance_minutes(1)
_expect(_runtime.elevators.get_actor_trip(ACTOR).get(&"state") == PathService.ELEVATOR_REQUEST_BOARDED, "queued actor boards through next real minute")
_check_busy_commands(lower_id, "boarded")
_check_operations_rollback("boarded")
var reassigned := _command(&"set_staff_work_assignment", {&"staff_id": STAFF, &"facility_id": &"", &"template_id": &"DAY"})
_expect(reassigned.is_success() and _runtime.elevators.get_actor_trip(ACTOR).get(&"state") == PathService.ELEVATOR_REQUEST_BOARDED, "unassigning boarded worker preserves cabin ride until safe arrival")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "reassigned boarded worker remains a valid Runtime save")
_clock.advance_minutes(20)
_expect(_runtime.elevators.get_actor_trip(ACTOR).is_empty() and _runtime.staff_service.get_staff(STAFF).current_cell == Vector3i(0, 1, 0), "unassigned worker still physically finishes the existing ride")
_expect(_runtime.restore_snapshot(idle), "restore original empty shaft for topology completion cases")
var shaft_id: StringName = _car()[&"shaft_id"]
_expect(_command(&"set_elevator_operation", {&"shaft_id": shaft_id, &"operator_open": false}).is_success(), "player stops idle shaft before extension")
_expect(_command(&"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i(0, 2, 0)}).is_success(), "stopped idle shaft can extend")
_clock.advance_minutes(310)
_expect(_car()[&"stops"].size() == 4 and _car()[&"module_ids"].size() == 2 and not _car()[&"operator_open"], "successful extension keeps one cabin and persists player-stopped policy")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "completed extended shaft has exact save crosslinks")
var upper_id: StringName = _car()[&"module_ids"][1]
_expect(_command(&"demolish_facility", {&"instance_id": upper_id}).is_success(), "empty upper module demolition succeeds")
_expect(_car()[&"stops"].size() == 2 and _car()[&"module_ids"] == [lower_id] and not _car()[&"operator_open"], "upper demolition removes its stops without re-enabling lower car")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "partially demolished shaft remains saveable")
var injected := LateFailElevators.new()
injected.configure(_runtime.path_service)
_expect(injected.restore_snapshot(_runtime.elevators.create_snapshot()), "late-failure injector starts from exact live cabin state")
_runtime.elevators = injected
injected.fail_next_sync = true
var before := _participants()
var failure := _command(&"demolish_facility", {&"instance_id": lower_id})
_expect(not failure.is_success() and failure.details.get(&"rolled_back", false), "post-demolition transport failure rolls back the public transaction")
_expect(_participants() == before, "late rollback restores world, money, projections, construction, power and physical car exactly")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "late demolition rollback produces a valid canonical save")
_expect(_command(&"demolish_facility", {&"instance_id": lower_id}).is_success(), "empty final shaft demolition succeeds on retry")
_expect(_runtime.elevators.get_report()[&"shafts"].is_empty() and _runtime.path_service.get_vertical_walk_blockers().is_empty(), "final demolition clears cabin and all shaft vertical blockers")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "fully demolished world remains exactly saveable")
_test_shift_end(idle)
_runtime.free()
_clock.free()
_state.free()
print("Transport boundaries: %d checks, %d failures" % [_checks, _failures])
quit(0 if _failures == 0 else 1)
func _test_shift_end(idle: Dictionary) -> void:
_expect(_runtime.restore_snapshot(idle), "shift-boundary fixture restores ordinary lower elevator")
_expect(_command(&"build_facility", {&"definition_id": &"cafeteria", &"origin": Vector3i(2, 1, 0)}).is_success(), "real upper-floor cafeteria accepts commuting staff")
_clock.advance_minutes(190)
_clock.advance_minutes(1078 - _clock.absolute_minute)
var cafeteria: StringName
for facility: FacilityInstance in _runtime.facility_service.get_instances():
if facility.definition_id == &"cafeteria": cafeteria = facility.instance_id
_expect(not cafeteria.is_empty() and _command(&"hire_staff_candidate", {&"candidate_id": &"staff_chika"}).is_success(), "hire real cook just before DAY shift ends")
var assignment := {&"staff_id": &"staff_chika", &"facility_id": cafeteria, &"template_id": &"DAY"}
_expect(_command(&"set_staff_work_assignment", assignment).is_success(), "cook receives real persistent DAY work plan")
var before_commute: Dictionary = _runtime.create_snapshot()
_clock.advance_minutes(1)
var actor_id := &"staff:staff_chika"
var queued: Dictionary = _runtime.elevators.get_actor_trip(actor_id)
_expect(queued.get(&"state") == PathService.ELEVATOR_REQUEST_QUEUED, "17:59 cook naturally queues during actual commute")
var unchanged := _command(&"set_staff_work_assignment", assignment)
_expect(unchanged.is_success() and unchanged.details.get(&"reason") == &"ASSIGNMENT_UNCHANGED" and _runtime.elevators.get_actor_trip(actor_id) == queued, "unchanged work plan preserves the same FIFO request")
_clock.advance_minutes(1)
var worker: StaffRuntime = _runtime.staff_service.get_staff(&"staff_chika")
_expect(worker.state == StaffRuntime.State.OFF_DUTY and _runtime.elevators.get_actor_trip(actor_id).is_empty(), "18:00 shift end cancels stale commute before the available cabin can board")
_expect(worker.current_cell == Vector3i.ZERO and _runtime.path_service.get_active_elevator_request(actor_id).is_empty(), "expired commute leaves cook at original landing without orphaned Path ownership")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "cancelled shift-boundary wait is a valid Runtime save")
_expect(_runtime.restore_snapshot(before_commute), "restore 17:58 for already-boarded shift-boundary case")
_runtime.advance_staff_commute(&"staff_chika", Vector3i(0, 1, 0), _clock.absolute_minute)
_clock.advance_minutes(1)
_expect(_runtime.elevators.get_actor_trip(actor_id).get(&"state") == PathService.ELEVATOR_REQUEST_BOARDED, "cook queued a minute earlier is already aboard before shift end")
_clock.advance_minutes(1)
worker = _runtime.staff_service.get_staff(&"staff_chika")
_expect(worker.state == StaffRuntime.State.OFF_DUTY and _runtime.elevators.get_actor_trip(actor_id).get(&"state") == PathService.ELEVATOR_REQUEST_BOARDED, "shift end retains already-boarded rider until physical alighting")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "off-duty boarded employee remains a valid save")
_clock.advance_minutes(20)
worker = _runtime.staff_service.get_staff(&"staff_chika")
_expect(worker.current_cell == Vector3i(0, 1, 0) and _runtime.elevators.get_actor_trip(actor_id).is_empty(), "off-duty boarded cook completes exactly one physical arrival")
func _check_busy_commands(module_id: StringName, label: String) -> void:
var before := _participants()
var build := _command(&"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i(0, 2, 0)})
_expect(not build.is_success() and build.details.get(&"reason") == &"ELEVATOR_BUSY" and _participants() == before, "%s passengers reject extension before any participant changes" % label)
var demolition := _command(&"demolish_facility", {&"instance_id": module_id})
_expect(not demolition.is_success() and demolition.details.get(&"reason") == &"ELEVATOR_BUSY" and _participants() == before, "%s passengers reject demolition before any participant changes" % label)
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "%s rejected topology commands preserve a valid save" % label)
func _check_operations_rollback(label: String) -> void:
var before: Dictionary = _runtime.create_snapshot()
var token: Dictionary = _runtime._capture_operations_external_state()
_expect(not token.is_empty(), "%s ride can participate in Operations snapshot boundary" % label)
var stopped := _command(&"set_elevator_operation", {&"shaft_id": _car()[&"shaft_id"], &"operator_open": false})
_expect(stopped.is_success() and _runtime.elevators.get_report()[&"trips"].is_empty(), "%s rollback fixture actually mutates car and Path ownership through public command" % label)
_expect(_runtime._restore_operations_external_state(token), "%s Operations rollback restores external participants" % label)
_expect(_runtime.create_snapshot() == before, "%s Operations rollback restores full Runtime including command IDs and event state exactly" % label)
func _participants() -> Dictionary:
var snapshot: Dictionary = _runtime._capture_infrastructure_demolition_state()
snapshot.erase(&"domain_events")
snapshot.erase(&"id_generator")
return snapshot
func _car() -> Dictionary:
var shafts: Array = _runtime.elevators.get_report()[&"shafts"]
return shafts[0] if not shafts.is_empty() else {}
func _command(kind: StringName, payload: Dictionary) -> CommandResult:
_serial += 1
return _runtime.submit_command(kind, payload, &"player", &"", 0, _clock.absolute_minute, StringName("transport.boundary.%d" % _serial))
func _expect(condition: bool, label: String) -> void:
_checks += 1
if not condition: _failures += 1
print("%s: %s" % ["PASS" if condition else "FAIL", label])