61 lines
4.0 KiB
JavaScript
61 lines
4.0 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 code = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText;
|
|
const module = { exports: {} };
|
|
vm.runInNewContext(`(function(exports,require,module){${code}})`, { AbortController, ArrayBuffer, Uint8Array })(module.exports, load, module);
|
|
modules.set(name, module.exports);
|
|
return module.exports;
|
|
}
|
|
const { PART_DOCUMENT_SCHEMA, handlePartDocument } = load('./part-document-facade.js');
|
|
const displayProperties = [{ object: 'Box1', color: '#cc3344', visible: false }];
|
|
const payload = { schema: PART_DOCUMENT_SCHEMA, module: 'Part', document: 'Test', action: 'save', path: '/tmp/Test.FCStd', displayProperties };
|
|
const response = { ok: true, result: { document: 'Test', path: payload.path, size: 4, objectCount: 1,
|
|
objects: [{ name: 'Box1', label: 'Box', type: 'Part::Box', displayColor: '#cc3344', displayVisibility: false }] } };
|
|
const signal = () => new AbortController().signal;
|
|
const kernel = reply => ({ ccall: () => JSON.stringify(reply), FS: { readFile: () => new Uint8Array([80,75,3,4]) } });
|
|
|
|
test('save transports native per-object appearance and returns native file bytes', async () => {
|
|
const backend = kernel(response);
|
|
backend.ccall = (_name,_type,_types,args) => { assert.deepEqual(JSON.parse(args[0]).args, payload); return JSON.stringify(response); };
|
|
const result = await handlePartDocument(payload,signal(),backend);
|
|
assert.deepEqual([...new Uint8Array(result.bytes)],[80,75,3,4]);
|
|
});
|
|
test('malformed, duplicate, wrong-operation appearance is rejected before native dispatch', async () => {
|
|
const backend = { ccall() { assert.fail('invalid input reached native'); } };
|
|
const entries = [null, {}, [{...displayProperties[0],color:'red'}], [{...displayProperties[0],visible:1}],
|
|
[{...displayProperties[0],object:'Other/Box'}], [displayProperties[0],displayProperties[0]]];
|
|
for(const value of entries) await assert.rejects(handlePartDocument({...payload,displayProperties:value},signal(),backend),/Invalid document display/);
|
|
await assert.rejects(handlePartDocument({...payload,action:'snapshot'},signal(),backend),/Invalid document display/);
|
|
});
|
|
test('a stale kernel cannot silently ignore display properties', async () => {
|
|
for(const changes of [{displayColor:undefined},{displayVisibility:undefined},{displayVisibility:true},{displayColor:'#73c6d4'}]) {
|
|
const reply = {...response,result:{...response.result,objects:[{...response.result.objects[0],...changes}]}};
|
|
await assert.rejects(handlePartDocument(payload,signal(),kernel(reply)),/Saved display properties do not match/);
|
|
}
|
|
});
|
|
test('import cancellation carries an explicit rollback decision to the native owner', async () => {
|
|
const payload={schema:PART_DOCUMENT_SCHEMA,module:'Part',document:'Test',action:'finishImport',commitImport:false};
|
|
let sent;
|
|
const result=await handlePartDocument(payload,signal(),{ccall(_n,_t,_a,args){sent=JSON.parse(args[0]);return JSON.stringify({ok:true,result:{document:'Test',objectCount:0,objects:[]}});}});
|
|
assert.deepEqual(sent.args,payload);
|
|
assert.equal(result.objectCount,0);
|
|
});
|
|
test('invalid import decisions and file targets are rejected before native execution',async()=>{
|
|
const backend={ccall(){assert.fail('invalid import request reached native');}};
|
|
const base={schema:PART_DOCUMENT_SCHEMA,module:'Part',document:'Test'};
|
|
for(const fields of [
|
|
{action:'finishImport'}, {action:'finishImport',commitImport:'false'},
|
|
{action:'snapshot',deferImportCommit:true},
|
|
{action:'importShape',object:'Box',path:'/tmp/../other.stl'},
|
|
{action:'exportShape',object:'Box',path:'/tmp/model.exe'},
|
|
])await assert.rejects(handlePartDocument({...base,...fields},signal(),backend));
|
|
});
|