Implement AXIS parity UI and execution baseline
This commit is contained in:
78
web/worker/test_contracts.mjs
Normal file
78
web/worker/test_contracts.mjs
Normal file
@@ -0,0 +1,78 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import createModule from "../../build-wasm/linuxcnc-sim-core.js";
|
||||
import { SimSession, CONTEXT_ID } from "./sim-session.mjs";
|
||||
import { ReplayTimeline, fnv1a64 } from "./replay.mjs";
|
||||
import { createWorkspacePackage, migrateWorkspacePackage } from "../workspace/migration.mjs";
|
||||
import {
|
||||
SCHEMAS,
|
||||
validateCommandEnvelope,
|
||||
validateEventLog,
|
||||
validateSimTask,
|
||||
validateSnapshot,
|
||||
validateWorkspacePackage,
|
||||
} from "./contracts.mjs";
|
||||
|
||||
const wasmBinary = await readFile(new URL("../../build-wasm/linuxcnc-sim-core.wasm", import.meta.url));
|
||||
const core = await createModule({ wasmBinary });
|
||||
const sessionId = "contract-session";
|
||||
const base = { schema_version: 1, context_id: CONTEXT_ID, session_id: sessionId };
|
||||
|
||||
function command(kind, seq, payload = {}, operation_id = undefined) {
|
||||
return { ...base, kind, seq, ...(operation_id ? { operation_id } : {}), payload };
|
||||
}
|
||||
|
||||
validateCommandEnvelope(command("HELLO", 1), { contextId: CONTEXT_ID, sessionId });
|
||||
validateCommandEnvelope({ ...command("PING", 2), sent_at_monotonic_ms: 12.5 });
|
||||
validateCommandEnvelope(command("X".repeat(128), 2));
|
||||
for (const invalid of [
|
||||
{ ...command("PING", 2), schema_version: 2 },
|
||||
{ ...command("PING", 2), seq: 0 },
|
||||
{ ...command("PING", 2), payload: [] },
|
||||
{ ...command("PING", 2), payload: { value: Number.NaN } },
|
||||
{ ...command("PING", 2), unknown: true },
|
||||
command("SIM_STEP", 2, { cycles: 1 }),
|
||||
command("X".repeat(129), 2),
|
||||
]) {
|
||||
assert.throws(() => validateCommandEnvelope(invalid), /^Error: contract_/);
|
||||
}
|
||||
assert.throws(() => validateCommandEnvelope(command("PING", 2), { contextId: "other" }), /contract_command_context_mismatch/);
|
||||
|
||||
const taskUnsigned = {
|
||||
schema: SCHEMAS.task,
|
||||
context_id: CONTEXT_ID,
|
||||
session_id: sessionId,
|
||||
state: "READY",
|
||||
mode: "MANUAL",
|
||||
program_status: "idle",
|
||||
seq: 2,
|
||||
simulation_tick: 0,
|
||||
current_line: null,
|
||||
};
|
||||
validateSimTask({ ...taskUnsigned, state_hash: fnv1a64(taskUnsigned) }, { contextId: CONTEXT_ID, sessionId });
|
||||
assert.throws(() => validateSimTask({ ...taskUnsigned, state_hash: "fnv1a64:0000000000000000" }), /contract_task_hash/);
|
||||
assert.throws(() => validateSimTask({ ...taskUnsigned, state: "UNKNOWN", state_hash: fnv1a64(taskUnsigned) }), /contract_task_state/);
|
||||
|
||||
const session = new SimSession(core, { sessionId });
|
||||
assert.equal(session.accept(command("HELLO", 1)).kind, "CAPABILITIES");
|
||||
assert.equal(session.accept(command("SESSION_ATTACH", 2, {}, "attach")).kind, "SESSION_READY");
|
||||
assert.equal(session.accept(command("LOAD_WORKSPACE", 3, { root_path: "/workspace" }, "load")).payload.status, "verified");
|
||||
const stepped = session.accept(command("SIM_STEP", 4, { cycles: 5 }, "step"));
|
||||
assert.equal(stepped.simulation_tick, 5);
|
||||
validateSnapshot(stepped.payload.snapshot);
|
||||
const eventLog = session.timeline.events.export();
|
||||
validateEventLog(eventLog);
|
||||
assert.equal(core._sim_protocol_last_seq(), eventLog.cursor);
|
||||
assert.equal(core._sim_event_count(), eventLog.events.length);
|
||||
assert.equal(core._sim_protocol_accept(2, 5, "PING", "", 5), -100);
|
||||
|
||||
const timeline = new ReplayTimeline(session.timeline.export());
|
||||
const workspace = createWorkspacePackage({ contextId: CONTEXT_ID, timeline, committedSeq: eventLog.cursor });
|
||||
validateWorkspacePackage(workspace, { contextId: CONTEXT_ID });
|
||||
const migrated = migrateWorkspacePackage(workspace, 2);
|
||||
validateWorkspacePackage(migrated, { contextId: CONTEXT_ID });
|
||||
assert.throws(() => validateWorkspacePackage({ ...workspace, package_hash: "fnv1a64:0000000000000000" }), /contract_workspace_package_hash/);
|
||||
assert.throws(() => validateWorkspacePackage({ ...workspace, committed_seq: Number.POSITIVE_INFINITY }), /contract_workspace_committed_seq/);
|
||||
assert.equal(migrateWorkspacePackage(migrated, 1).package_hash, workspace.package_hash);
|
||||
|
||||
console.log(`contracts=passed schemas=${Object.values(SCHEMAS).join(",")} tick=${stepped.simulation_tick} cursor=${eventLog.cursor}`);
|
||||
Reference in New Issue
Block a user