111 lines
5.6 KiB
JavaScript
111 lines
5.6 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const modulePath = process.argv[2];
|
|
if (!modulePath) throw new Error("compiled cam-facade module path is required");
|
|
const facade = await import(modulePath);
|
|
const envelope = await import(path.resolve(path.dirname(modulePath), "facade-envelope.js"));
|
|
const {
|
|
CAM_ACTION_SCHEMA,
|
|
CAM_COMMAND,
|
|
CAM_FIXTURE,
|
|
CAM_OBJECT_TYPE,
|
|
createCamRequest,
|
|
handleCamCreateRecompute,
|
|
isCamRequest,
|
|
} = facade;
|
|
const { FacadeSerialQueue, createCancel, isFacadeResponse } = envelope;
|
|
|
|
const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..");
|
|
const docsRoot = path.join(root, "docs");
|
|
const outputPath = path.join(docsRoot, "p6", "cam-facade.json");
|
|
const schemaPath = path.join(docsRoot, "p6", "cam-facade.schema.json");
|
|
const logPath = path.join(docsRoot, "freecad-p6-cam-e-facade.log");
|
|
const queue = new FacadeSerialQueue();
|
|
const trace = [];
|
|
let active = 0;
|
|
let maxActive = 0;
|
|
|
|
async function dispatch(request) {
|
|
trace.push({ direction: "request", requestId: request.requestId, command: request.command, payload: request.payload });
|
|
const response = await queue.dispatch(request, async (payload, signal) => {
|
|
active += 1;
|
|
maxActive = Math.max(maxActive, active);
|
|
trace.push({ direction: "handler-start", requestId: request.requestId });
|
|
try {
|
|
return handleCamCreateRecompute(payload, signal);
|
|
} finally {
|
|
active -= 1;
|
|
trace.push({ direction: "handler-end", requestId: request.requestId });
|
|
}
|
|
});
|
|
trace.push({ direction: "response", requestId: response.requestId, kind: response.kind, code: response.kind === "error" ? response.error.code : null });
|
|
return response;
|
|
}
|
|
|
|
const success = await dispatch(createCamRequest("cam-success"));
|
|
const expectedError = await dispatch(createCamRequest("cam-error", { ...CAM_FIXTURE, gcode: "G1 X" }));
|
|
const duplicate = await dispatch(createCamRequest("cam-success"));
|
|
const cancelRequest = createCamRequest("cam-cancel");
|
|
const cancelPromise = dispatch(cancelRequest);
|
|
trace.push({ direction: "cancel", requestId: "cam-cancel-message", cancelOf: cancelRequest.requestId });
|
|
const cancelAccepted = queue.cancel(createCancel("cam-cancel-message", cancelRequest.requestId, "cam-doc"));
|
|
const cancelled = await cancelPromise;
|
|
const afterCancel = await dispatch(createCamRequest("cam-after-cancel"));
|
|
|
|
assert.equal(CAM_COMMAND, "cam.createRecompute");
|
|
assert.equal(CAM_OBJECT_TYPE, "Path::Feature");
|
|
assert.equal(isCamRequest(createCamRequest("schema-test")), true);
|
|
assert.equal(isCamRequest({ ...createCamRequest("schema-test"), command: "other" }), false);
|
|
assert.equal(success.kind, "result");
|
|
assert.equal(success.result.schema, CAM_ACTION_SCHEMA);
|
|
assert.equal(success.result.path.commandCount, 3);
|
|
assert.equal(success.result.path.length, 20);
|
|
assert.equal(expectedError.kind, "error");
|
|
assert.equal(expectedError.error.code, "invalid_gcode");
|
|
assert.equal(duplicate.kind, "error");
|
|
assert.equal(duplicate.error.code, "duplicate_request");
|
|
assert.equal(cancelAccepted, true);
|
|
assert.equal(cancelled.kind, "error");
|
|
assert.equal(cancelled.error.code, "cancelled");
|
|
assert.equal(afterCancel.kind, "result");
|
|
assert.equal(afterCancel.result.stateSha256, success.result.stateSha256);
|
|
assert.equal(maxActive, 1);
|
|
assert.equal(active, 0);
|
|
for (const response of [success, expectedError, duplicate, cancelled, afterCancel]) assert.equal(isFacadeResponse(response), true);
|
|
|
|
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
|
|
assert.equal(schema.$id, "https://freecad.org/schemas/freecad-web-cam-create-recompute-v1.json");
|
|
assert.equal(schema.$defs.request.properties.command.const, CAM_COMMAND);
|
|
assert.equal(schema.$defs.payload.properties.objectType.const, CAM_OBJECT_TYPE);
|
|
assert.equal(schema.$defs.typedResult.properties.schema.const, CAM_ACTION_SCHEMA);
|
|
|
|
const report = {
|
|
schema: "freecad-web-p6-cam-facade/v1",
|
|
task_id: "FW-P6-CAM-E",
|
|
status: "pass",
|
|
action: CAM_COMMAND,
|
|
envelopeSchema: "freecad-web-facade/v1",
|
|
typedSchema: CAM_ACTION_SCHEMA,
|
|
fixture: { ...CAM_FIXTURE, inputSha256: crypto.createHash("sha256").update(JSON.stringify(CAM_FIXTURE)).digest("hex") },
|
|
responses: { success, expectedError, duplicate, cancelled, afterCancel },
|
|
trace,
|
|
sequencing: { maxActive, activeAfter: active, requestCount: 5, responseCount: 5, cancelAccepted },
|
|
state: { beforeCancel: success.result.stateSha256, afterCancel: afterCancel.result.stateSha256, unchangedAfterCancel: success.result.stateSha256 === afterCancel.result.stateSha256 },
|
|
schemaChecks: { request: true, result: true, expectedError: true, duplicate: true, cancel: true, noNativePointers: !JSON.stringify(trace).match(/pointer|native/i) },
|
|
commands: [
|
|
"npx tsc --target ES2022 --module ESNext --moduleResolution Bundler --strict --skipLibCheck --outDir /tmp/freecad-p6-cam-facade --declaration false --noEmit false src/facade-envelope.ts src/cam-facade.ts",
|
|
"node tools/run_p6_cam_facade_smoke.mjs /tmp/freecad-p6-cam-facade/cam-facade.js"
|
|
],
|
|
artifacts: { schema: path.relative(root, schemaPath), workerLog: path.relative(root, logPath) },
|
|
boundaries: ["The facade returns a deterministic CAM summary and never exposes a native or WASM pointer.", "Only one create/recompute action is included."],
|
|
};
|
|
|
|
fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
fs.writeFileSync(logPath, `${trace.map((event, index) => `${String(index + 1).padStart(2, "0")} ${JSON.stringify(event)}`).join("\n")}\nresult=pass\nmaxActive=${maxActive}\n`);
|
|
console.log(JSON.stringify({ result: report.status, requests: report.sequencing.requestCount, responses: report.sequencing.responseCount, maxActive }));
|