extends SceneTree var _checks := 0 var _failures := 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: print("P1: Runtime v8 supply save migration") var source := _fixture(7301) var first := _fixture(7302) var second := _fixture(7303) var source_runtime: Variant = source[&"runtime"] var first_runtime: Variant = first[&"runtime"] var second_runtime: Variant = second[&"runtime"] 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"] == 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 v8") var migrated: Dictionary = first_runtime.create_snapshot() _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 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), "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 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") var bad_version := canonical.duplicate(true) bad_version[&"systems"][&"runtime"][&"kitchen_production"][&"version"] = 2 _expect(_rejects_atomically(first_runtime, bad_version), "unknown nested production schema is rejected") var stock: Dictionary = source_runtime.inventory_service.add_stack(&"supplier.legacy", SupplyCatalog.RAW_FOOD, 4) _expect(stock[&"ok"], "orphan migration fixture has raw food") var hold: Dictionary = source_runtime.inventory_service.reserve_item( &"supplier.legacy", SupplyCatalog.RAW_FOOD, 4, &"kitchen.batch:00000001", SupplyCatalog.PRODUCTION_PURPOSE, source[&"clock"].absolute_minute ) _expect(hold[&"ok"], "orphan fixture creates a production reservation") 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() fixture[&"clock"].free() fixture[&"state"].free() if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) 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"], "migration fixture initializes") clock.set_paused(true) return {&"clock": clock, &"state": state, &"runtime": runtime} func _rejects_atomically(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, message: String) -> void: _checks += 1 if not condition: _failures += 1 push_error(message)