// @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 { handleCodingPluginRoutes } from '../../electron/api/routes/coding-plugins'; 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 handleCodingPluginRoutes( request(method, body), output.res, new URL(`http://localhost${target}`), ctx, ); return { handled, status: output.status, payload: output.json() }; } describe('coding plugin Host routes', () => { it('uses only the local project handle for GET and preserves the bounded projection', async () => { const projection = { schemaVersion: 1 as const, project: { localProjectId: 'local-a', durableProjectId: 'durable-a' }, policyStatus: 'current' as const, unknownPluginIds: ['makelore.removed'], items: [], }; const list = vi.fn().mockResolvedValue(projection); const ctx = { codingProducts: { plugins: { list } } } as unknown as HostApiContext; const result = await invoke(ctx, 'GET', '/api/coding/plugins?projectId=local-a'); expect(result).toMatchObject({ handled: true, status: 200, payload: projection }); expect(list).toHaveBeenCalledWith('local-a'); expect(JSON.stringify(result.payload)).not.toMatch(/projectPath|owner|token|entitlement_scope/u); }); it('accepts only the exact PUT body and never forwards authority fields', async () => { const setEnabled = vi.fn().mockResolvedValue({ schemaVersion: 1, project: { localProjectId: 'local-a', durableProjectId: null }, policyStatus: 'unavailable', unknownPluginIds: [], items: [], }); const ctx = { codingProducts: { plugins: { setEnabled } } } as unknown as HostApiContext; const accepted = await invoke(ctx, 'PUT', '/api/coding/plugins/makelore.data-service', { projectId: 'local-a', enabled: true, }); const rejected = await invoke(ctx, 'PUT', '/api/coding/plugins/makelore.data-service', { projectId: 'local-a', enabled: true, projectPath: 'C:\\untrusted', }); expect(accepted.status).toBe(200); expect(setEnabled).toHaveBeenCalledOnce(); expect(setEnabled).toHaveBeenCalledWith('local-a', 'makelore.data-service', true); expect(rejected).toMatchObject({ status: 400, payload: { success: false, code: 'plugin_request_invalid' } }); }); it('rejects duplicate or unexpected query authority before calling Main services', async () => { const list = vi.fn(); const ctx = { codingProducts: { plugins: { list } } } as unknown as HostApiContext; const duplicate = await invoke(ctx, 'GET', '/api/coding/plugins?projectId=local-a&projectId=local-b'); const authority = await invoke(ctx, 'GET', '/api/coding/plugins?projectId=local-a&durableProjectId=forged'); expect(duplicate.status).toBe(400); expect(authority.status).toBe(400); expect(list).not.toHaveBeenCalled(); }); });