import { EventEmitter } from 'node:events'; import type { IncomingMessage, ServerResponse } from 'node:http'; import { spawn, type ChildProcessWithoutNullStreams } from 'node:child_process'; import { join } from 'node:path'; import { createInterface } from 'node:readline'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { createAiHardwareRouteHandler } from '@electron/api/routes/ai-hardware'; type ContractServer = { process: ChildProcessWithoutNullStreams; baseUrl: string }; const OPERATION = { createDesk: '00000000-0000-4000-8000-000000000001', bindDevice: '00000000-0000-4000-8000-000000000002', updateAgent: '00000000-0000-4000-8000-000000000003', updateAssignment: '00000000-0000-4000-8000-000000000004', conflict: '00000000-0000-4000-8000-000000000005', recoverCredential: '00000000-0000-4000-8000-000000000006', recoverCredentialAgain: '00000000-0000-4000-8000-000000000007', inProgress: '00000000-0000-4000-8000-000000000009', } as const; function localRequest(method: string, body?: unknown): IncomingMessage { const req = new EventEmitter(); const raw = body === undefined ? undefined : JSON.stringify(body); Object.assign(req, { method, headers: raw === undefined ? {} : { 'content-length': String(Buffer.byteLength(raw)) }, [Symbol.asyncIterator]: async function* () { if (raw !== undefined) yield Buffer.from(raw); }, }); return req as IncomingMessage; } function localResponse() { const chunks: string[] = []; const res = new EventEmitter(); Object.assign(res, { statusCode: 0, setHeader: vi.fn(), end: vi.fn((chunk?: string) => { if (chunk) chunks.push(chunk); }), }); return { res: res as unknown as ServerResponse, json: () => JSON.parse(chunks.join('')) as Record, }; } async function invoke( handler: ReturnType, method: string, path: string, body?: unknown, ) { const target = localResponse(); const handled = await handler( localRequest(method, body), target.res, new URL(`http://127.0.0.1${path}`), {} as never, ); expect(handled).toBe(true); return target.json(); } async function startServer(disabled = false): Promise { const serverDir = process.env.WORKS_SQUARE_SERVER_DIR; if (!serverDir) throw new Error('WORKS_SQUARE_SERVER_DIR is required'); const script = join(serverDir, 'tests', 'contract', 'ai_hardware_contract_server.py'); const executable = process.env.WORKS_SQUARE_PYTHON ?? 'uv'; const args = process.env.WORKS_SQUARE_PYTHON ? [script] : ['run', '--frozen', 'python', script]; if (disabled) args.push('--disabled'); const child = spawn(executable, args, { cwd: serverDir, env: { ...process.env, PYTHONPATH: [serverDir, process.env.PYTHONPATH].filter(Boolean).join(process.platform === 'win32' ? ';' : ':'), PYTHONUNBUFFERED: '1', }, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true, }); let stderr = ''; child.stderr.setEncoding('utf8'); child.stderr.on('data', (chunk: string) => { stderr += chunk; }); const lines = createInterface({ input: child.stdout }); return await new Promise((resolve, reject) => { const timer = setTimeout(() => { child.kill(); reject(new Error(`contract server readiness timed out\n${stderr}`)); }, 15_000); child.once('error', (error) => { clearTimeout(timer); reject(error); }); child.once('exit', (code) => { clearTimeout(timer); reject(new Error(`contract server exited before ready (${code})\n${stderr}`)); }); lines.once('line', (line) => { clearTimeout(timer); try { const ready = JSON.parse(line) as { base_url?: unknown }; if (typeof ready.base_url !== 'string') throw new Error('missing base_url'); resolve({ process: child, baseUrl: ready.base_url }); } catch (error) { child.kill(); reject(new Error(`invalid contract server handshake: ${line}`, { cause: error })); } }); }); } async function stopServer(server: ContractServer): Promise { if (server.process.exitCode !== null) return; const exited = new Promise((resolve) => server.process.once('exit', () => resolve())); server.process.stdin.end(); const forced = setTimeout(() => server.process.kill(), 10_000); await exited; clearTimeout(forced); } describe('Makelore Main to Works Square AI hardware wire contract', () => { let server: ContractServer | undefined; let handler: ReturnType; beforeAll(async () => { server = await startServer(); handler = createAiHardwareRouteHandler({ apiBaseUrl: server.baseUrl, fetchImpl: globalThis.fetch, getAccessToken: async () => 'contract-token', }); }); afterAll(async () => { if (server) await stopServer(server); }); it('uses the real authenticated overview and create-agent HTTP routes', async () => { expect(await invoke(handler, 'GET', '/api/works/ai-hardware')).toEqual({ success: true, data: { status: 'unprovisioned', agents: [], devices: [] }, }); const createBody = { agent_name: 'Desk', client_operation_id: OPERATION.createDesk }; const created = await invoke(handler, 'POST', '/api/works/ai-hardware/agents', createBody); expect(created).toEqual({ success: true, data: { id: 'agent-1', name: 'Desk', config_revision: 0 }, }); expect(await invoke(handler, 'POST', '/api/works/ai-hardware/agents', createBody)).toEqual(created); expect(await invoke(handler, 'GET', '/api/works/ai-hardware')).toEqual({ success: true, data: { status: 'active', agents: [{ id: 'agent-1', name: 'Desk', config_revision: 0 }], devices: [], }, }); }); it('binds a device and preserves GET/PATCH configuration ETags as numeric revisions', async () => { const bindBody = { activation_code: '123456', agent_id: 'agent-1', client_operation_id: OPERATION.bindDevice, }; const bound = await invoke(handler, 'POST', '/api/works/ai-hardware/device-bindings', bindBody); expect(bound).toEqual({ success: true, data: { id: 'device-1', agent_id: 'agent-1', assignment_revision: 0 }, }); expect(await invoke(handler, 'POST', '/api/works/ai-hardware/device-bindings', bindBody)).toEqual(bound); const read = await invoke(handler, 'GET', '/api/works/ai-hardware/agents/agent-1'); expect(read).toMatchObject({ success: true, revision: 0, data: { id: 'agent-1', config_revision: 0 } }); const updateBody = { revision: 0, agent_name: 'Workshop', system_prompt: 'Build carefully', client_operation_id: OPERATION.updateAgent, }; const updated = await invoke(handler, 'PATCH', '/api/works/ai-hardware/agents/agent-1', updateBody); expect(updated).toMatchObject({ success: true, revision: 1, data: { id: 'agent-1', name: 'Workshop', config_revision: 1, system_prompt: 'Build carefully' }, }); expect(await invoke(handler, 'PATCH', '/api/works/ai-hardware/agents/agent-1', updateBody)).toEqual(updated); }); it('preserves assignment revisions and safe 409 Retry-After metadata', async () => { expect(await invoke(handler, 'GET', '/api/works/ai-hardware/devices/device-1/agent-assignment')).toEqual({ success: true, revision: 0, data: { id: 'device-1', agent_id: 'agent-1', assignment_revision: 0 }, }); expect(await invoke(handler, 'PUT', '/api/works/ai-hardware/devices/device-1/agent-assignment', { revision: 9, agent_id: 'agent-1', client_operation_id: OPERATION.conflict, })).toEqual({ success: false, status: 409, code: 'ai_hardware_revision_conflict', error: 'AI hardware data changed; refresh and retry', retryable: true, retry_after_seconds: 2, operation_id: OPERATION.conflict, }); const assignmentBody = { revision: 0, agent_id: 'agent-1', client_operation_id: OPERATION.updateAssignment, }; const assigned = await invoke( handler, 'PUT', '/api/works/ai-hardware/devices/device-1/agent-assignment', assignmentBody, ); expect(assigned).toEqual({ success: true, revision: 1, data: { id: 'device-1', agent_id: 'agent-1', assignment_revision: 1 }, }); expect(await invoke( handler, 'PUT', '/api/works/ai-hardware/devices/device-1/agent-assignment', assignmentBody, )).toEqual(assigned); }); it('rejects reuse of one operation id with a different request body', async () => { expect(await invoke(handler, 'POST', '/api/works/ai-hardware/agents', { agent_name: 'Conflict original', client_operation_id: OPERATION.conflict, })).toMatchObject({ success: true, data: { id: 'agent-2', name: 'Conflict original', config_revision: 0 } }); expect(await invoke(handler, 'POST', '/api/works/ai-hardware/agents', { agent_name: 'Conflict changed', client_operation_id: OPERATION.conflict, })).toEqual({ success: false, status: 409, code: 'ai_hardware_idempotency_conflict', error: 'AI hardware request conflicts with an earlier operation', retryable: false, operation_id: OPERATION.conflict, }); const overview = await invoke(handler, 'GET', '/api/works/ai-hardware'); expect(overview).toMatchObject({ success: true, data: { agents: [ { id: 'agent-1', name: 'Workshop', config_revision: 1 }, { id: 'agent-2', name: 'Conflict original', config_revision: 0 }, ] }, }); }); it('retries operation-in-progress once with the identical key and body', async () => { expect(await invoke(handler, 'POST', '/api/works/ai-hardware/agents', { agent_name: 'Retried', client_operation_id: OPERATION.inProgress, })).toEqual({ success: true, data: { id: 'agent-3', name: 'Retried', config_revision: 0 }, }); const overview = await invoke(handler, 'GET', '/api/works/ai-hardware'); expect(overview).toMatchObject({ success: true, data: { agents: [ { id: 'agent-1' }, { id: 'agent-2' }, { id: 'agent-3', name: 'Retried', config_revision: 0 }, ] }, }); }); it('recovers credentials without an upstream body and replays safely', async () => { const recoveryBody = { client_operation_id: OPERATION.recoverCredential }; const recovered = await invoke( handler, 'POST', '/api/works/ai-hardware/credential-recovery', recoveryBody, ); expect(recovered).toEqual({ success: true, data: { status: 'active', agents: [ { id: 'agent-1', name: 'Workshop', config_revision: 1 }, { id: 'agent-2', name: 'Conflict original', config_revision: 0 }, { id: 'agent-3', name: 'Retried', config_revision: 0 }, ], devices: [{ id: 'device-1', agent_id: 'agent-1', assignment_revision: 1 }], }, }); expect(await invoke( handler, 'POST', '/api/works/ai-hardware/credential-recovery', recoveryBody, )).toEqual(recovered); expect(await invoke(handler, 'POST', '/api/works/ai-hardware/credential-recovery', { client_operation_id: OPERATION.recoverCredentialAgain, })).toEqual(recovered); }); it('maps a real FastAPI Bearer rejection to a safe local auth envelope', async () => { const unauthorized = createAiHardwareRouteHandler({ apiBaseUrl: server!.baseUrl, fetchImpl: globalThis.fetch, getAccessToken: async () => 'wrong-token', }); expect(await invoke(unauthorized, 'GET', '/api/works/ai-hardware')).toEqual({ success: false, status: 401, code: 'AI_HARDWARE_AUTH_REQUIRED', error: 'Works Square sign-in is required', retryable: false, }); }); it('maps a real disabled FastAPI route to the product disabled state', async () => { const disabled = await startServer(true); try { const disabledHandler = createAiHardwareRouteHandler({ apiBaseUrl: disabled.baseUrl, fetchImpl: globalThis.fetch, getAccessToken: async () => 'contract-token', }); expect(await invoke(disabledHandler, 'GET', '/api/works/ai-hardware')).toEqual({ success: false, status: 404, code: 'AI_HARDWARE_DISABLED', error: 'AI hardware module is not enabled', retryable: false, }); } finally { await stopServer(disabled); } }); });