import assert from "node:assert/strict"; import { PythonBridge, manifest } from "./python-bridge.mjs"; import { SimSession, CONTEXT_ID } from "./sim-session.mjs"; const bridge = new PythonBridge({ loadPyodide: async () => ({ runPythonAsync: async (source) => source.length }) }); assert.equal((await bridge.initialize()).status, "verified"); for (const module of ["linuxcnc", "hal", "gstat", "INI", "tool", "parameter", "workspace", "virtual-clock"]) assert.equal(bridge.importModule(module).status, "verified"); assert.equal(bridge.invoke("hal", "component", ["demo"]).status, "verified"); assert.equal(bridge.invoke("hal", "param", ["demo.gain", 2.5]).value.value, 2.5); assert.equal(bridge.invoke("hal", "signal", ["ready", 1]).value.value, 1); assert.equal(bridge.invoke("linuxcnc", "command", ["run"]).value.task_state, "RUNNING"); assert.equal(bridge.invoke("gstat", "state", []).value.state, "RUNNING"); assert.equal(bridge.configureIni({ RS274NGC: { PROGRAM: "main.ngc" }, HAL: { HALFILE: ["machine.hal", "extra.hal"] } }).status, "verified"); assert.equal(bridge.invoke("linuxcnc", "ini", []).value.sections.RS274NGC, true); assert.deepEqual(bridge.invoke("gstat", "state", []).value, { state: "RUNNING", task: 0, motion: 0 }); assert.equal(bridge.invoke("workspace", "write", ["/workspace/main.ngc", "G0 X1\n"]).status, "verified"); assert.equal(bridge.invoke("workspace", "read", ["/workspace/main.ngc"]).value, "G0 X1\n"); assert.deepEqual(bridge.invoke("workspace", "list", ["/workspace/"]).value, ["/workspace/main.ngc"]); assert.deepEqual(bridge.invoke("workspace", "list", []).value, ["/workspace/main.ngc"]); assert.equal(bridge.invoke("workspace", "stat", ["/workspace/main.ngc"]).value.bytes, 6); assert.match(bridge.invoke("workspace", "hash", ["/workspace/"]).value, /^fnv1a64:/); assert.equal(bridge.invoke("workspace", "read", ["/etc/passwd"]).code, "python_workspace_path"); assert.equal(bridge.invoke("virtual-clock", "sleep", [4]).clock.seconds, 0.004); assert.equal(bridge.invoke("virtual-clock", "now", []).value.tick, 4); assert.equal(bridge.invoke("virtual-clock", "sleep", [-1]).code, "python_clock_argument"); assert.equal(bridge.checkCapability("subprocess").status, "REAL-EXCLUDED"); assert.equal((await bridge.execute("1 + 1")).value, 5); const saved = bridge.exportState(); const restored = new PythonBridge(); assert.equal(restored.restoreState(saved).status, "verified"); assert.equal(restored.hash(), bridge.hash()); const limited = new PythonBridge({ maxCalls: 1 }); assert.equal(limited.invoke("parameter", "get", [1]).status, "verified"); assert.equal(limited.invoke("parameter", "get", [2]).code, "python_call_limit"); assert.equal(manifest.worker_only, true); let sequence = 0; const core = { _sim_protocol_reset() {}, _sim_protocol_accept() { return 0; }, _sim_create() { return 0; }, _sim_load_workspace() { return 0; }, _sim_step() { return 0; }, _sim_protocol_set_tick() { return 0; } }; const session = new SimSession(core, { sessionId: "python-api" }); const base = { schema_version: 1, context_id: CONTEXT_ID, session_id: "python-api" }; const send = (kind, operation_id, payload = {}) => { sequence += 1; return session.accept({ ...base, kind, seq: sequence, operation_id, payload }); }; send("HELLO"); send("SESSION_ATTACH", "attach"); send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace" }); assert.equal(send("PYTHON_IMPORT", "import-workspace", { module: "workspace" }).payload.status, "verified"); assert.equal(send("PYTHON_CALL", "write", { module: "workspace", symbol: "write", args: ["/workspace/session.ngc", "G1 X2\n"] }).payload.status, "verified"); assert.equal(send("PYTHON_CALL", "clock", { module: "virtual-clock", symbol: "sleep", args: [2] }).payload.status, "verified"); const checkpoint = send("CHECKPOINT", "checkpoint").payload.checkpoint; assert.equal(checkpoint.state.python.schema, "python-worker-bridge.v1"); assert.equal(checkpoint.state.python.state.api.workspace.files["/workspace/session.ngc"], "G1 X2\n"); console.log(JSON.stringify({ schema: "python-api-s3-sample.v1", status: "verified", manifest_version: manifest.version, bridge_hash: bridge.hash(), restored_hash: restored.hash(), session_tick: session.tick, checkpoint_hash: checkpoint.checkpoint_hash }));