/** * orchestrator/test-ipc.mjs * * Integration test: spawn the C++ worker, exchange messages, verify responses. * * Run: node orchestrator/test-ipc.mjs * Needs: npm run build (to compile the C++ binary first) */ import { spawnWorker } from './spawn.mjs'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const EXE = resolve(__dirname, '..', 'dist', 'polymech-cli.exe'); let passed = 0; let failed = 0; function assert(condition, label) { if (condition) { console.log(` āœ… ${label}`); passed++; } else { console.error(` āŒ ${label}`); failed++; } } async function run() { console.log('\nšŸ”§ IPC Integration Tests\n'); // ── 1. Spawn & ready ──────────────────────────────────────────────────── console.log('1. Spawn worker and wait for ready signal'); const worker = spawnWorker(EXE); const readyMsg = await worker.ready; assert(readyMsg.type === 'ready', 'Worker sends ready message on startup'); // ── 2. Ping / Pong ───────────────────────────────────────────────────── console.log('2. Ping → Pong'); const pong = await worker.request({ type: 'ping' }); assert(pong.type === 'pong', `Response type is "pong" (got "${pong.type}")`); // ── 3. Job echo ───────────────────────────────────────────────────────── console.log('3. Job → Job Result (echo payload)'); const payload = { action: 'resize', width: 1024, format: 'webp' }; const jobResult = await worker.request({ type: 'job', payload }); assert(jobResult.type === 'job_result', `Response type is "job_result" (got "${jobResult.type}")`); assert( jobResult.payload?.action === 'resize' && jobResult.payload?.width === 1024, 'Payload echoed back correctly' ); // ── 4. Unknown type → error ───────────────────────────────────────────── console.log('4. Unknown type → error response'); const errResp = await worker.request({ type: 'nonsense' }); assert(errResp.type === 'error', `Response type is "error" (got "${errResp.type}")`); // ── 5. Multiple rapid requests ────────────────────────────────────────── console.log('5. Multiple concurrent requests'); const promises = []; for (let i = 0; i < 10; i++) { promises.push(worker.request({ type: 'ping', payload: { seq: i } })); } const results = await Promise.all(promises); assert(results.length === 10, `All 10 responses received`); assert(results.every(r => r.type === 'pong'), 'All responses are pong'); // ── 6. Graceful shutdown ──────────────────────────────────────────────── console.log('6. Graceful shutdown'); const shutdownRes = await worker.shutdown(); assert(shutdownRes.type === 'shutdown_ack', `Shutdown acknowledged (got "${shutdownRes.type}")`); // Wait a beat for process exit await new Promise(r => setTimeout(r, 200)); assert(worker.process.exitCode === 0, `Worker exited with code 0 (got ${worker.process.exitCode})`); // ── Summary ───────────────────────────────────────────────────────────── console.log(`\n────────────────────────────────`); console.log(` Passed: ${passed} Failed: ${failed}`); console.log(`────────────────────────────────\n`); process.exit(failed > 0 ? 1 : 0); } run().catch((err) => { console.error('Test runner error:', err); process.exit(1); });