import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path from 'node:path'; import { afterEach, describe, expect, it } from 'vitest'; import type { CodingProductHost } from '../../electron/api/coding-product-services'; import type { HostApiContext } from '../../electron/api/context'; import { dispatchHostApiRequest } from '../../electron/api/host-api-dispatcher'; import { CodingProjectFileService } from '../../electron/coding-projects/project-files'; const roots: string[] = []; afterEach(async () => { await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))); }); describe('PI-105 Electron Host API seam', () => { it('reads a project-relative file through the Main dispatcher without loopback or path leakage', async () => { const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-host-dispatch-')); roots.push(root); await writeFile(path.join(root, 'notes.txt'), 'Main-owned content\n', 'utf8'); const files = new CodingProjectFileService(); const host = { fileContent: async (filePath: string) => await files.content(root, filePath), } as CodingProductHost; const ctx = { codingProducts: { host, attachments: {}, productTools: {} }, } as HostApiContext; const response = await dispatchHostApiRequest(ctx, { path: '/api/coding/files/content?path=notes.txt', }); expect(response).toMatchObject({ status: 200, ok: true, json: { file: { path: 'notes.txt', content: 'Main-owned content\n', truncated: false }, }, }); expect(JSON.stringify(response.json)).not.toContain(root); }); });