// @vitest-environment node import { EventEmitter } from 'node:events'; import type { IncomingMessage, ServerResponse } from 'node:http'; import { describe, expect, it, vi } from 'vitest'; import type { HostApiContext } from '../../electron/api/context'; import { handleDevicePackageRoutes } from '../../electron/api/routes/device-packages'; import { DevicePackageError } from '../../electron/coding-packages/device-package-manager'; function request(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 response() { 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, get status() { return (res as { statusCode: number }).statusCode; }, json: () => JSON.parse(chunks.join('')) as Record, }; } async function invoke(ctx: HostApiContext, method: string, target: string, body?: unknown) { const output = response(); const handled = await handleDevicePackageRoutes( request(method, body), output.res, new URL(`http://localhost${target}`), ctx, ); return { handled, status: output.status, payload: output.json() }; } describe('device package routes', () => { it('lists, enables, and removes device packages without exposing an install route', async () => { const empty = { schemaVersion: 1, generation: 0, packages: [] }; const list = vi.fn().mockResolvedValue(empty); const setEnabled = vi.fn().mockResolvedValue({ ...empty, generation: 1 }); const uninstall = vi.fn().mockResolvedValue({ ...empty, generation: 2 }); const ctx = { codingProducts: { devicePackages: { list, setEnabled, uninstall } } } as unknown as HostApiContext; expect(await invoke(ctx, 'GET', '/api/coding/device-packages')).toMatchObject({ handled: true, status: 200, payload: empty, }); expect(await invoke(ctx, 'PATCH', '/api/coding/device-packages/pi-tools', { enabled: false })) .toMatchObject({ handled: true, status: 200, payload: { generation: 1 } }); expect(await invoke(ctx, 'DELETE', '/api/coding/device-packages/pi-tools', {})) .toMatchObject({ handled: true, status: 200, payload: { generation: 2 } }); expect(setEnabled).toHaveBeenCalledWith('pi-tools', false); expect(uninstall).toHaveBeenCalledWith('pi-tools'); const unsupported = response(); expect(await handleDevicePackageRoutes( request('POST', { source: 'npm:pi-web-search' }), unsupported.res, new URL('http://localhost/api/coding/device-packages'), ctx, )).toBe(false); }); it('rejects forged fields and returns only bounded manager errors', async () => { const setEnabled = vi.fn(); const uninstall = vi.fn().mockRejectedValue(new DevicePackageError( 'local_package_in_use', 'Package is still active', )); const ctx = { codingProducts: { devicePackages: { list: vi.fn(), setEnabled, uninstall, } } } as unknown as HostApiContext; expect(await invoke(ctx, 'PATCH', '/api/coding/device-packages/pi-tools', { enabled: false, source: 'C:\\forged', })).toMatchObject({ status: 400, payload: { code: 'local_package_request_invalid' } }); expect(setEnabled).not.toHaveBeenCalled(); expect(await invoke(ctx, 'DELETE', '/api/coding/device-packages/pi-tools', {})) .toMatchObject({ status: 409, payload: { code: 'local_package_in_use' } }); }); });