154 lines
9.2 KiB
GDScript
154 lines
9.2 KiB
GDScript
extends SceneTree
|
|
|
|
var _checks := 0
|
|
var _failures := 0
|
|
var _serial := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
var fixture := _fixture(81423)
|
|
var runtime: Variant = fixture[&"runtime"]
|
|
var clock: Variant = fixture[&"clock"]
|
|
clock.advance_minutes(3)
|
|
var current: Dictionary = runtime.create_snapshot()
|
|
_expect(current[&"systems"][&"runtime"][&"runtime_schema_version"] == 9, "Runtime emits schema9 with elevator and explicit vertical-route aggregates")
|
|
var legacy := current.duplicate(true)
|
|
var data: Dictionary = legacy[&"systems"][&"runtime"]
|
|
data[&"runtime_schema_version"] = 7
|
|
data.erase(&"elevators")
|
|
data.erase(&"legacy_stairs")
|
|
data[&"path"][&"schema_version"] = 3
|
|
data[&"path"].erase(&"vertical_walk_blockers")
|
|
data[&"path"].erase(&"vertical_walk_requires_connector")
|
|
data[&"path"].erase(&"transit_actor_ids")
|
|
_expect(runtime.restore_snapshot(legacy), "genuine v7 without lifts migrates from Path3 into Runtime9")
|
|
var migrated: Dictionary = runtime.create_snapshot()
|
|
var expected := current.duplicate(true)
|
|
# Closing the old implicit-WALK topology bumps only the Path graph revision.
|
|
expected[&"systems"][&"runtime"][&"path"][&"graph_revision"] += 1
|
|
_expect(migrated == expected, "empty-lift migration preserves all state except the explicit topology revision")
|
|
var bad := current.duplicate(true)
|
|
bad[&"systems"][&"runtime"][&"runtime_schema_version"] = 7
|
|
_expect(_rejects(runtime, bad), "outer-only downgrade retaining elevator state is rejected atomically")
|
|
bad = current.duplicate(true)
|
|
bad[&"systems"][&"runtime"].erase(&"elevators")
|
|
_expect(_rejects(runtime, bad), "Runtime9 cannot omit elevator aggregate")
|
|
bad = current.duplicate(true)
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"current_minute"] -= 1
|
|
_expect(_rejects(runtime, bad), "elevator clock must match Runtime domain minute")
|
|
bad = current.duplicate(true)
|
|
bad[&"systems"][&"runtime"][&"path"][&"schema_version"] = 3
|
|
_expect(_rejects(runtime, bad), "Path3 relabel with retained vertical blocker metadata is rejected")
|
|
_expect(_command(runtime, clock, &"build_facility", {&"definition_id": &"guest_elevator", &"origin": Vector3i(0, 0, 0)}).is_success(), "real construction starts elevator module")
|
|
clock.advance_minutes(310)
|
|
_expect(_command(runtime, clock, &"hire_staff_candidate", {&"candidate_id": &"staff_akane"}).is_success(), "hire actual actor at entrance for ownership fixture")
|
|
var worker: StaffRuntime = runtime.staff_service.get_staff(&"staff_akane")
|
|
var actor_id := &"staff:staff_akane"
|
|
_expect(worker != null and worker.current_cell == Vector3i.ZERO, "hired actor is physically at elevator's ground landing")
|
|
var access: Array[StringName] = [&"staff"]
|
|
var queued: Dictionary = runtime.elevators.request_trip(actor_id, Vector3i.ZERO, Vector3i(0, 1, 0), clock.absolute_minute, access)
|
|
_expect(queued.get(&"ok", false), "queue real actor through transport ownership API: %s" % queued)
|
|
if not queued.get(&"ok", false):
|
|
for key: StringName in [&"runtime", &"clock", &"state"]: fixture[key].free()
|
|
quit(1)
|
|
return
|
|
clock.advance_minutes(1)
|
|
var trip: Dictionary = runtime.elevators.get_actor_trip(actor_id)
|
|
_expect(trip.get(&"state") == PathService.ELEVATOR_REQUEST_BOARDED, "save fixture owns a physical cabin seat")
|
|
current = runtime.create_snapshot()
|
|
_expect(runtime.restore_snapshot(current), "Runtime9 restores actual cabin actor and projected origin crosslinks")
|
|
bad = current.duplicate(true)
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"trips"][0][&"actor_id"] = &"staff:missing_person"
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"shafts"][0][&"boarded"][0] = &"staff:missing_person"
|
|
for unit: Dictionary in bad[&"systems"][&"runtime"][&"path"][&"active_elevator_units"]:
|
|
if unit[&"unit_id"] == actor_id: unit[&"unit_id"] = &"staff:missing_person"
|
|
_expect(_rejects(runtime, bad), "internally consistent seat and Path owner cannot refer to nonexistent Runtime actor")
|
|
bad = current.duplicate(true)
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"shafts"][0][&"config"][&"module_ids"][0] = &"missing_module"
|
|
_expect(_rejects(runtime, bad), "elevator cabin cannot substitute a module absent from real construction")
|
|
bad = current.duplicate(true)
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"shafts"][0][&"boarded"] = []
|
|
_expect(_rejects(runtime, bad), "active Path rider cannot lose its cabin seat")
|
|
bad = current.duplicate(true)
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"trips"][0][&"from"] = Vector3i(0, 1, 0)
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"trips"][0][&"to"] = Vector3i.ZERO
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"shafts"][0][&"boarding_floor"] = 1
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"shafts"][0][&"target_floor"] = 0
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"shafts"][0][&"current_floor"] = 1
|
|
bad[&"systems"][&"runtime"][&"elevators"][&"shafts"][0][&"phase_origin_floor"] = 1
|
|
_expect(_rejects(runtime, bad), "valid reverse cabin route cannot move actor's saved logical origin")
|
|
var shaft_id: StringName = runtime.elevators.get_report()[&"shafts"][0][&"shaft_id"]
|
|
_expect(_command(runtime, clock, &"set_elevator_operation", {&"shaft_id": shaft_id, &"operator_open": false}).is_success(), "player closes running elevator through public command")
|
|
current = runtime.create_snapshot()
|
|
_expect(runtime.restore_snapshot(current) and not runtime.elevators.get_report()[&"shafts"][0][&"operator_open"], "closed operator policy survives Runtime restore")
|
|
for key: StringName in [&"runtime", &"clock", &"state"]: fixture[key].free()
|
|
_test_attendance_migration()
|
|
print("Transport save: %d checks, %d failures" % [_checks, _failures])
|
|
quit(0 if _failures == 0 else 1)
|
|
|
|
|
|
func _test_attendance_migration() -> void:
|
|
var fixture := _fixture(81424)
|
|
var runtime: Variant = fixture[&"runtime"]
|
|
var clock: Variant = fixture[&"clock"]
|
|
_expect(_command(runtime, clock, &"build_facility", {&"definition_id": &"reception", &"origin": Vector3i(2, 0, 0)}).is_success(), "attendance fixture builds a real workplace")
|
|
clock.advance_minutes(180)
|
|
var facility: FacilityInstance = runtime.facility_service.get_instances()[0]
|
|
_expect(_command(runtime, clock, &"hire_staff_candidate", {&"candidate_id": &"staff_akane"}).is_success(), "attendance fixture hires actual receptionist")
|
|
_expect(_command(runtime, clock, &"set_staff_work_assignment", {&"staff_id": &"staff_akane", &"facility_id": facility.instance_id, &"template_id": &"DAY"}).is_success(), "attendance fixture assigns a real shift")
|
|
var worker: StaffRuntime = runtime.staff_service.get_staff(&"staff_akane")
|
|
_expect(not StaffCommuteService.is_at_facility(worker, facility), "employee has not yet physically walked to reception")
|
|
# Reproduce the old logical-attendance rule without changing position, route,
|
|
# income or inventory. Facility projection is rebuilt by its public service.
|
|
worker.state = StaffRuntime.State.AVAILABLE
|
|
runtime.facility_service.set_operational_status(facility.instance_id, {&"active_staff_ids": [&"staff_akane"]}, clock.absolute_minute)
|
|
var logical: Dictionary = runtime.create_snapshot()
|
|
_expect(_rejects(runtime, logical), "Runtime9 rejects an AVAILABLE employee counted at a distant workplace")
|
|
var legacy := logical.duplicate(true)
|
|
var data: Dictionary = legacy[&"systems"][&"runtime"]
|
|
data[&"runtime_schema_version"] = 7
|
|
data.erase(&"elevators")
|
|
data.erase(&"legacy_stairs")
|
|
data[&"path"][&"schema_version"] = 3
|
|
data[&"path"].erase(&"vertical_walk_blockers")
|
|
data[&"path"].erase(&"vertical_walk_requires_connector")
|
|
data[&"path"].erase(&"transit_actor_ids")
|
|
_expect(runtime.restore_snapshot(legacy), "genuine v7 logical attendance migrates into physical attendance")
|
|
facility = runtime.facility_service.get_instance(facility.instance_id)
|
|
worker = runtime.staff_service.get_staff(&"staff_akane")
|
|
_expect(facility.active_staff_ids.is_empty() and worker.state == StaffRuntime.State.COMMUTING and worker.current_cell == Vector3i.ZERO, "migration removes phantom workplace capacity and starts commute without teleporting")
|
|
_expect(runtime.restore_snapshot(runtime.create_snapshot()), "migrated attendance is a valid current Runtime snapshot")
|
|
for key: StringName in [&"runtime", &"clock", &"state"]: fixture[key].free()
|
|
|
|
|
|
func _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(runtime.initialize(clock, state, seed)[&"valid"], "transport save fixture initializes")
|
|
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
|
clock.set_paused(true)
|
|
return {&"clock": clock, &"state": state, &"runtime": runtime}
|
|
|
|
|
|
func _command(runtime: Variant, clock: Variant, kind: StringName, payload: Dictionary) -> CommandResult:
|
|
_serial += 1
|
|
return runtime.submit_command(kind, payload, &"player", &"", 0, clock.absolute_minute, StringName("transport.save.%d" % _serial))
|
|
|
|
|
|
func _rejects(runtime: Variant, candidate: Dictionary) -> bool:
|
|
var before: Dictionary = runtime.create_snapshot()
|
|
return not runtime.restore_snapshot(candidate) and runtime.create_snapshot() == before
|
|
|
|
|
|
func _expect(condition: bool, label: String) -> void:
|
|
_checks += 1
|
|
if not condition: _failures += 1
|
|
print("%s: %s" % ["PASS" if condition else "FAIL", label])
|