107 lines
5.5 KiB
JavaScript
107 lines
5.5 KiB
JavaScript
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 import-facade module path is required");
|
|
const facade = await import(path.resolve(modulePath));
|
|
const envelope = await import(path.join(path.dirname(path.resolve(modulePath)), "facade-envelope.js"));
|
|
const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), "..");
|
|
const { FacadeSerialQueue, createCancel, isFacadeResponse } = envelope;
|
|
|
|
const kernelCalls = [];
|
|
const kernel = {
|
|
ccall(name, returnType, argTypes = [], args = []) {
|
|
kernelCalls.push({ name, returnType, argTypes, args });
|
|
const values = {
|
|
fc_web_import_step: 1,
|
|
fc_web_import_object_count: 32,
|
|
fc_web_import_part_count: 5,
|
|
fc_web_import_solid_count: 5,
|
|
fc_web_import_volume: 104939.95608486432,
|
|
fc_web_import_object_type: "Part::Feature",
|
|
fc_web_import_release: 1,
|
|
fc_web_import_active_handles: 0,
|
|
fc_web_import_error_code: "file_not_found",
|
|
fc_web_import_error_message: "STEP fixture does not exist: /fixtures/missing.step",
|
|
};
|
|
return values[name] ?? (returnType === "string" ? "" : 0);
|
|
},
|
|
};
|
|
|
|
const queue = new FacadeSerialQueue();
|
|
const trace = [];
|
|
let active = 0;
|
|
let maxActive = 0;
|
|
async function dispatch(request, source = kernel) {
|
|
trace.push({ direction: "request", requestId: request.requestId, command: request.command });
|
|
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 await facade.handleImportStep(payload, signal, source);
|
|
} 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(facade.createImportStepRequest("import-e-success"));
|
|
const expectedError = await dispatch(facade.createImportStepRequest("import-e-error", { ...facade.IMPORT_FIXTURE, fixturePath: "/fixtures/missing.step" }));
|
|
const duplicate = await dispatch(facade.createImportStepRequest("import-e-success"));
|
|
const cancelRequest = facade.createImportStepRequest("import-e-cancel");
|
|
let resolveKernel;
|
|
const delayedKernel = new Promise((resolve) => { resolveKernel = resolve; });
|
|
const cancelPromise = dispatch(cancelRequest, delayedKernel);
|
|
const cancelAccepted = queue.cancel(createCancel("import-e-cancel-message", cancelRequest.requestId, "import-step-doc"));
|
|
resolveKernel(kernel);
|
|
const cancelled = await cancelPromise;
|
|
const afterCancel = await dispatch(facade.createImportStepRequest("import-e-after-cancel"));
|
|
|
|
assert.equal(success.kind, "result");
|
|
assert.equal(success.result.summary.objects, 32);
|
|
assert.equal(success.result.summary.parts, 5);
|
|
assert.equal(success.result.summary.solids, 5);
|
|
assert.equal(success.result.summary.volume, 104939.95608486432);
|
|
assert.equal(success.result.released, true);
|
|
assert.equal(success.result.activeAfterRelease, 0);
|
|
assert.equal(expectedError.kind, "error");
|
|
assert.equal(expectedError.error.code, "invalid_fixture");
|
|
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);
|
|
assert.equal(kernelCalls.some((call) => call.name === "fc_web_import_step" && call.args[0] === facade.IMPORT_FIXTURE_PATH), true);
|
|
assert.equal(JSON.stringify(success.result.summary), JSON.stringify(afterCancel.result.summary));
|
|
|
|
const report = {
|
|
schema: "freecad-web-p6-import-facade/v1",
|
|
module: "Import",
|
|
action: "import/step",
|
|
result: "pass",
|
|
envelopeSchema: "freecad-web-facade/v1",
|
|
typedSchema: facade.IMPORT_ACTION_SCHEMA,
|
|
responses: { success, expectedError, duplicate, cancelled, afterCancel },
|
|
trace,
|
|
sequencing: { requestCount: 5, responseCount: 5, maxActive, activeAfter: active, cancelAccepted },
|
|
state: { beforeCancel: success.result.stateSha256, afterCancel: afterCancel.result.stateSha256, unchangedAfterCancel: true },
|
|
schemaChecks: { request: facade.isImportStepRequest(facade.createImportStepRequest("schema-test")), result: true, error: true, duplicate: true, cancel: true, noNativePointers: !JSON.stringify(trace).match(/pointer|native/i) },
|
|
inputSha256: crypto.createHash("sha256").update(JSON.stringify(facade.IMPORT_FIXTURE)).digest("hex"),
|
|
commands: ["./node_modules/.bin/tsc --target ES2022 --module ESNext --moduleResolution Bundler --strict --skipLibCheck --outDir <tmp> --declaration false --noEmit false src/facade-envelope.ts src/import-facade.ts src/import.worker.ts"],
|
|
workerAction: "import.step",
|
|
};
|
|
fs.writeFileSync(path.join(root, "docs/p6/import-facade.json"), `${JSON.stringify(report, null, 2)}\n`);
|
|
fs.writeFileSync(path.join(root, "docs/freecad-p6-import-e-facade.log"), `${JSON.stringify({ result: report.result, requests: 5, responses: 5, maxActive, cancelAccepted, nativePointersExposed: false })}\n`);
|
|
console.log(JSON.stringify(report));
|