import assert from 'node:assert/strict'; import fs from 'node:fs'; import ts from '../web/node_modules/typescript/lib/typescript.js'; const source = fs.readFileSync(new URL('../web/src/user-file-import.ts', import.meta.url), 'utf8'); const compiled = ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ES2022 } }).outputText.replaceAll('import.meta.url', JSON.stringify(new URL('../web/src/user-file-import.ts', import.meta.url).href)); const { UserFileImportService } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`); let worker; globalThis.Worker = class { messages = []; constructor() { worker = this; } postMessage(message) { if (this.sendError) throw this.sendError; this.messages.push(message); } terminate() { this.terminated = true; } emit(data) { this.onmessage({ data }); } }; const file = { name: 'part.step', arrayBuffer: async () => new ArrayBuffer(4) }; const tick = () => new Promise(resolve => setImmediate(resolve)); const deadline = async promise => { let timer; try { return await Promise.race([promise, new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('request hung')), 250); })]); } finally { clearTimeout(timer); } }; const reject = (promise, pattern) => assert.rejects(deadline(promise), pattern); let service = new UserFileImportService(); let request = reject(service.import(file), /HTTP 503/); worker.emit({ type: 'error', message: 'HTTP 503' }); await request; assert.equal(worker.terminated, true); service = new UserFileImportService(); worker.emit({ type: 'ready' }); await reject(service.import({ ...file, arrayBuffer: async () => { throw new Error('read failed'); } }), /read failed/); worker.sendError = new Error('send failed'); await reject(service.import(file), /send failed/); worker.sendError = null; request = service.import(file); await tick(); worker.emit({ type: 'mesh', requestId: worker.messages.at(-1).requestId, payload: { vertexCount: 3 } }); assert.equal((await deadline(request)).vertexCount, 3); let release; request = reject(service.import({ ...file, arrayBuffer: () => new Promise(resolve => { release = resolve; }) }), /cancelled/); await tick(); service.reset(); await request; const count = worker.messages.length; release(new ArrayBuffer(4)); await tick(); assert.equal(worker.messages.length, count); request = reject(service.import(file), /Worker failed: crash/); await tick(); worker.onerror({ message: 'crash' }); await request; await reject(service.import(file), /crash/); service = new UserFileImportService(); request = reject(service.import(file), /disposed/); service.dispose(); await request; console.log('PASS: startup failure, read/send recovery, reset during read, Worker crash, disposal');