Connect physical elevators and staff commutes to construction, saves, and cutaway UI

This commit is contained in:
Takeshi
2026-09-09 12:58:40 +09:00
parent 4d72de8973
commit 0f12c748e9
46 changed files with 3267 additions and 165 deletions
@@ -380,6 +380,9 @@ func _test_v3_migration_and_v4_tamper_guards() -> void:
var v3_runtime: Dictionary = v3[&"systems"][&"runtime"]
v3_runtime[&"runtime_schema_version"] = 3
v3_runtime.erase(&"kitchen_production")
v3_runtime.erase(&"elevators")
v3_runtime[&"path"][&"schema_version"] = 3
v3_runtime[&"path"].erase(&"vertical_walk_blockers")
v3_runtime.erase(&"operations")
v3_runtime.erase(&"catalog_manifest")
v3_runtime.erase(&"progression")
@@ -396,10 +399,10 @@ func _test_v3_migration_and_v4_tamper_guards() -> void:
_expect(int(_runtime.progression_service.metrics[&"guest_lifetime"]) == 2, "v3 migration derives lifetime Guests from active plus persistent records")
_expect(_runtime.get_mvp_guest_definition_ids().size() == 32, "v3 migration registers the current 32 catalog definitions")
var migrated: Dictionary = _runtime.create_snapshot()
_expect(int(migrated[&"systems"][&"runtime"][&"runtime_schema_version"]) == 7, "v3 migration emits canonical Runtime schema v7")
_expect(int(migrated[&"systems"][&"runtime"][&"runtime_schema_version"]) == 8, "v3 migration emits canonical Runtime schema v8")
var digest := SimulationDigest.compute(migrated)
_expect(_runtime.restore_snapshot(migrated), "canonical v7 snapshot restores")
_expect(SimulationDigest.compute(_runtime.create_snapshot()) == digest, "canonical v7 restore is exact")
_expect(_runtime.restore_snapshot(migrated), "canonical v8 snapshot restores")
_expect(SimulationDigest.compute(_runtime.create_snapshot()) == digest, "canonical v8 restore is exact")
var progressed_legacy: Dictionary = v3.duplicate(true)
progressed_legacy[&"game_state"][&"world_tree_rank"] = 2
+31 -22
View File
@@ -1,7 +1,7 @@
extends SceneTree
const FOURTEEN_DAYS_MINUTES := 14 * SimulationKernel.MINUTES_PER_DAY
const RUNTIME_SNAPSHOT_VERSION := 7
const RUNTIME_SNAPSHOT_VERSION := 8
var _checks := 0
var _failures := 0
@@ -15,7 +15,7 @@ func _run() -> void:
print("MVP: production Operations acceptance")
_test_waiting_choice_and_command_exactly_once()
_test_twenty_four_hour_actual_tick_smoke()
_test_full_partial_payroll_bankruptcy_and_v7_tamper()
_test_full_partial_payroll_bankruptcy_and_v8_tamper()
_test_fourteen_day_boundary_snapshot_determinism()
await process_frame
if _failures == 0:
@@ -39,7 +39,7 @@ func _new_fixture(seed: int, enable_operations := true) -> Dictionary:
int(initial_snapshot[&"systems"][&"runtime"].get(
&"runtime_schema_version", -1
)) == RUNTIME_SNAPSHOT_VERSION,
"fixture emits Runtime snapshot schema v7"
"fixture emits Runtime snapshot schema v8"
)
if enable_operations:
var enabled: Dictionary = runtime.enable_production_operations()
@@ -107,6 +107,15 @@ func _align_operations_fixture_clocks(runtime: Variant, minute: int) -> void:
runtime.defense_service.current_minute = minute
runtime.construction_service.current_minute = minute
runtime.excavation_service.current_minute = minute
# This accelerated oracle owns no cars or rides; update its empty transport
# aggregate through the public snapshot validator instead of a private clock.
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 Operations oracle 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 _operations_slice_is_healthy(fixture: Dictionary) -> bool:
@@ -208,7 +217,7 @@ func _test_waiting_choice_and_command_exactly_once() -> void:
var command_snapshot: Dictionary = runtime.create_snapshot()
_expect(
runtime.restore_snapshot(command_snapshot),
"command history survives an exact v7 snapshot round trip"
"command history survives an exact v8 snapshot round trip"
)
var restored_retry: CommandResult = runtime.submit_command(
&"change_resources",
@@ -248,12 +257,12 @@ func _test_twenty_four_hour_actual_tick_smoke() -> void:
_expect(
runtime.restore_snapshot(snapshot)
and runtime.latest_digest == SimulationDigest.compute(snapshot),
"24-hour actual-tick v7 snapshot round trip is exact"
"24-hour actual-tick v8 snapshot round trip is exact"
)
_free_fixture(fixture)
func _test_full_partial_payroll_bankruptcy_and_v7_tamper() -> void:
func _test_full_partial_payroll_bankruptcy_and_v8_tamper() -> void:
var fixture := _new_fixture(73_002, false)
var runtime: Variant = fixture[&"runtime"]
var clock: Variant = fixture[&"clock"]
@@ -340,7 +349,7 @@ func _test_full_partial_payroll_bankruptcy_and_v7_tamper() -> void:
var terminal_digest := SimulationDigest.compute(terminal_snapshot)
_expect(
runtime.restore_snapshot(terminal_snapshot),
"BANKRUPT terminal v7 snapshot restores without clearing terminal state"
"BANKRUPT terminal v8 snapshot restores without clearing terminal state"
)
clock.advance_minutes(180)
_expect(
@@ -353,32 +362,32 @@ func _test_full_partial_payroll_bankruptcy_and_v7_tamper() -> void:
"BANKRUPT cannot be acknowledged away"
)
_assert_v7_tamper_rejected(
_assert_v8_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"operations"][&"personal_action_points"] = 99,
"v7 rejects an out-of-range Operations action-point total"
"v8 rejects an out-of-range Operations action-point total"
)
_assert_v7_tamper_rejected(
_assert_v8_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"next_command_id"] = int(
tampered[&"systems"][&"runtime"][&"next_command_id"]
) + 1,
"v7 rejects command high-water gaps"
"v8 rejects command high-water gaps"
)
_assert_v7_tamper_rejected(
_assert_v8_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
var accepted: Array = tampered[&"systems"][&"runtime"][&"command_queue"][&"accepted"]
if not accepted.is_empty():
accepted[0][&"idempotency_key"] = &"tampered.accepted.key",
"v7 rejects accepted-command/idempotency cross-link tampering"
"v8 rejects accepted-command/idempotency cross-link tampering"
)
_assert_v7_tamper_rejected(
_assert_v8_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
@@ -386,26 +395,26 @@ func _test_full_partial_payroll_bankruptcy_and_v7_tamper() -> void:
summaries[0][&"category_totals"][&"wages"] = int(
summaries[0][&"category_totals"][&"wages"]
) + 1,
"v7 rejects DaySummary/payroll wage reconciliation tampering"
"v8 rejects DaySummary/payroll wage reconciliation tampering"
)
_assert_v7_tamper_rejected(
_assert_v8_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"unexpected_v7_field"] = true,
"v7 rejects unknown Runtime fields"
tampered[&"systems"][&"runtime"][&"unexpected_v8_field"] = true,
"v8 rejects unknown Runtime fields"
)
_assert_v7_tamper_rejected(
_assert_v8_tamper_rejected(
runtime,
terminal_snapshot,
func(tampered: Dictionary) -> void:
tampered[&"systems"][&"runtime"][&"operations"][&"coordinator"][&"unexpected_field"] = true,
"v7 rejects unknown Coordinator fields"
"v8 rejects unknown Coordinator fields"
)
_free_fixture(fixture)
func _assert_v7_tamper_rejected(
func _assert_v8_tamper_rejected(
runtime: Variant,
canonical: Dictionary,
mutator: Callable,
@@ -454,7 +463,7 @@ func _test_fourteen_day_boundary_snapshot_determinism() -> void:
var resumed_clock: Variant = resumed[&"clock"]
var resumed_runtime: Variant = resumed[&"runtime"]
var restored: bool = resumed_runtime.restore_snapshot(midpoint)
_expect(restored, "fresh Runtime restores the enabled v7 midpoint snapshot")
_expect(restored, "fresh Runtime restores the enabled v8 midpoint snapshot")
var resumed_advanced := 0
if restored:
resumed_advanced = _advance_operations_boundary_slice(
@@ -57,7 +57,7 @@ func _test_disabled_compatibility_and_enable() -> void:
_expect(not disabled[&"enabled"], "Operations remain disabled for existing Runtime tests")
var disabled_snapshot: Dictionary = _runtime.create_snapshot()
var disabled_runtime: Dictionary = disabled_snapshot[&"systems"][&"runtime"]
_expect(int(disabled_runtime[&"runtime_schema_version"]) == 7, "Runtime snapshot advances to schema v7")
_expect(int(disabled_runtime[&"runtime_schema_version"]) == 8, "Runtime snapshot advances to schema v8")
_expect(
disabled_runtime[&"operations"] == {
&"wrapper_version": 1,
@@ -361,6 +361,9 @@ func _test_daily_payroll_financials_and_crosslink_tamper() -> void:
var downgraded: Dictionary = canonical.duplicate(true)
downgraded[&"systems"][&"runtime"][&"runtime_schema_version"] = 4
downgraded[&"systems"][&"runtime"].erase(&"kitchen_production")
downgraded[&"systems"][&"runtime"].erase(&"elevators")
downgraded[&"systems"][&"runtime"][&"path"][&"schema_version"] = 3
downgraded[&"systems"][&"runtime"][&"path"].erase(&"vertical_walk_blockers")
_expect(
_rejects_atomically(downgraded),
"relabelled enabled v5 cannot discard Operations history through v4 migration"
@@ -93,7 +93,7 @@ func _run() -> void:
_test_corridors_and_primary_world_coexistence()
_test_staff_door_access_and_stable_ids()
_test_project_unproject_replay_and_defensive_queries()
_test_vertical_topology_rejects_unchanged()
_test_vertical_topology_projects_without_walk_bypass()
_test_definition_and_instance_crosslink_validation()
_test_project_and_unproject_failures_roll_back_exactly()
_test_callback_reentry_tamper_and_quarantine_recovery()
@@ -202,21 +202,17 @@ func _test_project_unproject_replay_and_defensive_queries() -> void:
_expect(context.projector.projection_revision == 2, "project and unproject each advance exactly once")
func _test_vertical_topology_rejects_unchanged() -> void:
func _test_vertical_topology_projects_without_walk_bypass() -> void:
var context := _context([Vector3i(0, 0, PathType.FRONT)])
var before_projector: Dictionary = context.projector.create_snapshot()
var before_source: Dictionary = context.source.create_snapshot()
var before_adapter: Dictionary = context.adapter.create_snapshot()
var before_path: Dictionary = context.path.create_snapshot()
var guest_instance := _instance(GUEST_ELEVATOR, &"facility_00000005", Vector3i(4, 4, PathType.FRONT), 40, &"construction_00000005")
var guest = context.projector.project_facility(&"guest_elevator", GUEST_ELEVATOR, guest_instance)
_expect(guest.code == CommandResultType.Code.VALIDATION_FAILED and guest.details[&"reason"] == context.projector.REASON_UNSUPPORTED_VERTICAL_TOPOLOGY, "guest elevator explicitly reports unsupported vertical topology")
_expect(guest.is_success(), "guest elevator projects completed shaft topology")
var service_instance := _instance(SERVICE_ELEVATOR, &"facility_00000006", Vector3i(8, 4, PathType.BACK), 41, &"construction_00000006")
var service = context.projector.project_facility(&"service_elevator", SERVICE_ELEVATOR, service_instance)
_expect(service.code == CommandResultType.Code.VALIDATION_FAILED and service.details[&"reason"] == context.projector.REASON_UNSUPPORTED_VERTICAL_TOPOLOGY, "service elevator explicitly reports unsupported vertical topology")
_expect(context.projector.create_snapshot() == before_projector, "unsupported vertical facilities leave projector state unchanged")
_expect(context.source.create_snapshot() == before_source and context.adapter.create_snapshot() == before_adapter and context.path.create_snapshot() == before_path, "unsupported vertical facilities leave all authoritative dependencies unchanged")
_expect(context.projector.get_processed_request_ids().is_empty(), "unsupported vertical rejection does not consume request IDs")
_expect(service.is_success(), "service elevator projects completed shaft topology")
_expect(context.path.find_path(Vector3i(4, 4, 0), Vector3i(4, 5, 0)).is_empty(), "guest shaft cannot be climbed without its separately owned cabin connector")
_expect(context.path.find_path(Vector3i(8, 4, 1), Vector3i(8, 5, 1)).is_empty(), "service shaft cannot be climbed by ordinary walk edges")
_expect(context.path.get_vertical_walk_blockers().size() == 2 and context.projector.validate_invariants()[&"ok"], "both completed elevator footprints retain authoritative owned shaft restrictions")
func _test_definition_and_instance_crosslink_validation() -> void:
+4 -3
View File
@@ -384,13 +384,13 @@ func _test_snapshot_roundtrip_and_atomic_rejection() -> void:
)
original.record_actor_progress(&"off_graph_actor", Vector3i(4, 4, 0), 12)
var snapshot := original.create_snapshot()
_expect(int(snapshot[&"schema_version"]) == 3, "request-safe traffic snapshot uses schema version 3")
_expect(int(snapshot[&"schema_version"]) == 4, "shaft-safe traffic snapshot uses schema version 4")
_expect(snapshot.has(&"cell_traffic") and snapshot.has(&"actor_cells"), "snapshot persists traffic and occupancy projection")
_expect(not snapshot.has(&"graph") and not snapshot.has(&"cache"), "snapshot still excludes derived graph and cache")
var restored = PathServiceScript.new()
_expect(restored.restore_snapshot(snapshot), "v3 snapshot restores before authoritative world graph")
_expect(restored.restore_snapshot(snapshot), "v4 snapshot restores before authoritative world graph")
_expect(restored.rebuild_graph([from_cell, to_cell]), "restored traffic validates against authoritative cells")
_expect(restored.create_snapshot() == snapshot, "v3 snapshot roundtrip is exact")
_expect(restored.create_snapshot() == snapshot, "v4 snapshot roundtrip is exact")
_expect(restored.get_current_density(from_cell) == 1, "restored density is derived consistently from actors")
_expect(restored.elevator_queue_size(&"lift_save") == 1, "restored elevator retains its request queue")
@@ -407,6 +407,7 @@ func _test_snapshot_roundtrip_and_atomic_rejection() -> void:
v2_snapshot[&"schema_version"] = 2
v2_snapshot.erase(&"known_elevator_request_ids")
v2_snapshot.erase(&"active_elevator_units")
v2_snapshot.erase(&"vertical_walk_blockers")
var v2_restored = PathServiceScript.new()
_expect(v2_restored.restore_snapshot(v2_snapshot), "schema v2 snapshot remains readable")
_expect(v2_restored.rebuild_graph([from_cell, to_cell]), "schema v2 traffic validates against authoritative cells")
@@ -1,6 +1,6 @@
extends SceneTree
const RUNTIME_SNAPSHOT_VERSION := 7
const RUNTIME_SNAPSHOT_VERSION := 8
const INFRASTRUCTURE_SNAPSHOT_KEYS: Array[StringName] = [
&"excavation",
&"spatial_subslots",
@@ -59,7 +59,7 @@ func _run() -> void:
_test_completed_proxy_projection()
_test_proxy_completion_raw_signal_commit_order()
_test_proxy_completion_projection_failure_is_atomic()
_test_vertical_preflight_rejection()
_test_completed_elevator_topology()
_test_demolition_projection_atomicity()
_test_current_snapshot_strictness()
_test_v5_proxy_migration_determinism()
@@ -113,14 +113,14 @@ func _test_runtime_surface(fixture: Dictionary) -> bool:
var snapshot: Dictionary = runtime.create_snapshot()
var runtime_data: Dictionary = snapshot[&"systems"][&"runtime"]
var current_version := int(runtime_data.get(&"runtime_schema_version", -1)) == RUNTIME_SNAPSHOT_VERSION
_expect(current_version, "Runtime emits schema v7 with infrastructure state")
_expect(current_version, "Runtime emits schema v8 with infrastructure state")
ready = ready and current_version
for key: StringName in INFRASTRUCTURE_SNAPSHOT_KEYS:
var valid_dictionary := runtime_data.get(key, null) is Dictionary
_expect(valid_dictionary, "current Runtime stores strict %s state" % key)
ready = ready and valid_dictionary
if not ready:
print(" PENDING: Runtime v6 infrastructure surface is not connected yet; behavioral cases skipped.")
print(" PENDING: Runtime v8 infrastructure surface is not connected yet; behavioral cases skipped.")
return ready
@@ -593,45 +593,23 @@ func _test_proxy_completion_projection_failure_is_atomic() -> void:
_free_fixture(fixture)
func _test_vertical_preflight_rejection() -> void:
func _test_completed_elevator_topology() -> void:
var fixture := _new_fixture(81_040)
var runtime: Variant = fixture[&"runtime"]
_expect(_promote_rank_two(fixture, &"infra.vertical.rank"), "vertical fixture reaches Rank 2")
var before := _infrastructure_state(fixture)
var guest = _submit(
runtime,
&"build_facility",
{
&"definition_id": &"guest_elevator",
&"origin": Vector3i(-6, 1, PathType.FRONT),
&"rotation": 0,
},
&"infra.vertical.guest"
)
var service = _submit(
runtime,
&"build_facility",
{
&"definition_id": &"service_elevator",
&"origin": Vector3i(5, 1, PathType.BACK),
&"rotation": 0,
},
&"infra.vertical.service"
)
_expect(
not guest.is_success()
and guest.details.get(&"reason", &"") == &"UNSUPPORTED_VERTICAL_TOPOLOGY",
"guest elevator is rejected at build-command preflight"
)
_expect(
not service.is_success()
and service.details.get(&"reason", &"") == &"UNSUPPORTED_VERTICAL_TOPOLOGY",
"service elevator is rejected with the same stable reason"
)
_expect(
_infrastructure_state(fixture) == before,
"unsupported vertical preflight spends nothing and mutates no service"
)
var cash_before: int = runtime.economy_service.get_balance(&"PLAYER_CASH")
for request: Array in [[&"guest_elevator", Vector3i(-6, 0, PathType.FRONT)], [&"service_elevator", Vector3i(5, 0, PathType.BACK)]]:
var result = _submit(runtime, &"build_facility", {&"definition_id": request[0], &"origin": request[1], &"rotation": 0}, StringName("infra.vertical." + String(request[0])))
_expect(result.is_success(), "%s is accepted through paid build-command preflight" % request[0])
_expect(runtime.economy_service.get_balance(&"PLAYER_CASH") == cash_before - 3700, "accepted lift construction spends actual definition costs")
_expect(runtime.facility_infrastructure_projector.get_projection_ids().is_empty(), "unfinished lift construction does not create usable shaft topology")
fixture[&"clock"].advance_minutes(360)
_expect(runtime.facility_infrastructure_projector.get_projection_ids().size() == 2 and runtime.elevators.get_report()[&"shafts"].size() == 2, "completed guest and service modules project separate cabins")
for shaft: Dictionary in runtime.elevators.get_report()[&"shafts"]:
var result = _submit(runtime, &"set_elevator_operation", {&"shaft_id": shaft[&"shaft_id"], &"operator_open": false}, StringName("infra.vertical.stop." + String(shaft[&"shaft_id"])))
_expect(result.is_success(), "completed lift can be stopped through Runtime command")
_expect(runtime.path_service.find_path(shaft[&"stops"][0], shaft[&"stops"][1]).is_empty(), "stopped lift shaft never permits an ordinary vertical walk bypass")
_expect(runtime.facility_infrastructure_projector.validate_invariants()[&"ok"], "completed elevator ROUTE and owned shaft restrictions crosslink exactly")
_free_fixture(fixture)
@@ -750,10 +728,10 @@ func _test_current_snapshot_strictness() -> void:
_expect(built[&"ok"], "snapshot fixture stores one completed projection")
var snapshot: Dictionary = runtime.create_snapshot()
var expected_digest := SimulationDigest.compute(snapshot)
_expect(runtime.restore_snapshot(snapshot), "valid Runtime v7 snapshot restores")
_expect(runtime.restore_snapshot(snapshot), "valid Runtime v8 snapshot restores")
_expect(
runtime.create_snapshot() == snapshot and runtime.latest_digest == expected_digest,
"Runtime v7 round trip is exact"
"Runtime v8 round trip is exact"
)
_assert_runtime_restore_rejected(
@@ -761,7 +739,7 @@ func _test_current_snapshot_strictness() -> void:
snapshot,
func(candidate: Dictionary) -> void:
candidate[&"systems"][&"runtime"][&"unknown_infrastructure"] = {},
"unknown v7 Runtime field"
"unknown v8 Runtime field"
)
for key: StringName in INFRASTRUCTURE_SNAPSHOT_KEYS:
_assert_runtime_restore_rejected(
@@ -838,9 +816,13 @@ func _test_v5_proxy_migration_determinism() -> void:
var stripped_v6_events := _strip_v6_infrastructure_domain_history(legacy_runtime)
legacy_runtime[&"runtime_schema_version"] = 5
legacy_runtime.erase(&"kitchen_production")
legacy_runtime.erase(&"elevators")
legacy_runtime.erase(&"staff_commute")
for key: StringName in INFRASTRUCTURE_SNAPSHOT_KEYS:
legacy_runtime.erase(key)
legacy_runtime[&"path"] = baseline[&"runtime"].path_service.create_snapshot()
legacy_runtime[&"path"][&"schema_version"] = 3
legacy_runtime[&"path"].erase(&"vertical_walk_blockers")
_free_fixture(baseline)
_expect(
stripped_v6_events > 0 and _legacy_v5_infrastructure_shape_is_clean(legacy_runtime),
@@ -855,8 +837,8 @@ func _test_v5_proxy_migration_determinism() -> void:
_expect(first_runtime.restore_snapshot(legacy), "v5 proxy snapshot migrates through Runtime restore")
var first_migrated: Dictionary = first_runtime.create_snapshot()
_expect(
int(first_migrated[&"systems"][&"runtime"][&"runtime_schema_version"]) == 7,
"v5 migration is immediately re-emitted as v7"
int(first_migrated[&"systems"][&"runtime"][&"runtime_schema_version"]) == 8,
"v5 migration is immediately re-emitted as v8"
)
_expect(
first_runtime.facility_infrastructure_projector.has_projection(instance_id)
@@ -1057,7 +1039,7 @@ func _strip_v6_infrastructure_domain_history(runtime_data: Dictionary) -> int:
func _legacy_v5_infrastructure_shape_is_clean(runtime_data: Dictionary) -> bool:
if runtime_data.has(&"kitchen_production"):
if runtime_data.has(&"kitchen_production") or runtime_data.has(&"elevators") or runtime_data.has(&"staff_commute"):
return false
for key: StringName in INFRASTRUCTURE_SNAPSHOT_KEYS:
if runtime_data.has(key) or runtime_data.has(String(key)):
+9 -6
View File
@@ -2,6 +2,7 @@ extends SceneTree
const POST_V2_RUNTIME_KEYS := [
&"kitchen_production",
&"elevators",
&"movement_credits",
&"catalog_manifest",
&"progression",
@@ -302,11 +303,11 @@ func _test_current_snapshot_crosslinks_and_legacy_v2() -> void:
_clock.advance_one_minute()
var snapshot: Dictionary = _runtime.create_snapshot()
var runtime_data: Dictionary = snapshot[&"systems"][&"runtime"]
_expect(int(runtime_data[&"runtime_schema_version"]) == 7, "Runtime snapshot advertises schema v7")
_expect(int(runtime_data[&"runtime_schema_version"]) == 8, "Runtime snapshot advertises schema v8")
_expect(not runtime_data[&"movement_credits"].is_empty(), "mid-move snapshot persists fixed-point movement credits")
var digest := SimulationDigest.compute(snapshot)
_expect(_runtime.restore_snapshot(snapshot), "valid mid-move v7 snapshot restores")
_expect(SimulationDigest.compute(_runtime.create_snapshot()) == digest, "mid-move v7 save/load is exact")
_expect(_runtime.restore_snapshot(snapshot), "valid mid-move v8 snapshot restores")
_expect(SimulationDigest.compute(_runtime.create_snapshot()) == digest, "mid-move v8 save/load is exact")
var corruptions: Array[Dictionary] = []
var bad_actor := snapshot.duplicate(true)
@@ -342,6 +343,8 @@ func _test_current_snapshot_crosslinks_and_legacy_v2() -> void:
var legacy_data: Dictionary = legacy_v2[&"systems"][&"runtime"]
legacy_data[&"runtime_schema_version"] = 2
_strip_post_v2_runtime_state(legacy_data)
legacy_data[&"path"][&"schema_version"] = 3
legacy_data[&"path"].erase(&"vertical_walk_blockers")
legacy_data[&"path"][&"actor_cells"] = []
legacy_data[&"path"][&"actor_progress"] = []
for traffic: Dictionary in legacy_data[&"path"][&"cell_traffic"]:
@@ -350,16 +353,16 @@ func _test_current_snapshot_crosslinks_and_legacy_v2() -> void:
traffic[&"temporary_block"] = true
_expect(
_is_true_runtime_v2_shape(legacy_data),
"legacy fixture removes every v3-v7 aggregate and dedicated history instead of relabelling v7"
"legacy fixture removes every v3-v8 aggregate and dedicated history instead of relabelling v8"
)
_expect(_runtime.restore_snapshot(legacy_v2), "legacy Runtime v2 deterministically rebuilds Path actors and credits")
var migrated: Dictionary = _runtime.create_snapshot()
_expect(
int(migrated[&"systems"][&"runtime"][&"runtime_schema_version"]) == 7
int(migrated[&"systems"][&"runtime"][&"runtime_schema_version"]) == 8
and migrated[&"systems"][&"runtime"][&"path"][&"actor_cells"].size() == 2
and migrated[&"systems"][&"runtime"][&"movement_credits"].size() == 2
and bool(_runtime.path_service.get_cell_traffic(origin)[&"temporary_block"]),
"legacy migration emits canonical v7 ownership while preserving blocked traffic state"
"legacy migration emits canonical v8 ownership while preserving blocked traffic state"
)
+16 -3
View File
@@ -156,7 +156,7 @@ func _test_runtime_assignment_boundary() -> void:
_expect(_runtime.submit_command(&"hire_staff_candidate", {&"candidate_id": id}).is_success(), "catalog employee is hired: %s" % id)
var money_before: int = _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT)
_expect(_assign(&"staff_akane", west, &"DAY").is_success(), "new command assigns hired receptionist")
_expect(buildings[0].active_staff_ids == [&"staff_akane"] and buildings[0].state == FacilityInstance.State.OPEN, "paused assignment immediately opens the facility")
_expect(buildings[0].active_staff_ids.is_empty() and _runtime.staff_service.get_staff(&"staff_akane").state == StaffRuntime.State.COMMUTING, "paused assignment waits for the receptionist to physically arrive")
var old_cell: Vector3i = _runtime.staff_service.get_staff(&"staff_akane").current_cell
var before: Dictionary = _runtime.staff_service.create_snapshot()
for row: Array in [
@@ -174,8 +174,10 @@ func _test_runtime_assignment_boundary() -> void:
_expect(_runtime.staff_service.create_snapshot() == before, "route failure preserves the old assignment")
_runtime.path_service.set_cell_temporary_block(old_cell, false)
_clock.advance_minutes(20)
_expect(buildings[0].active_staff_ids == [&"staff_akane"] and buildings[0].state == FacilityInstance.State.OPEN, "real commute arrival opens the first reception")
old_cell = _runtime.staff_service.get_staff(&"staff_akane").current_cell
_expect(_assign(&"staff_akane", east, &"DAY").is_success(), "employee can move to another compatible facility")
_expect(buildings[0].active_staff_ids.is_empty() and buildings[1].active_staff_ids == [&"staff_akane"], "reassignment removes the old service staffing and activates the new facility")
_expect(buildings[0].active_staff_ids.is_empty() and buildings[1].active_staff_ids.is_empty(), "reassignment removes old staffing while the new commute is in progress")
_expect(_runtime.staff_service.get_staff(&"staff_akane").current_cell == old_cell, "Runtime assignment leaves Path position intact")
_expect(_runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT) == money_before, "assignment and shift changes charge no second signing fee")
var report: Dictionary = _runtime.get_staff_management_report()
@@ -206,7 +208,7 @@ func _test_runtime_assignment_boundary() -> void:
var applied: Dictionary = _runtime.staff_service.get_work_assignment(&"staff_akane")
var applied_shift: ShiftInstance = _runtime.staff_service.get_shift(_runtime.staff_service.get_staff(&"staff_akane").shift_id)
_expect(int(applied[&"changed_at"]) == queued_at + 1 and int(applied[&"effective_from"]) == queued_at + 1, "queued assignment uses the execution minute, not the previous Staff tick")
_expect(applied_shift.worked_minutes == 1, "command and AI phases record the execution minute only once")
_expect(applied_shift.worked_minutes == 0, "queued reassignment cannot record work before arriving at its new workplace")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "queued assignment leaves a coherent save at the end of its tick")
_test_active_service_reassignment(west, east)
_test_busy_staff_and_capacity(west)
@@ -214,6 +216,7 @@ func _test_runtime_assignment_boundary() -> void:
func _test_active_service_reassignment(west: StringName, east: StringName) -> void:
_expect(_assign(&"staff_akane", east, &"DAY").is_success(), "service test keeps receptionist on DAY")
_wait_for_attendance(&"staff_akane", east)
var spawned = _runtime.submit_command(&"spawn_guest", {
&"guest_id": &"reassignment_guest", &"guest_type_id": &"traveler", &"gold": 100,
&"options": {&"state": GuestRuntime.State.DECIDING, &"current_cell": Vector3i.ZERO},
@@ -236,6 +239,7 @@ func _test_active_service_reassignment(west: StringName, east: StringName) -> vo
_expect(service.state == ServiceInstance.State.WAITING_FOR_STAFF, "existing service enters the normal staff-replacement grace period")
_expect(_runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT) == balance_before, "waiting service posts no phantom revenue")
_expect(_assign(&"staff_akane", east, &"DAY").is_success(), "replacement can return during grace period")
_wait_for_attendance(&"staff_akane", east)
_clock.advance_one_minute()
_expect(service.state == ServiceInstance.State.ACTIVE, "staff return resumes the same service instead of duplicating it")
_expect(_assign(&"staff_akane", &"", &"DAY").is_success(), "employee can be unassigned during service")
@@ -247,6 +251,7 @@ func _test_active_service_reassignment(west: StringName, east: StringName) -> vo
func _test_busy_staff_and_capacity(west: StringName) -> void:
_expect(_assign(&"staff_akane", west, &"DAY").is_success(), "busy-task test starts with a working employee")
_wait_for_attendance(&"staff_akane", west)
var task = _runtime.submit_command(&"create_staff_task", {
&"task_id": &"assignment_guard_task", &"task_type": &"reception", &"priority": 99,
&"options": {&"target_cell": Vector3i.ZERO, &"required_role_tags": [&"receptionist"],
@@ -270,6 +275,14 @@ func _test_busy_staff_and_capacity(west: StringName) -> void:
_expect(_assign(&"capacity_overflow", west, &"NIGHT").is_success(), "opposite shift has its own staffing capacity")
func _wait_for_attendance(staff_id: StringName, facility_id: StringName) -> void:
for minute in range(40):
if _runtime.facility_service.get_instance(facility_id).active_staff_ids.has(staff_id):
return
_clock.advance_one_minute()
_expect(false, "employee reaches the assigned workplace through the real route")
func _assign(staff_id: StringName, facility_id: StringName, template_id: StringName) -> CommandResult:
return _runtime.submit_command(&"set_staff_work_assignment", {&"staff_id": staff_id, &"facility_id": facility_id, &"template_id": template_id})
+3
View File
@@ -794,6 +794,9 @@ func _test_snapshot_replay_corruption_and_legacy_migration() -> void:
var legacy: Dictionary = _runtime.create_snapshot()
legacy[&"systems"][&"runtime"].erase(&"runtime_schema_version")
legacy[&"systems"][&"runtime"].erase(&"kitchen_production")
legacy[&"systems"][&"runtime"].erase(&"elevators")
legacy[&"systems"][&"runtime"][&"path"][&"schema_version"] = 3
legacy[&"systems"][&"runtime"][&"path"].erase(&"vertical_walk_blockers")
legacy[&"systems"][&"runtime"].erase(&"operations")
legacy[&"systems"][&"runtime"].erase(&"defense")
legacy[&"systems"][&"runtime"][&"rng_streams"].erase("defense")
+317
View File
@@ -0,0 +1,317 @@
extends SceneTree
const CommuteType = preload("res://scripts/staff/staff_commute_service.gd")
var _checks := 0
var _failures := 0
var _fixtures: Array = []
## Movement is injected at Runtime's narrow boundary. Real Staff, Shift, Task,
## Facility and staff-only Path rights run unchanged in this focused suite.
class CommuteRuntime extends RefCounted:
var staff_service := StaffService.new()
var facility_service := FacilityService.new()
var path_service := PathService.new()
var commute = CommuteType.new()
var movements: Array[Dictionary] = []
var last_moved: Dictionary = {}
var fail_movement := false
var in_transit := false
func configure() -> void:
var cells: Array[Vector3i] = []
for layer: int in [0, 1]:
for x in range(13): cells.append(Vector3i(x, 0, layer))
path_service.rebuild_graph(cells)
path_service.configure_connector(&"staff_door", Vector3i.ZERO, Vector3i(0, 0, 1), 100, true, &"staff")
for row: Array in [[&"west", Vector3i(5, 0, 0)], [&"east", Vector3i(10, 0, 0)], [&"back", Vector3i(5, 0, 1)]]:
facility_service.create_instance({&"instance_id": row[0], &"definition_id": &"office", &"origin": row[1], &"occupied_cells": [row[1]], &"staff_min": 1, &"staff_max": 2})
commute.configure(self)
func advance_staff_commute(staff_id: StringName, target: Vector3i, minute: int, reason: StringName = &"") -> Dictionary:
var worker := staff_service.get_staff(staff_id)
if not reason.is_empty() or int(last_moved.get(staff_id, -1)) >= minute or fail_movement:
return {&"moved": false}
var path := path_service.find_path(worker.current_cell, target, [0, 1], [&"staff"])
if path.size() <= 1: return {&"moved": false}
var previous := worker.current_cell
worker.current_cell = path[1]
last_moved[staff_id] = minute
movements.append({&"minute": minute, &"staff_id": staff_id, &"from": previous, &"to": worker.current_cell})
return {&"moved": true}
func is_staff_in_transit(_staff_id: StringName) -> bool:
return in_transit
func advance(minute: int) -> void:
staff_service.advance_one_minute(minute)
commute.tick(minute)
for facility: FacilityInstance in facility_service.get_instances():
var active: Array[StringName] = []
for worker: StaffRuntime in staff_service.get_staff_members():
if commute.is_present(worker, facility): active.append(worker.staff_id)
facility_service.set_operational_status(facility.instance_id, {&"active_staff_ids": active}, minute)
func dispose() -> void:
staff_service.configure_attendance_driver()
commute._runtime = null
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
print("P2: physical attendance, recurring commutes and saved position replay")
_test_legacy_contract()
_test_walk_and_arrival_staffing()
_test_early_arrival_and_shift_end()
_test_late_route_repair_and_replay()
_test_reassignment_and_return_to_post()
_test_reassignment_after_late_arrival_counts_boundary_once()
_test_porter_dispatch_after_check_in()
_test_back_layer_permissions_and_unavailable()
_test_night_shift_and_missing_workplace()
_test_boarded_reassignment_does_not_staff_origin()
for fixture: CommuteRuntime in _fixtures: fixture.dispose()
print("Staff commute: %d checks, %d failures" % [_checks, _failures])
quit(_failures)
func _fixture(role: StringName = &"receptionist", minute := 360) -> CommuteRuntime:
var fixture := CommuteRuntime.new()
fixture.configure()
fixture.staff_service.create_staff(&"worker", role)
fixture.staff_service.advance_one_minute(minute)
_fixtures.append(fixture)
return fixture
func _plan(fixture: CommuteRuntime, facility: StringName = &"west", template: StringName = &"DAY") -> StaffRuntime:
_expect(fixture.staff_service.set_work_assignment(&"worker", facility, template).is_success(), "repeating work plan is accepted")
return fixture.staff_service.get_staff(&"worker")
func _test_legacy_contract() -> void:
var legacy := StaffService.new()
legacy.create_staff(&"legacy", &"receptionist")
legacy.advance_one_minute(360)
legacy.set_work_assignment(&"legacy", &"west", &"DAY")
_expect(legacy.get_staff(&"legacy").state == StaffRuntime.State.AVAILABLE, "component-only recurring plans keep their original shift contract")
var fixture := _fixture()
fixture.staff_service.create_standard_shift(&"worker", &"DAY", 0, &"west", &"manual")
var worker := fixture.staff_service.get_staff(&"worker")
_expect(worker.state == StaffRuntime.State.AVAILABLE and not fixture.staff_service.requires_attendance(&"worker"), "manual legacy shifts remain available without a persistent plan")
_expect(fixture.commute.is_present(worker, fixture.facility_service.get_instance(&"west")), "legacy manual-shift staffing keeps compatibility")
func _test_walk_and_arrival_staffing() -> void:
var fixture := _fixture()
var worker := _plan(fixture)
var shift := fixture.staff_service.get_shift(worker.shift_id)
var west := fixture.facility_service.get_instance(&"west")
_expect(worker.state == StaffRuntime.State.COMMUTING and worker.current_cell == Vector3i.ZERO, "paused assignment starts a real commute without teleporting")
_expect(shift.worked_minutes == 0 and shift.actual_started_at == -1, "assignment time is not falsely counted as work")
_expect(not fixture.commute.is_present(worker, west), "a remote assignment cannot staff a facility")
for minute in range(361, 365):
var previous := worker.current_cell
fixture.advance(minute)
_expect(worker.current_cell.distance_to(previous) <= 1.0, "commute advances at most one route edge per minute")
_expect(worker.current_cell == Vector3i(4, 0, 0), "worker reaches the actual facility access cell")
_expect(worker.state == StaffRuntime.State.COMMUTING and west.active_staff_ids.is_empty(), "movement arrival activates at the next Staff AI boundary")
fixture.advance(365)
_expect(worker.state == StaffRuntime.State.AVAILABLE and west.active_staff_ids == [&"worker"], "only physical arrival contributes the facility staff capacity")
_expect(shift.actual_started_at == 365 and shift.worked_minutes == 1, "first recorded work minute is the arrival activation minute")
var snapshot := fixture.staff_service.create_snapshot()
fixture.commute.get_report()
_expect(fixture.staff_service.create_snapshot() == snapshot, "attendance report reads do not mutate saved Staff state")
_expect(fixture.staff_service.validate_invariants()[&"ok"], "physical commute preserves Staff invariants")
func _test_early_arrival_and_shift_end() -> void:
var fixture := _fixture(&"receptionist", 330)
var worker := _plan(fixture)
var shift := fixture.staff_service.get_shift(worker.shift_id)
for minute in range(331, 360): fixture.advance(minute)
_expect(worker.current_cell == Vector3i(4, 0, 0) and worker.state == StaffRuntime.State.COMMUTING, "early arrival waits at the workplace through the commute lead window")
_expect(shift.actual_started_at == -1 and shift.worked_minutes == 0, "early arrival does not start paid duty before 06:00")
fixture.advance(360)
_expect(worker.state == StaffRuntime.State.AVAILABLE and shift.actual_started_at == 360, "an employee already at the workplace starts at exactly 06:00")
fixture.advance(1080)
_expect(worker.state == StaffRuntime.State.OFF_DUTY and worker.shift_id.is_empty() and shift.actual_ended_at == 1080, "18:00 closes the work shift and removes attendance")
_expect(worker.current_cell == Vector3i(4, 0, 0), "shift end keeps the real position rather than teleporting home")
fixture.advance(1770)
_expect(worker.state == StaffRuntime.State.COMMUTING, "next day's recurring commute begins automatically at 05:30")
fixture.advance(1800)
_expect(worker.state == StaffRuntime.State.AVAILABLE, "next day's saved plan activates only at its new shift boundary")
func _test_late_route_repair_and_replay() -> void:
var fixture := _fixture()
fixture.path_service.set_cell_temporary_block(Vector3i(2, 0, 0), true)
var worker := _plan(fixture)
for minute in range(361, 379): fixture.advance(minute)
var shift := fixture.staff_service.get_shift(worker.shift_id)
_expect(worker.current_cell == Vector3i.ZERO and worker.state == StaffRuntime.State.COMMUTING, "a blocked route never teleports or invents attendance")
_expect(shift.late_minutes == 18 and shift.late_reason == CommuteType.ROUTE_BLOCKED, "lateness records the real route blockage after the grace threshold")
_expect(shift.worked_minutes == 0 and shift.actual_started_at == -1, "unreachable staff do not accumulate fictional worked minutes")
var checkpoint := fixture.staff_service.create_snapshot()
var restored := _fixture()
_expect(restored.staff_service.restore_snapshot(checkpoint), "a blocked commute snapshot restores using existing Staff and Shift data")
restored.path_service.set_cell_temporary_block(Vector3i(2, 0, 0), true)
_expect(restored.staff_service.create_snapshot() == checkpoint, "restored blocked attendance is exact without new service state")
fixture.path_service.set_cell_temporary_block(Vector3i(2, 0, 0), false)
restored.path_service.set_cell_temporary_block(Vector3i(2, 0, 0), false)
for minute in range(379, 390):
fixture.advance(minute)
restored.advance(minute)
_expect(fixture.staff_service.create_snapshot() == restored.staff_service.create_snapshot(), "route repair replays to identical work, lateness and positions after restore")
_expect(worker.state == StaffRuntime.State.AVAILABLE and worker.absence_reason.is_empty(), "repairing the route allows physical arrival and clears the active absence")
_expect(shift.actual_started_at == 383 and shift.late_minutes == 22, "arrival keeps historical late minutes while beginning real duty")
var midwalk := _fixture()
var walker := _plan(midwalk)
midwalk.advance(361)
midwalk.advance(362)
var moving := midwalk.staff_service.create_snapshot()
var replay := _fixture()
_expect(replay.staff_service.restore_snapshot(moving), "mid-walk save restores the exact current cell")
for minute in range(363, 368):
midwalk.advance(minute)
replay.advance(minute)
_expect(midwalk.staff_service.create_snapshot() == replay.staff_service.create_snapshot() and walker.current_cell == Vector3i(4, 0, 0), "mid-walk replay neither skips nor repeats route cells")
func _test_reassignment_and_return_to_post() -> void:
var fixture := _fixture()
var worker := _plan(fixture)
for minute in range(361, 367): fixture.advance(minute)
var first := fixture.staff_service.get_shift(worker.shift_id)
_expect(fixture.staff_service.set_work_assignment(&"worker", &"east", &"DAY").is_success(), "fixed staff can be reassigned after arriving")
first = fixture.staff_service.get_shift(first.shift_instance_id)
_expect(worker.state == StaffRuntime.State.COMMUTING and worker.current_cell == Vector3i(4, 0, 0), "reassignment walks from the current location")
_expect(first.assigned_facility_id == &"west" and first.actual_ended_at == 366, "historical duty remains attached to the old workplace")
for minute in range(367, 374): fixture.advance(minute)
var shift := fixture.staff_service.get_shift(worker.shift_id)
_expect(worker.current_cell == Vector3i(9, 0, 0) and worker.state == StaffRuntime.State.AVAILABLE, "reassignment opens the new workplace after arrival")
# Completion of a portable task leaves its employee at the work target.
worker.current_cell = Vector3i(6, 0, 0)
worker.transition_to(StaffRuntime.State.AVAILABLE, &"TASK_COMPLETED", 374)
fixture.advance(374)
_expect(worker.state == StaffRuntime.State.COMMUTING and worker.current_cell == Vector3i(7, 0, 0), "fixed roles return to their post after work elsewhere")
fixture.advance(375)
fixture.advance(376)
fixture.advance(377)
_expect(worker.state == StaffRuntime.State.AVAILABLE and worker.current_cell == Vector3i(9, 0, 0), "desk service resumes after returning physically")
_expect(shift.late_minutes == 0, "returning from a completed task does not rewrite arrival lateness")
_expect(fixture.staff_service.set_work_assignment(&"worker", &"", &"DAY").is_success(), "unassignment cancels the repeating commute")
var cell := worker.current_cell
fixture.advance(378)
_expect(worker.state == StaffRuntime.State.OFF_DUTY and worker.current_cell == cell, "unassigned staff keep position and stop commuting")
func _test_reassignment_after_late_arrival_counts_boundary_once() -> void:
var fixture := _fixture()
var worker := _plan(fixture)
for minute in range(361, 366): fixture.advance(minute)
var old_id := worker.shift_id
fixture.facility_service.create_instance({&"instance_id": &"nearby", &"definition_id": &"office", &"origin": Vector3i(3, 0, 0), &"occupied_cells": [Vector3i(3, 0, 0)]})
_expect(fixture.staff_service.get_shift(old_id).worked_minutes == 1, "late arrival has only one actual duty minute before reassignment")
_expect(fixture.staff_service.set_work_assignment(&"worker", &"nearby", &"DAY").is_success(), "paused reassignment may choose another workplace sharing this access cell")
var old := fixture.staff_service.get_shift(old_id)
var replacement := fixture.staff_service.get_shift(worker.shift_id)
_expect(old.worked_minutes == 0 and old.actual_started_at == -1 and replacement.worked_minutes == 1, "reassignment transfers the current duty minute exactly once despite earlier commute gaps")
_expect(fixture.staff_service.restore_snapshot(fixture.staff_service.create_snapshot()), "gap-aware historical shift split restores without extra saved attendance state")
func _test_porter_dispatch_after_check_in() -> void:
var fixture := _fixture(&"porter")
var worker := _plan(fixture)
_expect(not worker.is_available_for_dispatch(), "new porter cannot take a delivery before checking in")
for minute in range(361, 367): fixture.advance(minute)
worker.current_cell = Vector3i(10, 0, 0)
worker.transition_to(StaffRuntime.State.AVAILABLE, &"TASK_COMPLETED", 367)
fixture.advance(367)
_expect(worker.state == StaffRuntime.State.AVAILABLE and worker.current_cell == Vector3i(10, 0, 0), "checked-in porter can accept another job from a delivery destination")
_expect(not fixture.commute.is_present(worker, fixture.facility_service.get_instance(&"west")), "porter away from the warehouse does not falsely staff the warehouse")
var restored := _fixture(&"porter")
_expect(restored.staff_service.restore_snapshot(fixture.staff_service.create_snapshot()), "the porter's once-per-shift check-in survives save restoration")
restored.advance(368)
_expect(restored.staff_service.get_staff(&"worker").state == StaffRuntime.State.AVAILABLE, "loaded porter duty does not force an unnecessary return commute")
fixture.advance(1080)
fixture.advance(1770)
_expect(worker.state == StaffRuntime.State.COMMUTING and worker.current_cell == Vector3i(9, 0, 0), "the following shift requires a fresh real warehouse check-in")
func _test_back_layer_permissions_and_unavailable() -> void:
var fixture := _fixture()
var worker := _plan(fixture, &"back")
var access: Dictionary = fixture.commute.find_facility_access_path(Vector3i.ZERO, fixture.facility_service.get_instance(&"back"))
_expect(access[&"reachable"] and access[&"cell"].z == 1, "commute can reach BACK facilities through an authorized staff door")
_expect(fixture.path_service.find_path(Vector3i.ZERO, Vector3i(4, 0, 1), [0, 1], []).is_empty(), "the same staff door remains inaccessible without staff rights")
fixture.advance(361)
_expect(worker.current_cell == Vector3i(0, 0, 1), "cross-layer commute uses the actual connector endpoint")
worker.health = 0
worker.transition_to(StaffRuntime.State.INCAPACITATED, &"TEST", 362)
fixture.advance(362)
_expect(worker.current_cell == Vector3i(0, 0, 1) and worker.state == StaffRuntime.State.INCAPACITATED, "unavailable workers do not walk or get promoted to available")
worker.health = 100
worker.transition_to(StaffRuntime.State.COMMUTING, &"RECOVERED", 363)
fixture.fail_movement = true
fixture.advance(363)
_expect(worker.current_cell == Vector3i(0, 0, 1), "a refused Runtime movement leaves the commute position unchanged")
fixture.fail_movement = false
for minute in range(364, 371): fixture.advance(minute)
_expect(fixture.commute.is_present(worker, fixture.facility_service.get_instance(&"back")), "recovered staff finish the real BACK commute")
_expect(not CommuteType.is_at_facility(worker, fixture.facility_service.get_instance(&"west")), "identical screen coordinates on different layers never count as attendance")
var upper := FacilityInstance.new().configure(&"upper", &"office", {&"origin": Vector3i(4, 1, 1), &"occupied_cells": [Vector3i(4, 1, 1)]})
_expect(not CommuteType.is_at_facility(worker, upper), "standing below an upper room cannot bypass the route to its floor")
_expect(not fixture.commute.find_facility_access_path(worker.current_cell, upper)[&"reachable"], "a corridor underneath a room is not a valid workplace entrance")
func _test_night_shift_and_missing_workplace() -> void:
var fixture := _fixture(&"receptionist", 1410)
var worker := _plan(fixture, &"west", &"NIGHT")
for minute in range(1411, 1417): fixture.advance(minute)
var shift := fixture.staff_service.get_shift(worker.shift_id)
fixture.advance(1440)
_expect(worker.state == StaffRuntime.State.AVAILABLE and worker.shift_id == shift.shift_instance_id, "midnight retains the same physically attended NIGHT shift")
fixture.advance(1800)
_expect(worker.state == StaffRuntime.State.OFF_DUTY and shift.actual_ended_at == 1800, "NIGHT attendance ends at next-day 06:00")
var missing := _fixture()
var absent := _plan(missing, &"missing")
missing.advance(361)
_expect(absent.state == StaffRuntime.State.COMMUTING and absent.absence_reason == CommuteType.WORKPLACE_MISSING, "missing workplace remains a visible waiting condition")
_expect(missing.staff_service.get_shift(absent.shift_id).worked_minutes == 0, "a missing workplace creates no phantom duty")
func _test_boarded_reassignment_does_not_staff_origin() -> void:
var fixture := _fixture()
var worker := _plan(fixture)
fixture.facility_service.create_instance({&"instance_id": &"boarding_post", &"definition_id": &"office", &"origin": Vector3i.ZERO, &"occupied_cells": [Vector3i.ZERO]})
fixture.in_transit = true
_expect(fixture.staff_service.set_work_assignment(&"worker", &"boarding_post", &"DAY").is_success(), "a boarded commute can accept a new destination plan")
var shift := fixture.staff_service.get_shift(worker.shift_id)
fixture.advance(361)
_expect(worker.state == StaffRuntime.State.COMMUTING and shift.worked_minutes == 0, "the cabin's projected boarding cell cannot turn into attendance at the origin")
_expect(not fixture.commute.is_present(worker, fixture.facility_service.get_instance(&"boarding_post")), "a passenger never supplies staff capacity while still in transit")
_expect(fixture.commute.get_report()[&"staff"][0][&"reason"] == &"ELEVATOR_WAIT", "commute report distinguishes the owned ride from arrival at its projected origin")
_expect(worker.current_cell == Vector3i.ZERO and fixture.movements.is_empty(), "the commute driver waits for the owned ride rather than moving its projected anchor")
fixture.in_transit = false
worker.current_cell = Vector3i(3, 0, 0)
fixture.advance(362)
_expect(worker.state == StaffRuntime.State.COMMUTING and worker.current_cell == Vector3i(2, 0, 0), "after safe disembarkation the employee walks toward the latest assignment")
fixture.advance(363)
fixture.advance(364)
_expect(worker.state == StaffRuntime.State.AVAILABLE and shift.actual_started_at == 364, "new-post duty begins only after the completed ride and actual return")
fixture.in_transit = true
worker.current_task_id = &"owned_work"
_expect(fixture.commute.can_attend_shift(worker, shift, 365), "an already checked-in employee's owned portable work remains duty while riding")
worker.current_task_id = &""
_expect(not fixture.commute.can_attend_shift(worker, shift, 365), "an idle passenger cannot become dispatchable based on its old checked-in position")
func _expect(condition: bool, message: String) -> void:
_checks += 1
if not condition:
_failures += 1
push_error("FAIL: %s" % message)
+1
View File
@@ -0,0 +1 @@
uid://cpp5niwsp4ayo
+16 -3
View File
@@ -52,16 +52,21 @@ func _run() -> void:
return
_expect(_command(&"hire_staff_candidate", {&"candidate_id": PORTER}), "hire the catalogue porter")
_expect(_command(&"set_staff_work_assignment", {&"staff_id": PORTER, &"facility_id": _source, &"template_id": &"DAY"}), "assign porter to a repeating real shift")
_expect(_worker().state == StaffRuntime.State.COMMUTING, "porter assignment begins a real warehouse commute")
for minute in range(40):
if _worker().state == StaffRuntime.State.AVAILABLE: break
_clock.advance_minutes(1)
_expect(_worker().state == StaffRuntime.State.AVAILABLE and StaffCommuteService.is_at_facility(_worker(), _runtime.facility_service.get_instance(_source)), "porter checks in physically before taking a supply job")
_expect(_command(&"purchase_supplies", {&"warehouse_id": _source, &"item_id": SupplyCatalog.RAW_FOOD, &"quantity": 8}), "purchase the source stock through the ledger")
_expect(_command(&"create_logistics_request", {&"request_id": REQUEST, &"source_inventory_id": _inventory(_source), &"destination_inventory_id": _inventory(_destination), &"item_requirements": {SupplyCatalog.RAW_FOOD: 8}}), "create the reserved supply request")
_expect(_quantity(_inventory(_destination)) == 0, "request creation does not deliver anything")
_test_manual_dispatch_boundary()
_reserved = _runtime.create_snapshot()
var check_in_cell := _worker().current_cell
_clock.advance_minutes(1)
_expect(_request().assigned_carrier_id == PORTER and _worker().current_cell.distance_to(Vector3i.ZERO) <= 1.0, "dispatcher selects the actual employee without teleporting")
var start := _worker().current_cell
_expect(_request().assigned_carrier_id == PORTER and _worker().current_cell.distance_to(check_in_cell) <= 1.0, "dispatcher selects the checked-in employee without teleporting")
_wait_for(LogisticsRequest.State.DELIVERING, 40)
_expect(_request().state == LogisticsRequest.State.DELIVERING and _worker().current_cell != start, "porter walks to the source before pickup")
_expect(_request().state == LogisticsRequest.State.DELIVERING and _worker().current_cell.distance_to(check_in_cell) <= 1.0, "physical pickup starts delivery from the checked-in warehouse access without teleporting")
if _request().state != LogisticsRequest.State.DELIVERING:
_finish()
return
@@ -69,6 +74,7 @@ func _run() -> void:
_expect(_crosslinks(), "loaded Request, Task, reservations and cargo agree")
_loaded = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(_loaded), "mid-delivery Runtime snapshot restores")
_test_same_floor_access()
_test_ownership_and_replay()
_test_capacity_wait()
_test_route_recovery()
@@ -84,6 +90,13 @@ func _run() -> void:
_finish()
func _test_same_floor_access() -> void:
var upper := FacilityInstance.new().configure(&"upper", &"warehouse", {&"origin": Vector3i(2, 1, 1), &"occupied_cells": [Vector3i(2, 1, 1)]})
_expect(not SupplyLogisticsBridge._cell_touches_facility(Vector3i(2, 0, 1), upper), "goods cannot be picked up from the floor below a warehouse")
_expect(SupplyLogisticsBridge._cell_touches_facility(Vector3i(1, 1, 1), upper), "same-floor doorway remains a valid transfer boundary")
_expect(not _runtime.supply_logistics.find_facility_access_path(Vector3i(2, 0, 1), upper).get(&"reachable", false), "source access routing cannot substitute a corridor below the destination floor")
func _test_ownership_and_replay() -> void:
var owned := _owned_snapshot()
var manual: CommandResult = _runtime.submit_command(&"advance_logistics_request", {&"request_id": REQUEST})
+13 -7
View File
@@ -9,7 +9,7 @@ func _initialize() -> void:
func _run() -> void:
print("P1: Runtime v7 supply save migration")
print("P1: Runtime v8 supply save migration")
var source := _fixture(7301)
var first := _fixture(7302)
var second := _fixture(7303)
@@ -19,28 +19,31 @@ func _run() -> void:
source[&"clock"].advance_minutes(3)
var canonical: Dictionary = source_runtime.create_snapshot()
var canonical_data: Dictionary = canonical[&"systems"][&"runtime"]
_expect(canonical_data[&"runtime_schema_version"] == 7, "current Runtime emits schema v7")
_expect(canonical_data[&"runtime_schema_version"] == 8, "current Runtime emits schema v8")
_expect(canonical_data[&"kitchen_production"][&"current_minute"] == source[&"clock"].absolute_minute, "production clock follows Runtime clock without existing kitchens")
var legacy := canonical.duplicate(true)
var legacy_data: Dictionary = legacy[&"systems"][&"runtime"]
legacy_data[&"runtime_schema_version"] = 6
legacy_data.erase(&"kitchen_production")
legacy_data.erase(&"elevators")
legacy_data[&"path"][&"schema_version"] = 3
legacy_data[&"path"].erase(&"vertical_walk_blockers")
_expect(legacy_data.has(&"facility_infrastructure") and legacy_data.has(&"route_projection"), "v6 fixture retains infrastructure introduced in v6")
_expect(first_runtime.restore_snapshot(legacy), "genuine v6 state restores into v7")
_expect(first_runtime.restore_snapshot(legacy), "genuine v6 state restores into v8")
var migrated: Dictionary = first_runtime.create_snapshot()
_expect(migrated[&"systems"][&"runtime"][&"runtime_schema_version"] == 7, "v6 migration re-emits current schema")
_expect(migrated[&"systems"][&"runtime"][&"runtime_schema_version"] == 8, "v6 migration re-emits current schema")
var kitchen: Dictionary = migrated[&"systems"][&"runtime"][&"kitchen_production"]
_expect(kitchen[&"plans"].is_empty() and kitchen[&"active_batches"].is_empty(), "legacy migration does not infer kitchen production plans or batches")
_expect(kitchen[&"current_minute"] == source[&"clock"].absolute_minute, "legacy production clock binds to the restored Runtime minute")
_expect(migrated == canonical, "v6 migration preserves non-supply state exactly")
_expect(second_runtime.restore_snapshot(legacy) and second_runtime.create_snapshot() == migrated, "v6 migration is deterministic across Runtime instances")
_expect(first_runtime.restore_snapshot(migrated) and first_runtime.create_snapshot() == migrated, "canonical v7 round trip is exact")
_expect(first_runtime.restore_snapshot(migrated) and first_runtime.create_snapshot() == migrated, "canonical v8 round trip is exact")
var missing_supply := canonical.duplicate(true)
missing_supply[&"systems"][&"runtime"].erase(&"kitchen_production")
_expect(_rejects_atomically(first_runtime, missing_supply), "v7 cannot omit its supply aggregate")
_expect(_rejects_atomically(first_runtime, missing_supply), "v8 cannot omit its supply aggregate")
var relabelled := canonical.duplicate(true)
relabelled[&"systems"][&"runtime"][&"runtime_schema_version"] = 6
_expect(_rejects_atomically(first_runtime, relabelled), "relabelled v7 with retained kitchen state cannot masquerade as v6")
_expect(_rejects_atomically(first_runtime, relabelled), "relabelled v8 with retained kitchen state cannot masquerade as v6")
var bad_clock := canonical.duplicate(true)
bad_clock[&"systems"][&"runtime"][&"kitchen_production"][&"current_minute"] -= 1
_expect(_rejects_atomically(first_runtime, bad_clock), "production and Runtime clock disagreement is rejected")
@@ -57,6 +60,9 @@ func _run() -> void:
var orphaned: Dictionary = source_runtime.create_snapshot()
orphaned[&"systems"][&"runtime"][&"runtime_schema_version"] = 6
orphaned[&"systems"][&"runtime"].erase(&"kitchen_production")
orphaned[&"systems"][&"runtime"].erase(&"elevators")
orphaned[&"systems"][&"runtime"][&"path"][&"schema_version"] = 3
orphaned[&"systems"][&"runtime"][&"path"].erase(&"vertical_walk_blockers")
_expect(_rejects_atomically(first_runtime, orphaned), "legacy label cannot hide input holds for a discarded production batch")
for fixture: Dictionary in [source, first, second]:
fixture[&"runtime"].free()
+258
View File
@@ -0,0 +1,258 @@
extends SceneTree
var _checks := 0
var _failures := 0
var _path: PathService
var _elevators: ElevatorService
var _config: Dictionary
var _minute := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
var clock := root.get_node_or_null("GameClock")
if clock != null: clock.set_paused(true)
_test_grouping()
_test_capacity_and_time()
_test_access_and_shared_cabin()
_test_restore_and_emergency()
_test_mutations_and_cancellation()
_test_snapshot_rejections()
print("Elevator service: %d checks, %d failures" % [_checks, _failures])
quit(0 if _failures == 0 else 1)
func _setup(layer := 0, floors := 4) -> void:
_path = PathService.new()
var cells: Array[Vector3i] = []
for y: int in range(floors + 2):
cells.append(Vector3i(0, y, layer))
_path.rebuild_graph(cells)
_elevators = ElevatorService.new()
_elevators.configure(_path)
_config = {&"shaft_id": StringName("transport.shaft.%d.0.0.%d" % [layer, 2 if layer == 0 else 3]),
&"module_ids": [&"module.0", &"module.1"], &"stops": [], &"width": 2 if layer == 0 else 3,
&"capacity": 4 if layer == 0 else 8, &"travel_minutes_per_floor": 10 if layer == 0 else 5,
&"access_id": &"" if layer == 0 else &"staff"}
for y: int in range(floors): (_config[&"stops"] as Array).append(Vector3i(0, y, layer))
_expect(_elevators.synchronize([_config], 0)[&"ok"], "shaft initializes")
_minute = 0
func _request(actor: StringName, from: int, to: int, layer := 0) -> Dictionary:
var access: Array[StringName] = []
if layer == 1: access.append(&"staff")
return _elevators.request_trip(actor, Vector3i(0, from, layer), Vector3i(0, to, layer), _minute, access)
func _tick(count := 1) -> Array[Dictionary]:
var arrivals: Array[Dictionary] = []
for i: int in range(count):
_minute += 1
arrivals.append_array(_elevators.advance_one_minute(_minute))
return arrivals
func _car() -> Dictionary:
return _elevators.get_report()[&"shafts"][0]
func _test_grouping() -> void:
var registry := DefinitionRegistry.new()
registry.rebuild()
var records: Array = []
for row: Array in [[&"g2", &"guest_elevator", Vector3i(0, 2, 0)], [&"g0", &"guest_elevator", Vector3i(0, 0, 0)], [&"g6", &"guest_elevator", Vector3i(0, 6, 0)], [&"b0", &"service_elevator", Vector3i(0, 0, 1)], [&"other", &"reception", Vector3i(6, 0, 0)]]:
records.append({&"instance_id": row[0], &"definition_id": row[1], &"origin": row[2]})
var configs := ElevatorService.configurations_from_facilities(records, registry)
_expect(configs.size() == 3, "adjacent modules form one shaft, gaps and layers form separate shafts")
_expect(configs[0][&"stops"] == [Vector3i(0, 0, 0), Vector3i(0, 1, 0), Vector3i(0, 2, 0), Vector3i(0, 3, 0)], "each occupied integer floor gets a landing")
_expect(configs[0][&"module_ids"] == [&"g0", &"g2"], "construction ordering does not change module/stop ordering")
_expect(configs[2][&"capacity"] == 8 and configs[2][&"travel_minutes_per_floor"] == 5 and configs[2][&"access_id"] == &"staff", "BACK shaft has eight seats, five minutes per floor and staff access")
records.reverse()
_expect(ElevatorService.configurations_from_facilities(records, registry) == configs, "configuration generation is deterministic")
func _test_capacity_and_time() -> void:
_setup()
_expect(_path.create_snapshot()[&"connectors"].size() == 6, "four stops expose six bidirectional direct links")
for i: int in range(5): _expect(_request(StringName("guest/%d" % i), 0, 3)[&"ok"], "guest queues without moving")
_expect(_car()[&"passenger_count"] == 0 and _car()[&"waiting_count"] == 5, "requests do not board before simulation advances")
_expect(_tick().is_empty() and _car()[&"passenger_count"] == 4 and _car()[&"waiting_count"] == 1, "shared cabin boards at most four FRONT actors")
_expect(_car()[&"phase"] == &"BOARDING" and _car()[&"remaining_minutes"] == 6, "doors take two minutes plus one per boarding actor")
_expect(_tick(6).is_empty() and _car()[&"phase"] == &"TRAVEL", "boarding duration elapses before cabin travels")
_expect(_tick(15).is_empty() and is_equal_approx(_car()[&"visual_floor"], 1.5), "cabin interpolates actual half journey without arriving")
_expect(_tick(15).is_empty() and _car()[&"phase"] == &"ALIGHTING", "three FRONT floors take thirty minutes before alighting")
_expect(_tick(5).is_empty(), "passengers remain owned while doors are unloading")
var arrivals := _tick()
_expect(arrivals.size() == 4 and _minute == 43, "first four arrivals happen only after both door intervals and travel")
for arrival: Dictionary in arrivals:
_expect(arrival[&"cell"] == Vector3i(0, 3, 0) and not arrival[&"emergency"] and _path.get_active_elevator_request(arrival[&"actor_id"]).is_empty(), "arrival transfers actor once and releases Path ownership")
_expect(_car()[&"phase"] == &"PICKUP" and _car()[&"waiting_count"] == 1, "remaining guest waits for same cabin to return")
arrivals = _tick(66)
_expect(arrivals.size() == 1 and arrivals[0][&"actor_id"] == &"guest/4" and _car()[&"phase"] == &"IDLE", "fifth guest waits for real return and completes without starvation")
_expect(_tick(10).is_empty(), "completed request never arrives twice")
var read := _elevators.get_report()
read[&"shafts"][0][&"boarded"].append(&"tamper")
_expect(_car()[&"boarded"].is_empty(), "report returns defensive copies")
func _test_access_and_shared_cabin() -> void:
_setup(1)
_expect(not _elevators.request_trip(&"guest/no_access", Vector3i(0, 0, 1), Vector3i(0, 3, 1), 0)[&"ok"], "BACK elevator rejects actor without staff access")
for i: int in range(9): _expect(_request(StringName("staff/%d" % i), 0, 3, 1)[&"ok"], "authorized staff queues")
_tick()
_expect(_car()[&"passenger_count"] == 8 and _car()[&"waiting_count"] == 1, "BACK cabin shares eight seats across requests")
_tick(10)
_expect(_car()[&"phase"] == &"TRAVEL" and _car()[&"remaining_minutes"] == 15, "BACK shaft uses five minutes per floor")
_tick(15)
_expect(_car()[&"phase"] == &"ALIGHTING", "BACK travel duration is physical")
_expect(_tick(10).size() == 8, "BACK eight passengers alight together after paid time")
_setup()
_request(&"a", 0, 3)
_request(&"b", 1, 2)
_tick()
_expect(_car()[&"passenger_count"] == 1 and _car()[&"waiting_count"] == 1, "different floor pair cannot create a second cabin")
_expect(_elevators.get_actor_trip(&"b")[&"state"] == PathService.ELEVATOR_REQUEST_QUEUED, "other connector actor remains waiting while shared car moves")
_expect(_elevators.owns_connector(_elevators.get_actor_trip(&"a")[&"connector_id"]), "transport identifies its owned path step")
_expect(not _elevators.owns_connector(&"door.other"), "unrelated door connector is not transport-owned")
var all := _tick(100)
_expect(all.size() == 2 and all[0][&"actor_id"] == &"a" and all[1][&"actor_id"] == &"b", "FIFO serves different floor pair after first journey")
func _test_restore_and_emergency() -> void:
_setup()
_request(&"rider", 0, 3)
_tick(20)
_expect(_car()[&"phase"] == &"TRAVEL" and _car()[&"visual_floor"] > 1.0, "midride fixture is physically between floors")
var state := _elevators.create_snapshot()
var path_state := _path.create_snapshot()
var future := _tick(40)
var final_state := _elevators.create_snapshot()
_expect(_path.restore_snapshot(path_state), "Path queue/boarded state restores")
_expect(_elevators.restore_snapshot(state), "midride save restores against matching Path ownership: %s" % _elevators.last_error)
_minute = state[&"current_minute"]
_expect(_tick(40) == future and _elevators.create_snapshot() == final_state, "restored trip produces identical arrivals and final snapshot")
_setup()
_request(&"aboard", 0, 3)
_request(&"waiting", 2, 3)
_tick(20)
var released := _elevators.set_powered(_config[&"shaft_id"], false, _minute)
_expect(released.size() == 2, "power failure releases both waiting and boarded owners")
_expect(released[0][&"actor_id"] == &"aboard" and released[0][&"cell"] == Vector3i(0, 2, 0) and released[0][&"emergency"], "power failure unloads boarded actor at nearest valid floor")
_expect(released[1][&"actor_id"] == &"waiting" and released[1][&"cell"] == Vector3i(0, 2, 0) and released[1][&"cancelled"], "waiting cancellation stays on original landing for replan")
_expect(_car()[&"operator_open"] and not _car()[&"powered"] and not _car()[&"operational"], "power loss does not alter player operation policy")
_expect(_path.get_active_elevator_request(&"aboard").is_empty() and _elevators.get_actor_trip(&"waiting").is_empty(), "emergency leaves no orphan owner")
_expect(not _request(&"stopped", 0, 3)[&"ok"], "stopped elevator rejects new boarding")
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "stopped elevator restores with Path outage state")
_elevators.set_operator_open(_config[&"shaft_id"], false, _minute)
_elevators.set_powered(_config[&"shaft_id"], true, _minute)
_expect(not _car()[&"operator_open"] and _car()[&"powered"] and not _car()[&"operational"], "power restoration preserves player-requested stop")
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "player-stopped powered elevator state restores")
_elevators.set_operator_open(_config[&"shaft_id"], true, _minute)
_expect(_car()[&"operational"] and _request(&"resumed", 2, 0)[&"ok"], "player reopens restored elevator")
_expect(_tick(27).size() == 1, "reopened cabin resumes from emergency landing with real travel")
_setup()
_request(&"blocked_destination", 0, 3)
_tick(20)
_path.configure_cell_traffic(Vector3i(0, 3, 0), 4, 0, true)
released = _tick()
_expect(released.size() == 1 and released[0][&"emergency"] and released[0][&"cell"] != Vector3i(0, 3, 0), "blocked destination causes emergency at a valid landing")
_setup()
_request(&"held", 0, 3)
_request(&"waiting_held", 2, 3)
_tick(15)
for cell: Vector3i in _config[&"stops"]: _path.configure_cell_traffic(cell, 4, 0, true)
released = _elevators.set_operator_open(_config[&"shaft_id"], false, _minute)
_expect(released.size() == 1 and released[0][&"actor_id"] == &"waiting_held" and released[0][&"cancelled"], "all-landings closure still cancels waiting actors at their original position")
_expect(_car()[&"passenger_count"] == 1 and not _car()[&"operational"] and not _path.get_active_elevator_request(&"held").is_empty(), "no valid landing holds boarded actor safely with ownership")
var held_remaining: int = _car()[&"remaining_minutes"]
_tick(20)
_expect(_car()[&"remaining_minutes"] == held_remaining and _elevators.restore_snapshot(_elevators.create_snapshot()), "held cabin survives elapsed time and save restore without advancing or losing its rider")
for cell: Vector3i in _config[&"stops"]: _path.configure_cell_traffic(cell)
_elevators.set_operator_open(_config[&"shaft_id"], true, _minute)
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "repaired cabin resumes with an adjusted physical phase clock")
_expect(_tick(40).size() == 1, "held actor arrives exactly once after landing repair")
func _test_mutations_and_cancellation() -> void:
_setup()
_request(&"waiter", 0, 3)
var before := _elevators.create_snapshot()
_expect(not _elevators.synchronize([], _minute)[&"ok"] and _elevators.create_snapshot() == before, "queued rider prevents destructive shaft modification atomically")
var larger := _config.duplicate(true)
larger[&"module_ids"].append(&"module.2")
larger[&"stops"].append(Vector3i(0, 4, 0))
larger[&"stops"].append(Vector3i(0, 5, 0))
_expect(not _elevators.synchronize([larger], _minute)[&"ok"], "extension waits until cabin and queues are idle")
_expect(_elevators.cancel_actor(&"waiter") and _path.get_active_elevator_request(&"waiter").is_empty(), "waiting actor removal cancels Path queue ownership")
_expect(_elevators.synchronize([larger], _minute)[&"ok"] and _car()[&"stops"].size() == 6, "idle upward extension joins existing physical cabin")
_expect(_request(&"despawn", 0, 3)[&"ok"], "new request uses fresh request ID after cancellation")
_tick(2)
_expect(_elevators.cancel_actor(&"despawn") and _path.get_active_elevator_request(&"despawn").is_empty() and _car()[&"passenger_count"] == 0, "boarded actor despawn removes only seat and ownership")
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "saved doors remain valid after only passenger despawns")
_expect(_tick(50).is_empty() and _car()[&"phase"] == &"IDLE", "empty cabin safely finishes its motion without resurrecting despawned actor")
_expect(_elevators.synchronize([], _minute)[&"ok"] and _path.create_snapshot()[&"connectors"].is_empty(), "idle demolition removes all owned connectors")
_setup()
_request(&"duplicate", 0, 3)
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "queued snapshot restores with matching floor, request, and Path unit")
var replay := _request(&"duplicate", 0, 3)
_expect(replay[&"ok"] and replay[&"replay"] and _car()[&"waiting_count"] == 1, "same actor route request replays without duplicate queue")
_expect(not _request(&"duplicate", 0, 2)[&"ok"], "actor cannot own another simultaneous destination")
_expect(_elevators.advance_one_minute(8).is_empty() and _elevators.current_minute == 0, "skipped simulation minutes are rejected")
_expect(_tick().is_empty() and _elevators.advance_one_minute(1).is_empty() and _car()[&"remaining_minutes"] == 3, "duplicate minute cannot advance cabin twice")
func _test_snapshot_rejections() -> void:
_setup()
_request(&"saved", 0, 3)
_tick(10)
var saved := _elevators.create_snapshot()
_expect(_elevators.restore_snapshot(saved), "valid active snapshot restores")
var bad := saved.duplicate(true)
bad[&"shafts"][0][&"config"][&"capacity"] = 5
_reject(bad, saved, "tampered FRONT capacity")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"config"][&"travel_minutes_per_floor"] = 1
_reject(bad, saved, "tampered floor speed")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"remaining_minutes"] -= 1
_reject(bad, saved, "tampered cabin countdown")
bad = saved.duplicate(true)
bad[&"trips"][0][&"to"] = Vector3i(0, 2, 0)
_reject(bad, saved, "tampered destination")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"boarded"] = []
_reject(bad, saved, "missing passenger seat")
bad = saved.duplicate(true)
bad[&"trips"] = []
_reject(bad, saved, "missing active trip")
bad = saved.duplicate(true)
bad[&"request_sequence"] = 0
_reject(bad, saved, "active request sequence reuse")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"powered"] = false
_reject(bad, saved, "inconsistent power and operation")
bad = saved.duplicate(true)
bad[&"unexpected"] = true
_reject(bad, saved, "unknown snapshot field")
_tick(50)
saved = _elevators.create_snapshot()
bad = saved.duplicate(true)
bad[&"request_sequence"] = 0
_reject(bad, saved, "retired request sequence reuse")
_expect(_elevators.restore_snapshot(saved), "completed trip history permits valid restore")
func _reject(bad: Dictionary, before: Dictionary, label: String) -> void:
_expect(not _elevators.restore_snapshot(bad) and _elevators.create_snapshot() == before, "%s is rejected atomically" % label)
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition: print("PASS: %s" % label)
else:
_failures += 1
push_error("FAIL: %s" % label)
@@ -0,0 +1 @@
uid://pe3usnvol833
+147
View File
@@ -0,0 +1,147 @@
extends SceneTree
var _checks := 0
var _failures := 0
var _runtime: Variant
var _clock: Variant
var _state: Variant
var _serial := 0
var _main: Control
var _saw_trip := false
var _saw_boarded := false
var _saw_arrival := false
var _saved := false
var _preview_snapshot: 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()
_runtime.initialize(_clock, _state, 424242)
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_clock.set_paused(true)
root.size = Vector2i(1440, 810)
_main = load("res://scenes/main/main.tscn").instantiate() as Control
_main.set("manual_save_path", "user://tests/transport_%d/manual" % OS.get_process_id())
root.add_child(_main)
await process_frame
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_expect(_state.get_resource(&"gold") == 5000 and _state.get_resource(&"materials") == 50, "ordinary starting budget")
if not _build(&"guest_elevator", Vector3i(0, 0, 0)) or not _build(&"reception", Vector3i(2, 0, 0)):
_finish(); return
_clock.advance_minutes(300)
_expect(_runtime.elevators.get_report()[&"shafts"].size() == 1, "completed construction registers physical cabin")
if _runtime.elevators.get_report()[&"shafts"].is_empty(): _finish(); return
if not _build(&"guest_elevator", Vector3i(0, 2, 0)) or not _build(&"standard_room", Vector3i(2, 2, 0)):
_finish(); return
_clock.advance_minutes(300)
if not _build(&"standard_room", Vector3i(2, 3, 0)):
_finish(); return
_clock.advance_minutes(60)
var report: Dictionary = _runtime.elevators.get_report()
_expect(report[&"shafts"].size() == 1 and report[&"shafts"][0][&"stops"].size() == 4, "stacked modules share one cabin across four floors")
_expect(_state.get_resource(&"materials") == 2, "buildings fit within 50 materials")
var reception: FacilityInstance
for facility: FacilityInstance in _runtime.facility_service.get_instances():
if facility.definition_id == &"reception": reception = facility
_expect(_cmd(&"hire_staff_candidate", {&"candidate_id": &"staff_akane"}).is_success(), "hire receptionist with earned starting funds")
_expect(_cmd(&"set_staff_work_assignment", {&"staff_id": &"staff_akane", &"facility_id": reception.instance_id, &"template_id": &"DAY"}).is_success(), "assign real commute")
_expect(reception.active_staff_ids.is_empty(), "assignment alone does not open reception")
for index: int in range(250):
_clock.advance_minutes(1)
var current: Dictionary = _runtime.elevators.get_report()
if not current[&"trips"].is_empty(): _saw_trip = true
for trip: Dictionary in current[&"trips"]:
if trip[&"state"] != PathService.ELEVATOR_REQUEST_BOARDED: continue
_saw_boarded = true
if not _saved:
_saved = _verify_mid_ride()
_preview_snapshot = _runtime.create_snapshot()
for guest: GuestRuntime in _runtime.guest_service.get_guests():
if guest.current_cell.y >= 2: _saw_arrival = true
if _saw_boarded and not _main.get_meta(&"captured", false) and OS.get_environment("THE_TREE_TRANSPORT_CAPTURE") == "1":
_main.set_meta(&"captured", true)
await _capture()
if index % 30 == 0: await process_frame
if not _saw_trip:
print("TRANSPORT_DIAGNOSTIC ", _runtime.elevators.get_report(), " STAFF ", _runtime.staff_commute.get_report())
for f: FacilityInstance in _runtime.facility_service.get_instances(): print("FAC ", f.definition_id, " ", f.state, " ", f.active_staff_ids)
for g: GuestRuntime in _runtime.guest_service.get_guests(): print("GUEST ", g.guest_id, " ", g.state, " ", g.current_cell, " ", _runtime._project_guest_candidates(g.guest_id))
_expect(_saw_trip, "natural guest requests an elevator")
_expect(_saw_boarded, "natural guest boards the shared cabin")
_expect(_saw_arrival, "natural guest physically reaches an upper floor")
_expect(_saved, "mid-ride replay tested")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "current completed runtime restores")
if OS.get_environment("THE_TREE_TRANSPORT_PREVIEW") == "1" and _failures == 0:
_expect(_runtime.restore_snapshot(_preview_snapshot), "preview returns to the verified first boarding")
if _failures > 0: _finish(); return
await _capture()
DisplayServer.window_set_title("The Tree · 昇降機と通勤の試遊(保存なし)")
_clock.set_paused(true)
print("Runtime transport: %d checks, %d failures" % [_checks, _failures])
print("TRANSPORT_PREVIEW_READY Space resumes; test saves disabled")
return
_finish()
func _verify_mid_ride() -> bool:
var save_path := "user://tests/transport_%d/ride" % OS.get_process_id()
_expect(bool(_runtime.save_to_path(save_path).get(&"ok", false)), "boarded save writes isolated disk slot")
_expect(bool(_runtime.load_from_path(save_path).get(&"ok", false)), "boarded disk slot reloads")
var snapshot: Dictionary = _runtime.create_snapshot()
_expect(snapshot[&"systems"][&"runtime"][&"runtime_schema_version"] == 8, "runtime schema8 records elevator state")
_expect(_runtime.restore_snapshot(snapshot), "mid-ride Runtime snapshot restores actor/cabin/Path ownership")
var shaft: Dictionary = _runtime.elevators.get_report()[&"shafts"][0]
var busy := _cmd(&"demolish_facility", {&"instance_id": shaft[&"module_ids"][1]})
_expect(not busy.is_success() and busy.details.get(&"reason") == &"ELEVATOR_BUSY", "occupied shaft demolition rejected before spending or topology changes")
_expect(_runtime.restore_snapshot(snapshot), "snapshot resets rejected command metadata")
_clock.advance_minutes(45)
var expected: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(snapshot), "rewind boarded save for deterministic replay")
_clock.advance_minutes(45)
_expect(_runtime._strict_variants_equal(expected, _runtime.create_snapshot()), "mid-ride save replays all Runtime state identically")
_expect(_runtime.restore_snapshot(snapshot), "restore original ride position after replay test")
var stopped := _cmd(&"set_elevator_operation", {&"shaft_id": shaft[&"shaft_id"], &"operator_open": false})
_expect(stopped.is_success(), "player stops elevator through public command")
_expect(_runtime.elevators.get_report()[&"trips"].is_empty(), "stop releases waiting and disembarks passengers at safe floor")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "stopped elevator save restores")
_expect(_runtime.restore_snapshot(snapshot), "resume original operating snapshot")
return true
func _capture() -> void:
var preview: Control = _main.get("world_preview")
_main.call("_on_facility_selected", _runtime.elevators.get_report()[&"shafts"][0][&"module_ids"][0])
preview.call("set_zoom", 2.0)
preview.set("pan_offset", preview.get("pan_offset") + preview.size * Vector2(0.5, 0.55) - preview.call("cell_to_canvas", Vector3i(2, 1, 0)))
preview.queue_redraw()
await process_frame
await RenderingServer.frame_post_draw
for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]:
if trip[&"state"] != PathService.ELEVATOR_REQUEST_BOARDED: continue
var key := String(trip[&"actor_id"]).replace("guest:", "guest/").replace("staff:", "staff/")
_expect((preview.get("_boarded_actor_ids") as Dictionary).has(key), "live rider identified in cabin rendering")
_expect(not (preview.get("_actor_tracks") as Dictionary).has(key), "live rider is not duplicated on floor")
var path := ProjectSettings.globalize_path("res://artifacts/transport/elevator-cabin.png")
root.get_texture().get_image().save_png(path)
func _build(id: StringName, origin: Vector3i) -> bool:
var result := _cmd(&"build_facility", {&"definition_id": id, &"origin": origin})
_expect(result.is_success(), "build %s at %s %s" % [id, origin, result.message])
return result.is_success()
func _cmd(kind: StringName, payload: Dictionary) -> CommandResult:
_serial += 1
return _runtime.submit_command(kind, payload, &"player", &"", 0, _clock.absolute_minute, StringName("transport.test.%d" % _serial))
func _expect(condition: bool, message: String) -> void:
_checks += 1
if not condition: _failures += 1
print("%s %s" % ["PASS" if condition else "FAIL", message])
func _finish() -> void:
print("Runtime transport: %d checks, %d failures" % [_checks, _failures])
quit(1 if _failures else 0)
@@ -0,0 +1 @@
uid://cityh7pucrssv
@@ -0,0 +1,175 @@
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])
@@ -0,0 +1 @@
uid://bjv4yxwqibp1n
+143
View File
@@ -0,0 +1,143 @@
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"] == 8, "Runtime emits schema8 with elevator aggregate")
var legacy := current.duplicate(true)
var data: Dictionary = legacy[&"systems"][&"runtime"]
data[&"runtime_schema_version"] = 7
data.erase(&"elevators")
data[&"path"][&"schema_version"] = 3
data[&"path"].erase(&"vertical_walk_blockers")
_expect(runtime.restore_snapshot(legacy), "genuine v7 without lifts migrates from Path3 into Runtime8")
_expect(runtime.create_snapshot() == current, "empty-lift migration preserves every existing system exactly")
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), "Runtime8 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), "Runtime8 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), "Runtime8 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[&"path"][&"schema_version"] = 3
data[&"path"].erase(&"vertical_walk_blockers")
_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])
@@ -0,0 +1 @@
uid://daygk8x6blorg
+101
View File
@@ -0,0 +1,101 @@
extends SceneTree
var _checks := 0
var _failures := 0
var _main: Control
var _runtime: Variant
var _clock: Variant
var _module_id: StringName = &""
var _shaft_id: StringName = &""
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_clock = root.get_node("GameClock")
_runtime = root.get_node("SimulationRuntime")
var state: Variant = root.get_node("GameState")
_clock.reset(1, 6, 0)
state.reset()
_runtime.initialize(_clock, state, 924263)
_clock.set_paused(true)
root.size = Vector2i(1280, 720)
_main = load("res://scenes/main/main.tscn").instantiate() as Control
_main.set("manual_save_path", "user://tests/elevator_ui_%d/manual" % OS.get_process_id())
root.add_child(_main)
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
await process_frame
await process_frame
_expect(not _main.get("selected_elevator_controls").visible, "empty inspector hides lift controls")
_main.call("_select_build_definition", &"guest_elevator")
_main.call("_on_map_cell_clicked", Vector3i(0, 0, 0))
_expect(_runtime.construction_service.get_jobs().size() == 1, "normal build UI pays for lift construction")
_clock.advance_minutes(300)
_main.call("_clear_build_selection")
var report: Dictionary = _runtime.elevators.get_report()
_expect(report[&"shafts"].size() == 1, "completed paid lift registers one real car")
if report[&"shafts"].size() != 1:
_finish()
return
_shaft_id = report[&"shafts"][0][&"shaft_id"]
_module_id = report[&"shafts"][0][&"module_ids"][0]
_main.call("_on_facility_selected", _module_id)
_expect(_main.get("selected_elevator_controls").visible and _main.get("management_panel").visible, "selecting finished lift opens existing inspector controls")
_expect(_main.get("selected_heading_label").text == "客用昇降機", "proxy lift gets localized title instead of syncing placeholder")
var detail: String = _main.get("selected_detail_label").text
_expect("現在階" in detail and "目的階" in detail and "乗客" in detail and "待機" in detail and "1F 〜 2F" in detail, "inspector displays real cabin floors, occupancy, queue and stops")
var button: Button = _main.get("elevator_operation_button")
_expect(not button.disabled and button.text == "昇降機を停止", "idle powered car exposes stop command")
button.pressed.emit()
_expect(not _shaft()[&"operator_open"] and button.text == "運行を再開", "paused stop applies real operator state and swaps button")
_expect("手動停止" in _main.get("selected_detail_label").text, "stopped inspector state is explicit")
_expect(_runtime.path_service.find_path(Vector3i(0, 0, 0), Vector3i(0, 1, 0)).is_empty(), "UI stop removes usable ride route while shaft blocks climbing")
button.pressed.emit()
_expect(_shaft()[&"operator_open"] and bool(_shaft()[&"operational"]) == bool(_shaft()[&"powered"]), "UI resume respects actual cabin power availability")
_expect(bool(_shaft()[&"powered"]) or "魔力供給なし" in _main.get("selected_detail_label").text, "unpowered cabin cannot appear as running after operator resume")
_clock.set_paused(false)
button.pressed.emit()
_expect(button.disabled and _runtime.command_queue.pending_count() == 1, "running-clock command locks further submission")
_main.call("_on_elevator_operation_pressed")
_expect(_runtime.command_queue.pending_count() == 1, "repeated stop press cannot enqueue duplicate command")
_main.call("_save_manual")
_clock.advance_minutes(1)
_expect(not _shaft()[&"operator_open"], "queued command applies at next simulation minute")
_main.call("_load_manual")
_main.call("_on_facility_selected", _module_id)
_expect(_runtime.command_queue.pending_count() == 1 and button.disabled, "manual load restores queued lift command and disables duplicate submission")
_clock.advance_minutes(1)
_clock.set_paused(true)
_expect(not _shaft()[&"operator_open"] and not button.disabled, "restored command applies exactly once and unlocks control")
button.pressed.emit()
_expect(_shaft()[&"operator_open"], "post-load UI uses fresh idempotency sequence and can resume")
_main.call("_on_map_cell_clicked", Vector3i(10, 0, 0))
_expect(not _main.get("selected_elevator_controls").visible, "selecting empty ground removes obsolete operation control")
_main.call("_on_facility_selected", _module_id)
await process_frame
await process_frame
var panel_rect: Rect2 = _main.get("management_panel").get_global_rect()
_expect(panel_rect.encloses(button.get_global_rect()) and _main.get_global_rect().encloses(panel_rect), "operation button remains visible inside inspector at a 1280 by 720 window")
if OS.get_environment("THE_TREE_ELEVATOR_UI_CAPTURE") == "1":
await RenderingServer.frame_post_draw
var image_value := root.get_texture().get_image()
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path("res://artifacts/transport"))
image_value.save_png("res://artifacts/transport/elevator-inspector.png")
_finish()
func _shaft() -> Dictionary:
for row: Dictionary in _runtime.elevators.get_report()[&"shafts"]:
if row[&"shaft_id"] == _shaft_id:
return row
return {}
func _finish() -> void:
_clock.set_paused(true)
print("Elevator management UI: %d checks, %d failures" % [_checks, _failures])
quit(_failures)
func _expect(ok: bool, label: String) -> void:
_checks += 1
if not ok:
_failures += 1
push_error(label)
+1
View File
@@ -0,0 +1 @@
uid://cl4r811l5vs7i
+4
View File
@@ -69,6 +69,10 @@ func _run() -> void:
_panel.apply_button.pressed.emit()
_expect(_assignment(&"staff_akane").get(&"facility_id") == _receptions[0],
"paused Apply assigns the receptionist to the chosen completed facility")
_expect(_runtime.staff_service.get_staff(&"staff_akane").state == StaffRuntime.State.COMMUTING
and _runtime.facility_service.get_instance(_receptions[0]).active_staff_ids.is_empty(),
"paused Apply visibly starts a commute and does not invent facility attendance")
_expect("出勤中" in _panel.staff_detail_label.text, "staff panel distinguishes physical commuting from active duty")
_expect("変更しました" in _panel.result_label.text and "毎日" in _panel.staff_detail_label.text,
"assignment success and recurring schedule are visible in Japanese")
_test_rejected_assignment()
+204
View File
@@ -0,0 +1,204 @@
extends SceneTree
const PathType = preload("res://scripts/world/path_service.gd")
const ProjectorType = preload("res://scripts/world/facility_infrastructure_projector.gd")
const AdapterType = preload("res://scripts/world/route_subslot_path_adapter.gd")
const SpatialType = preload("res://scripts/world/spatial_subslot_service.gd")
const GUEST = preload("res://data/facilities/guest_elevator.tres")
const SERVICE = preload("res://data/facilities/service_elevator.tres")
class ControlledPath:
extends PathService
var fail_set := false
var fail_remove := false
func set_vertical_walk_blockers(owner_id: StringName, cells: Array[Vector3i]) -> bool:
var result := super.set_vertical_walk_blockers(owner_id, cells)
if fail_set:
fail_set = false
return false
return result
func remove_vertical_walk_blockers(owner_id: StringName) -> bool:
var result := super.remove_vertical_walk_blockers(owner_id)
if fail_remove:
fail_remove = false
return false
return result
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_test_static_shaft_and_cabin_edges()
_test_owner_boundaries_and_cache()
_test_snapshot_validation_and_legacy()
_test_projection_and_rollback()
print("Elevator topology: %d checks, %d failures" % [_checks, _failures])
quit(_failures)
func _test_static_shaft_and_cabin_edges() -> void:
var path := PathType.new()
var cells: Array[Vector3i] = [Vector3i(-1, 0, 0), Vector3i(0, -1, 0), Vector3i(0, 0, 0), Vector3i(0, 1, 0), Vector3i(0, 2, 0), Vector3i(1, 0, 0), Vector3i(1, 1, 0), Vector3i(2, 1, 0)]
_expect(path.rebuild_graph(cells), "materialize ordinary graph")
_expect(not path.find_path(Vector3i(-1, 0, 0), Vector3i(2, 1, 0)).is_empty(), "legacy plain corridor graph still walks vertically")
_expect(path.set_vertical_walk_blockers(&"shaft.a", [Vector3i(0, 0, 0), Vector3i(0, 1, 0), Vector3i(1, 0, 0), Vector3i(1, 1, 0)]), "mark entire two-column shaft")
_expect(path.find_path(Vector3i(-1, 0, 0), Vector3i(2, 1, 0)).is_empty(), "neither shaft column admits ordinary climbing")
_expect(path.last_no_route_reason == path.ROUTE_GOAL_UNREACHABLE, "diagnostic graph cannot invent walk bypass")
_expect(path.find_path(Vector3i(0, -1, 0), Vector3i(0, 0, 0)).is_empty(), "edge touching bottom of shaft is suppressed")
_expect(path.find_path(Vector3i(0, 1, 0), Vector3i(0, 2, 0)).is_empty(), "edge touching top of shaft is suppressed")
_expect(path.find_path(Vector3i(-1, 0, 0), Vector3i(1, 0, 0)).size() == 3, "horizontal floor access through shaft remains")
_expect(path.configure_elevator(&"transport.a", Vector3i(0, 0, 0), Vector3i(0, 1, 0)), "separate cabin connector enables rides")
_expect(not path.find_path(Vector3i(-1, 0, 0), Vector3i(2, 1, 0)).is_empty(), "cabin restores reachable vertical route")
var edge := path.get_edge_report(Vector3i(0, 0, 0), Vector3i(0, 1, 0))
_expect(edge.get(&"movement_type") == path.MOVEMENT_ELEVATOR and edge.get(&"connector_id") == &"transport.a", "movement driver sees elevator edge")
_expect(path.get_step_connector(Vector3i(0, 1, 0), Vector3i(0, 0, 0)) == &"transport.a", "reverse ride resolves same bidirectional connector")
_expect(path.get_step_connector(Vector3i(-1, 0, 0), Vector3i(0, 0, 0)).is_empty(), "floor step has no cabin owner")
edge[&"connector_id"] = &"forged"
_expect(path.get_step_connector(Vector3i(0, 0, 0), Vector3i(0, 1, 0)) == &"transport.a", "edge report is defensive")
_expect(path.set_connector_operational_state(&"transport.a", path.CONNECTOR_STOPPED), "stop car without removing shaft")
_expect(path.find_path(Vector3i(-1, 0, 0), Vector3i(2, 1, 0)).is_empty(), "stopped car cannot be bypassed")
_expect(path.last_no_route_reason == path.ROUTE_CONNECTOR_UNAVAILABLE, "diagnostic recognizes unavailable car")
_expect(path.get_edge_report(Vector3i(0, 0, 0), Vector3i(0, 1, 0)).is_empty(), "stopped car has no usable step")
_expect(path.remove_connector(&"transport.a"), "remove transport connector independently")
_expect(path.find_path(Vector3i(0, 0, 0), Vector3i(0, 1, 0)).is_empty(), "removing cabin retains shaft walk suppression")
_expect(path.configure_elevator(&"transport.staff", Vector3i(0, 0, 0), Vector3i(0, 1, 0), {}, &"staff"), "configure service-only cabin")
_expect(path.find_path(Vector3i(0, 0, 0), Vector3i(0, 1, 0), [0], []).is_empty(), "guest cannot use service cabin")
_expect(path.get_step_connector(Vector3i(0, 0, 0), Vector3i(0, 1, 0), [&"staff"]) == &"transport.staff", "staff access identifies service cabin")
func _test_owner_boundaries_and_cache() -> void:
var path := PathType.new()
var bottom := Vector3i(0, 0, 0)
var top := Vector3i(0, 1, 0)
path.rebuild_graph([bottom, top])
path.find_path(bottom, top)
var revision := path.graph_revision
_expect(path.cached_path_count() > 0, "seed walk path cache")
_expect(path.set_vertical_walk_blockers(&"owner.a", [bottom]), "first owner blocks one endpoint")
_expect(path.graph_revision > revision and path.cached_path_count() == 0, "blocker edit invalidates cached routes")
var snapshot := path.create_snapshot()
_expect(path.set_vertical_walk_blockers(&"owner.a", [bottom]) and path.create_snapshot() == snapshot, "same owner configuration is idempotent")
_expect(path.set_vertical_walk_blockers(&"owner.b", [bottom]), "independent owner can overlap")
_expect(path.remove_vertical_walk_blockers(&"owner.a") and path.find_path(bottom, top).is_empty(), "removing one owner retains other's restriction")
var defensive := path.get_vertical_walk_blockers()
defensive[&"owner.b"].clear()
_expect(path.find_path(bottom, top).is_empty(), "blocker read model cannot mutate graph")
_expect(path.remove_vertical_walk_blockers(&"owner.b") and path.find_path(bottom, top) == [bottom, top], "last owner removal restores ordinary topology")
snapshot = path.create_snapshot()
_expect(not path.set_vertical_walk_blockers(&"", [bottom]), "reject empty owner")
_expect(not path.set_vertical_walk_blockers(&" bad", [bottom]), "reject noncanonical owner")
_expect(not path.set_vertical_walk_blockers(&"owner", []), "empty replacement must use explicit removal")
_expect(not path.set_vertical_walk_blockers(&"owner", [bottom, bottom]), "reject duplicate owned cell")
_expect(not path.set_vertical_walk_blockers(&"owner", [Vector3i(4, 4, 0)]), "reject unknown graph cell")
_expect(not path.remove_vertical_walk_blockers(&"unknown"), "reject removing unknown owner")
_expect(path.create_snapshot() == snapshot, "invalid blocker mutations leave full state unchanged")
func _test_snapshot_validation_and_legacy() -> void:
var path := PathType.new()
var cells: Array[Vector3i] = [Vector3i(0, 0, 0), Vector3i(0, 1, 0), Vector3i(1, 0, 0)]
path.rebuild_graph(cells)
path.set_vertical_walk_blockers(&"shaft.a", [cells[0], cells[1]])
path.set_vertical_walk_blockers(&"shaft.b", [cells[2]])
var snapshot := path.create_snapshot()
_expect(snapshot[&"schema_version"] == 4, "Path v4 declares shaft ownership")
var restored := PathType.new()
_expect(restored.restore_snapshot(snapshot) and restored.rebuild_graph(cells), "restore shaft topology before authoritative graph materialization")
_expect(restored.create_snapshot() == snapshot and restored.find_path(cells[0], cells[1]).is_empty(), "round trip restores exact restrictions and revision")
var malformed_cases: Array[Dictionary] = []
var missing := snapshot.duplicate(true)
missing.erase(&"vertical_walk_blockers")
malformed_cases.append(missing)
var owner_duplicate := snapshot.duplicate(true)
owner_duplicate[&"vertical_walk_blockers"].append(owner_duplicate[&"vertical_walk_blockers"][0].duplicate(true))
malformed_cases.append(owner_duplicate)
var wrong_order := snapshot.duplicate(true)
wrong_order[&"vertical_walk_blockers"].reverse()
malformed_cases.append(wrong_order)
var wrong_cells := snapshot.duplicate(true)
wrong_cells[&"vertical_walk_blockers"][0][&"cells"].reverse()
malformed_cases.append(wrong_cells)
var unknown_cell := snapshot.duplicate(true)
unknown_cell[&"vertical_walk_blockers"][0][&"cells"] = [Vector3i(999, 999, 0)]
malformed_cases.append(unknown_cell)
var duplicate_cell := snapshot.duplicate(true)
duplicate_cell[&"vertical_walk_blockers"][0][&"cells"] = [cells[0], cells[0]]
malformed_cases.append(duplicate_cell)
var unknown_field := snapshot.duplicate(true)
unknown_field[&"vertical_walk_blockers"][0][&"extra"] = true
malformed_cases.append(unknown_field)
var empty_cells := snapshot.duplicate(true)
empty_cells[&"vertical_walk_blockers"][0][&"cells"] = []
malformed_cases.append(empty_cells)
for malformed: Dictionary in malformed_cases:
_expect(not restored.restore_snapshot(malformed), "reject malformed shaft ownership snapshot")
_expect(restored.create_snapshot() == snapshot, "snapshot rejection is atomic")
for version: int in [1, 2, 3]:
var legacy := snapshot.duplicate(true)
legacy[&"schema_version"] = version
_expect(not restored.restore_snapshot(legacy), "old version with retained shaft field cannot disable safety")
legacy.erase(&"vertical_walk_blockers")
_expect(restored.restore_snapshot(legacy), "true legacy snapshot lacking shaft field migrates")
_expect(restored.get_vertical_walk_blockers().is_empty() and not restored.find_path(cells[0], cells[1]).is_empty(), "legacy plain topology remains compatible")
_expect(restored.restore_snapshot(snapshot), "return to current shaft snapshot")
func _test_projection_and_rollback() -> void:
var context := _context()
var guest := _instance(GUEST, &"guest", Vector3i(0, 0, 0))
var service := _instance(SERVICE, &"service", Vector3i(4, 0, 1))
_expect(context.projector.project_facility(&"project.guest", GUEST, guest).is_success(), "project guest elevator ROUTE footprint")
_expect(context.projector.project_facility(&"project.service", SERVICE, service).is_success(), "project service elevator ROUTE footprint")
_expect(context.source.get_route_cells().size() == 10, "all occupied 2x2 and 3x2 cells are floor-accessible")
_expect(context.projector.get_projection(&"guest")[&"connector_id"] == &"", "projector owns static shaft separately from transport connector")
_expect(context.projector.validate_invariants()[&"ok"], "shaft owner and record crosslinks match")
var saved := _snapshots(context)
_expect(context.projector.restore_snapshot(saved.projector) and context.projector.create_snapshot() == saved.projector, "current elevator projection snapshot roundtrip preserves history and static ownership")
context.path.remove_vertical_walk_blockers(&"infra.shaft:guest")
_expect(context.projector.is_projection_stale(), "missing shaft owner makes infrastructure stale")
_expect(not context.projector.restore_snapshot(saved.projector), "metadata restore cannot accept missing static shaft restriction")
_expect(context.path.restore_snapshot(saved.path), "repair exact shaft state")
_expect(context.projector.validate_invariants()[&"ok"], "restored shaft repairs infrastructure crosslink")
var orphan_cells: Array[Vector3i] = [Vector3i(0, 0, 0)]
context.path.set_vertical_walk_blockers(&"infra.shaft:orphan", orphan_cells)
_expect(context.projector.is_projection_stale(), "untracked managed shaft owner rejected")
context.path.remove_vertical_walk_blockers(&"infra.shaft:orphan")
_expect(context.projector.unproject_facility(&"remove.guest", &"guest").is_success(), "unproject guest shaft with its route")
_expect(not context.path.get_vertical_walk_blockers().has(&"infra.shaft:guest") and not context.path.has_cell(Vector3i(0, 0, 0)), "demolition removes static owner and route cells")
_expect(context.path.get_vertical_walk_blockers().has(&"infra.shaft:service") and context.projector.validate_invariants()[&"ok"], "independent service shaft remains intact")
var controlled := ControlledPath.new()
var failure_context := _context(controlled)
var before := _snapshots(failure_context)
controlled.fail_set = true
_expect(not failure_context.projector.project_facility(&"fail.project", GUEST, guest).is_success(), "injected shaft mutation failure rejects project")
_expect(_snapshots(failure_context) == before, "failed shaft project restores source, route adapter, Path, and metadata exactly")
_expect(failure_context.projector.project_facility(&"fail.project", GUEST, guest).is_success(), "failed transaction leaves request id available")
before = _snapshots(failure_context)
controlled.fail_remove = true
_expect(not failure_context.projector.unproject_facility(&"fail.remove", &"guest").is_success(), "injected shaft removal failure rejects demolition")
_expect(_snapshots(failure_context) == before, "failed shaft removal restores every dependency exactly")
_expect(failure_context.projector.unproject_facility(&"fail.remove", &"guest").is_success(), "failed demolition remains retryable")
func _context(path_value: PathService = null) -> Dictionary:
var source := SpatialType.new()
var path := path_value if path_value != null else PathType.new()
var adapter := AdapterType.new()
_expect(adapter.configure(source, path, []), "configure route adapter")
var projector := ProjectorType.new()
_expect(projector.configure(adapter, source, path), "configure infrastructure projector")
return {&"source": source, &"path": path, &"adapter": adapter, &"projector": projector}
func _instance(definition: FacilityDefinition, instance_id: StringName, origin: Vector3i) -> Dictionary:
var cells: Array[Vector3i] = []
for y: int in range(definition.footprint.y):
for x: int in range(definition.footprint.x):
cells.append(origin + Vector3i(x, y, 0))
return {&"instance_id": instance_id, &"definition_id": definition.id, &"origin": origin, &"rotation": 0, &"layer": definition.layer, &"occupied_cells": cells, &"construction_cost": definition.construction_cost.duplicate(true), &"source_job_id": StringName("build." + String(instance_id)), &"state": &"ACTIVE", &"completed_at": 300}
func _snapshots(context: Dictionary) -> Dictionary:
return {&"source": context.source.create_snapshot(), &"path": context.path.create_snapshot(), &"adapter": context.adapter.create_snapshot(), &"projector": context.projector.create_snapshot()}
func _expect(ok: bool, label: String) -> void:
_checks += 1
if not ok:
_failures += 1
push_error(label)
@@ -0,0 +1 @@
uid://b54qfpjj4idli