Files
linuxcncWebWASM/web/worker/test_recovery_s3.mjs

36 lines
3.2 KiB
JavaScript

import assert from "node:assert/strict";
import { MemoryStorage, WorkspaceJournal } from "../workspace/journal.mjs";
import { createWorkspacePackage } from "../workspace/migration.mjs";
import { ReplayTimeline, fnv1a64 } from "./replay.mjs";
import { RecoveryRuntime } from "./recovery-runtime.mjs";
const contextId = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1";
const timeline = new ReplayTimeline();
timeline.appendEvent(1, 0, "SESSION_READY", {});
timeline.checkpoint(1, 0, { tick: 0, position: 0 });
const journalStorage = new MemoryStorage();
const journal = new WorkspaceJournal(journalStorage, { projectId: "recovery-s3" });
journal.prepare("commit-1", [{ path: "config/main.ini", data: "[EMC]\n" }]);
journal.commit("commit-1");
const runtime = new RecoveryRuntime({ contextId });
const saved = runtime.saveCheckpoint({ timeline, state: { tick: 0, position: 0 }, operations: [{ operation_id: "pending", status: "awaiting_ack", seq: 1, kind: "SIM_STEP" }] });
assert.equal(saved.status, "verified");
assert.equal(runtime.markCrash().status, "verified");
const events = [{ seq: 2, tick: 4, kind: "SIM_STEP", payload: { cycles: 4 }, payload_hash: fnv1a64({ cycles: 4 }) }];
const recovered = runtime.restart({ events, reconcile: (operation) => ({ ...operation, status: "aborted" }), apply: (state, event) => ({ tick: event.tick, position: state.position + event.payload.cycles }) });
assert.equal(recovered.status, "verified");
assert.equal(recovered.recovery.status, "aborted");
assert.equal(recovered.recovery.state.position, 4);
assert.equal(recovered.recovery.state_hash, fnv1a64(recovered.recovery.state));
const packageValue = createWorkspacePackage({ contextId, timeline: new ReplayTimeline({ schema: "replay-timeline.v1", events: { schema: "event-log.v1", cursor: 1, tick: 0, events: timeline.events.events.slice(0, 1) }, snapshots: { schema: "snapshot.v1", snapshots: [timeline.snapshots.latest()] } }), checkpoint: saved.checkpoint, journal: journal.records(), committedSeq: 1 });
assert.equal(runtime.createOffline(packageValue, [{ path: "ui/index.html", data: "<main>offline</main>" }, { path: "wasm/sim.wasm", data: "wasm" }]).status, "verified");
assert.equal(runtime.verifyOffline([{ path: "ui/index.html", data: "<main>offline</main>" }, { path: "wasm/sim.wasm", data: "wasm" }], { workspacePackage: packageValue }).result.status, "ready");
assert.equal(runtime.verifyOffline(["ui/index.html"], { workspacePackage: packageValue, online: true }).result.status, "network_required");
assert.equal(runtime.createOffline(packageValue, [{ path: "../escape", data: "bad" }]).code, "offline_resource_path");
const quotaJournal = new WorkspaceJournal(new MemoryStorage(), { projectId: "quota", maxBytes: 3 });
assert.throws(() => quotaJournal.prepare("too-large", [{ path: "a", data: "1234" }]), /workspace_journal_quota/);
const restored = new RecoveryRuntime({ contextId });
assert.deepEqual(restored.restore(runtime.export()), runtime.export());
console.log(JSON.stringify({ schema: "recovery-s3-sample.v1", status: "verified", crash_count: runtime.state.crash_count, restart_count: runtime.state.restart_count, recovery_hash: runtime.state.recovery_hash, package_hash: runtime.state.package_hash, state_hash: runtime.export().state_hash }));