import assert from "node:assert/strict"; import { createHash } from "node:crypto"; import { writeFile } from "node:fs/promises"; import { MemoryStorage, WorkspaceJournal } from "./journal.mjs"; import { createCheckpoint } from "./recovery.mjs"; import { ReplayTimeline, canonicalJson, fnv1a64 } from "../worker/replay.mjs"; import { applyRollbackPackage, createOfflinePackage, createRollbackPackage, createWorkspacePackage, migrateWorkspacePackage, verifyOfflinePackage, verifyWorkspacePackage, } from "./migration.mjs"; const contextId = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"; function timelineAt(events) { const timeline = new ReplayTimeline(); timeline.appendEvent(1, 0, "SESSION_READY", {}); timeline.checkpoint(1, 0, { tick: 0, position: 0 }); if (events) { timeline.appendEvent(2, 4, "SIM_STEP", { cycles: 4 }); timeline.checkpoint(2, 4, { tick: 4, position: 4 }, timeline.snapshots.latest().snapshot_id); } return timeline; } const storage = new MemoryStorage(); const journal = new WorkspaceJournal(storage, { projectId: "demo" }); journal.prepare("op-1", [{ path: "config/main.ini", data: "[EMC]\n" }]); journal.commit("op-1"); const journalRecords = journal.records(); journal.prepare("op-2", [{ path: "programs/main.ngc", data: "G0 X1\n" }]); journal.commit("op-2"); const currentJournalRecords = journal.records(); const committedTimeline = timelineAt(false); const committedCheckpoint = createCheckpoint({ contextId, timeline: committedTimeline, state: { tick: 0, position: 0 }, workspaceHash: fnv1a64(journalRecords), }); const currentTimeline = timelineAt(true); const currentCheckpoint = createCheckpoint({ contextId, timeline: currentTimeline, state: { tick: 4, position: 4 }, workspaceHash: fnv1a64(currentJournalRecords), }); const committed = createWorkspacePackage({ contextId, timeline: committedTimeline, checkpoint: committedCheckpoint, journal: journalRecords, committedSeq: 1 }); const current = createWorkspacePackage({ contextId, timeline: currentTimeline, checkpoint: currentCheckpoint, journal: currentJournalRecords, committedSeq: 2 }); assert.equal(verifyWorkspacePackage(committed, { contextId }).package_hash, committed.package_hash); assert.equal(verifyWorkspacePackage(current, { contextId }).package_hash, current.package_hash); assert.deepEqual(createWorkspacePackage({ contextId, timeline: currentTimeline, checkpoint: currentCheckpoint, journal: currentJournalRecords, committedSeq: 2 }), current); const migrated = migrateWorkspacePackage(current, 2); assert.equal(migrated.format_version, 2); assert.equal(migrated.migration.lossless, true); assert.equal(verifyWorkspacePackage(migrated, { contextId, expectedVersion: 2 }).format_version, 2); const roundTrip = migrateWorkspacePackage(migrated, 1); assert.deepEqual(roundTrip, current); assert.throws(() => migrateWorkspacePackage(current, 3), /package_version_unsupported/); assert.throws(() => verifyWorkspacePackage(current, { contextId: "other-context" }), /package_context_mismatch/); const tampered = structuredClone(migrated); tampered.timeline.events.events[1].payload.cycles = 99; assert.throws(() => verifyWorkspacePackage(tampered), /(?:package_(?:content_)?hash_mismatch|event_payload_hash_mismatch)/); const rollback = createRollbackPackage({ current, committed }); assert.equal(rollback.nearest_committed_seq, 1); assert.throws(() => applyRollbackPackage(rollback, { currentPackageHash: current.package_hash, failureInjection: "before_commit" }), /rollback_injected_failure/); const rolledBack = applyRollbackPackage(rollback, { currentPackageHash: current.package_hash }); assert.deepEqual(rolledBack, committed); assert.equal(current.package_hash, current.package_hash); const badRollback = structuredClone(rollback); badRollback.target_package_hash = "fnv1a64:0000000000000000"; assert.throws(() => applyRollbackPackage(badRollback, { currentPackageHash: current.package_hash }), /rollback_hash_mismatch/); const offline = createOfflinePackage({ workspacePackage: migrated, resources: [ { path: "ui/index.html", data: "
LinuxCNC
" }, { path: "wasm/sim.wasm", data: "wasm-bytes" }, ], }); assert.deepEqual(verifyOfflinePackage(offline, { contextId, workspacePackage: migrated, available: [{ path: "ui/index.html", data: "
LinuxCNC
" }, { path: "wasm/sim.wasm", data: "wasm-bytes" }] }), { status: "ready", missing: [], corrupted: [], offline: true }); assert.throws(() => verifyOfflinePackage(offline, { contextId, available: ["ui/index.html"], online: false }), /offline_resource_invalid/); assert.deepEqual(verifyOfflinePackage(offline, { contextId, available: ["ui/index.html"], online: true }), { status: "network_required", missing: ["wasm/sim.wasm"], corrupted: [], offline: false }); const report = { schema: "workspace-migration-report.v1", context_id: contextId, status: "passed", implementation_status: "verified", format: { schema: "workspace-package.v1", supported_versions: [1, 2], payload_schemas_unchanged: ["event-log.v1", "snapshot.v1", "simulation-checkpoint.v1", "workspace-journal.v1"], old_package_immutable: true, }, migration: { status: "passed", source_version: current.format_version, target_version: migrated.format_version, source_hash: current.package_hash, migrated_hash: migrated.package_hash, round_trip_hash: roundTrip.package_hash, round_trip_equal: roundTrip.package_hash === current.package_hash, deterministic: migrated.package_hash === migrateWorkspacePackage(current, 2).package_hash, }, rollback: { status: "passed", schema: rollback.schema, current_hash: current.package_hash, target_hash: committed.package_hash, nearest_committed_seq: rollback.nearest_committed_seq, rollback_hash: rollback.rollback_hash, failure_injection: "before_commit", failure_result: "rollback_injected_failure", recovery_status: "recovered", recovery_hash: rolledBack.package_hash, }, offline: { status: "passed", schema: offline.schema, bundle_hash: offline.bundle_hash, required_paths: offline.required_paths, offline_start: "ready", missing_resource_result: "offline_resource_invalid", online_degraded_result: "network_required", package_path: "coverage/offline-cache.package.json", }, rollback_package_path: "coverage/workspace-rollback.package.json", recovery_endpoint: { status: "verified", endpoint: "nearest_committed_package", state_hash: committedCheckpoint.state_hash, no_silent_downgrade: true, }, oracle: { status: "blocked", reason: "official migration and browser offline oracle is absent from the locked source", blocked_samples: ["native/WASM package migration", "browser Service Worker offline startup", "OPFS rollback after crash"], }, }; report.report_sha256 = createHash("sha256").update(canonicalJson(report), "utf8").digest("hex"); await writeFile(new URL("../../coverage/workspace-rollback.package.json", import.meta.url), `${JSON.stringify(rollback, null, 2)}\n`); await writeFile(new URL("../../coverage/offline-cache.package.json", import.meta.url), `${JSON.stringify(offline, null, 2)}\n`); await writeFile(new URL("../../coverage/workspace-migration.report.json", import.meta.url), `${JSON.stringify(report, null, 2)}\n`); console.log(`migration=passed source=${current.package_hash} target=${migrated.package_hash} rollback=${rollback.rollback_hash}`);