70 lines
4.9 KiB
JavaScript
70 lines
4.9 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import vm from "node:vm";
|
|
import test from "node:test";
|
|
import ts from "typescript";
|
|
|
|
const modules = new Map();
|
|
function load(name) {
|
|
if (modules.has(name)) return modules.get(name);
|
|
const source = fs.readFileSync(new URL(`../src/${name.replace(/^\.\//, "").replace(/\.js$/, ".ts")}`, import.meta.url), "utf8");
|
|
const compiled = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText;
|
|
const module = { exports: {} };
|
|
vm.runInNewContext("(function(exports,require,module){" + compiled + "})", { AbortController })(module.exports, load, module);
|
|
modules.set(name, module.exports);
|
|
return module.exports;
|
|
}
|
|
const facade = load("./part-document-facade.js");
|
|
const input = { schema: facade.PART_DOCUMENT_SCHEMA, module: "Part", document: "Main", object: "Cylinder", action: "setPrimitive", primitive: "Cylinder", parameters: { Radius: 3, Height: 7, Angle: 180 } };
|
|
const response = { ok: true, result: { document: "Main", objectCount: 1, objects: [{ name: "Cylinder", type: "Part::Cylinder", primitive: "Cylinder", parameters: input.parameters }] } };
|
|
|
|
test("valid native primitive parameters are transported without computing geometry in JS", async () => {
|
|
let request;
|
|
const kernel = { ccall(_name, _type, _types, args) { request = JSON.parse(args[0]); return JSON.stringify(response); } };
|
|
await facade.handlePartDocument(input, new AbortController().signal, kernel);
|
|
assert.deepEqual(request.args, input);
|
|
assert.equal(request.cmd, "part.document");
|
|
});
|
|
|
|
test("invalid dimensions, angles and primitive types never reach native code", async () => {
|
|
const kernel = { ccall() { assert.fail("invalid request reached native code"); } };
|
|
const variants = [
|
|
{ primitive: "constructor" }, { parameters: { Radius: 2 } }, { parameters: { Radius: -1, Height: 5, Angle: 360 } },
|
|
{ parameters: { Radius: 2, Height: 5, Angle: 0 } }, { parameters: { Radius: 2, Height: 5, Angle: 361 } },
|
|
{ primitive: "Cone", parameters: { Radius1: 3, Radius2: 3, Height: 5, Angle: 360 } },
|
|
{ primitive: "Torus", parameters: { Radius1: 1, Radius2: 2, Angle1: -180, Angle2: 180, Angle3: 360 } },
|
|
{ primitive: "Sphere", parameters: { Radius: 2, Angle1: 20, Angle2: -20, Angle3: 360 } },
|
|
{ primitive: "Sphere", parameters: { Radius: 2, Angle1: -100, Angle2: 90, Angle3: 360 } },
|
|
];
|
|
for (const variant of variants) await assert.rejects(facade.handlePartDocument({ ...input, ...variant }, new AbortController().signal, kernel));
|
|
});
|
|
|
|
test("a native reply for another primitive or parameter set cannot pass validation", async () => {
|
|
for (const override of [{ type: "Part::Sphere" }, { parameters: { Radius: 5, Height: 7, Angle: 180 } }]) {
|
|
const value = { ...response, result: { ...response.result, objects: [{ ...response.result.objects[0], ...override }] } };
|
|
await assert.rejects(facade.handlePartDocument(input, new AbortController().signal, { ccall: () => JSON.stringify(value) }), /type or parameters/);
|
|
}
|
|
});
|
|
|
|
test("boolean operand links use the native document and reject invalid or mismatched results", async () => {
|
|
const input = { schema: facade.PART_DOCUMENT_SCHEMA, module: "Part", document: "Main", action: "setBoolean", object: "Result", operation: "Cut", base: "Box", tool: "Cylinder" };
|
|
const object = { name: "Result", type: "Part::Cut", operation: "Cut", base: "Box", tool: "Cylinder" };
|
|
const kernel = { ccall: () => JSON.stringify({ ok: true, result: { document: "Main", objectCount: 1, objects: [object] } }) };
|
|
await facade.handlePartDocument(input, new AbortController().signal, kernel);
|
|
for (const override of [{ operation: "constructor" }, { tool: "Box" }, { base: "Result" }, { tool: "other/doc" }])
|
|
await assert.rejects(facade.handlePartDocument({ ...input, ...override }, new AbortController().signal, { ccall() { assert.fail("invalid request reached native code"); } }));
|
|
object.tool = "Other";
|
|
await assert.rejects(facade.handlePartDocument(input, new AbortController().signal, kernel), /operand links/);
|
|
});
|
|
|
|
test("native placement requires finite coordinates and validates the returned position", async () => {
|
|
const input = { schema: facade.PART_DOCUMENT_SCHEMA, module: "Part", document: "Main", action: "setPosition", object: "Box", position: [2, -3, 4] };
|
|
const object = { name: "Box", type: "Part::Box", position: input.position };
|
|
const kernel = { ccall: () => JSON.stringify({ ok: true, result: { document: "Main", objectCount: 1, objects: [object] } }) };
|
|
await facade.handlePartDocument(input, new AbortController().signal, kernel);
|
|
for (const position of [[1, 2], [1, NaN, 3], [Infinity, 0, 0]])
|
|
await assert.rejects(facade.handlePartDocument({ ...input, position }, new AbortController().signal, kernel), /finite position/);
|
|
object.position = [0, 0, 0];
|
|
await assert.rejects(facade.handlePartDocument(input, new AbortController().signal, kernel), /position does not match/);
|
|
});
|