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)); });