import { mkdtemp, realpath, rm, truncate, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { Readable } from "node:stream"; import type { FastifyInstance } from "fastify"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { buildApp } from "./app.js"; import { ProjectService } from "./projects/projectService.js"; import { ProjectStore } from "./storage/projectStore.js"; import { RemoteMachineRequestError, type MachineClient } from "./machines/machineClient.js"; import { MachineService } from "./machines/machineService.js"; import { MachineStore } from "./machines/machineStore.js"; import { WorkspaceService } from "./workspaces/workspaceService.js"; import type { SessionProxyDaemon } from "./sessiond/sessionProxyRoutes.js"; import { PI_WEB_CAPABILITIES } from "../shared/capabilities.js"; import { machineScopedPluginId } from "../shared/machinePluginIds.js"; import { MAX_IMAGE_PREVIEW_BYTES } from "../shared/workspaceFiles.js"; import type { Project, Workspace } from "./types.js"; let app: FastifyInstance; let tempDir: string; let projectDir: string; let remoteClient: MachineClient | undefined; let sessionDaemonRequests: CapturedSessionDaemonRequest[]; beforeEach(async () => { tempDir = await realpath(await mkdtemp(join(tmpdir(), "pi-web-app-test-"))); projectDir = join(tempDir, "project"); remoteClient = undefined; sessionDaemonRequests = []; app = await buildApp({ projects: new ProjectService(new ProjectStore(join(tempDir, "projects.json"))), workspaces: new WorkspaceService(), machines: new MachineService(new MachineStore(join(tempDir, "machines.json")), { remoteClientFactory: () => { if (remoteClient === undefined) throw new Error("No remote machine client configured"); return remoteClient; }, now: () => new Date("2026-05-25T00:00:00.000Z"), localStatus: () => Promise.resolve({ packageName: "@jmfederico/pi-web", generatedAt: "2026-05-25T00:00:00.000Z", components: { web: { component: "web", label: "PI WEB", stale: false, available: true }, sessiond: { component: "sessiond", label: "PI WEB Session Daemon", stale: false, available: true }, }, release: { packageName: "@jmfederico/pi-web", updateAvailable: false }, commands: { update: "", restart: "", restartSystemd: "", restartDev: "" }, messages: [], }), localRuntime: () => Promise.resolve({ packageName: "@jmfederico/pi-web", generatedAt: "2026-05-25T00:00:00.000Z", components: { web: { component: "web", label: "PI WEB", available: true, capabilities: [PI_WEB_CAPABILITIES.sessionsDeleteArchived] }, sessiond: { component: "sessiond", label: "PI WEB Session Daemon", available: true, capabilities: [PI_WEB_CAPABILITIES.sessionsDeleteArchived] }, }, capabilities: [PI_WEB_CAPABILITIES.sessionsDeleteArchived], }), }), sessionDaemon: fakeSessionDaemon(), piWebPlugins: { manifest: () => Promise.resolve({ plugins: [{ id: "fake", module: "/pi-web-plugins/fake/plugin.js?v=1", source: "test", scope: "local", machineSpecific: false }] }), plugins: () => Promise.resolve({ plugins: [{ id: "fake", module: "/pi-web-plugins/fake/plugin.js?v=1", source: "test", scope: "local", machineSpecific: false, enabled: true }] }), readAsset: (pluginId, assetPath) => Promise.resolve(pluginId === "fake" && assetPath === "plugin.js" ? { content: Buffer.from("export default {};"), contentType: "application/javascript; charset=utf-8" } : undefined), }, clientDist: false, logger: false, }); }); afterEach(async () => { await app.close(); await rm(tempDir, { recursive: true, force: true }); }); describe("buildApp", () => { it("lists synthesized local machine through the HTTP contract", async () => { const response = await app.inject({ method: "GET", url: "/api/machines" }); expect(response.statusCode).toBe(200); expect(response.json()).toEqual({ machines: [{ id: "local", name: "Local", kind: "local", createdAt: "1970-01-01T00:00:00.000Z", updatedAt: "1970-01-01T00:00:00.000Z" }] }); }); it("adds remote machines without exposing tokens", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/", token: "secret" } }); expect(addResponse.statusCode).toBe(200); expect(addResponse.json()).toMatchObject({ name: "Remote", kind: "remote", baseUrl: "https://remote.example.test" }); expect(addResponse.json()).not.toHaveProperty("token"); }); it("reports machine health for local and remote machines", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const requestJson: MachineClient["requestJson"] = () => Promise.resolve({ statusCode: 200, headers: { "content-type": "application/json" }, body: { packageName: "@jmfederico/pi-web", generatedAt: "2026-05-25T00:00:00.000Z", components: { web: { component: "web", label: "Remote Web", stale: false, available: true }, sessiond: { component: "sessiond", label: "Remote Sessiond", stale: false, available: true }, }, release: { packageName: "@jmfederico/pi-web", updateAvailable: false }, commands: { update: "", restart: "", restartSystemd: "", restartDev: "" }, messages: [], }, }); remoteClient = fakeRemoteClient({ requestJson }); const localHealth = await app.inject({ method: "GET", url: "/api/machines/local/health" }); const remoteHealth = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/health` }); expect(localHealth.statusCode).toBe(200); expect(localHealth.json()).toMatchObject({ machineId: "local", ok: true, status: "online" }); expect(remoteHealth.statusCode).toBe(200); expect(remoteHealth.json()).toMatchObject({ machineId: remote.id, ok: true, status: "online" }); }); it("reports effective machine runtime capabilities for remote machines", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const requestJson = vi.fn(() => Promise.resolve({ statusCode: 200, headers: { "content-type": "application/json" }, body: { packageName: "@jmfederico/pi-web", generatedAt: "2026-05-25T00:00:00.000Z", components: { web: { component: "web", label: "Remote Web", runtimeVersion: "1.0.0", available: true, capabilities: [PI_WEB_CAPABILITIES.sessionsDeleteArchived] }, sessiond: { component: "sessiond", label: "Remote Sessiond", runtimeVersion: "1.0.0", available: true, capabilities: [PI_WEB_CAPABILITIES.sessionsDeleteArchived] }, }, capabilities: [PI_WEB_CAPABILITIES.sessionsDeleteArchived], }, })); remoteClient = fakeRemoteClient({ requestJson }); const runtime = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/runtime` }); expect(runtime.statusCode).toBe(200); expect(runtime.json()).toMatchObject({ machineId: remote.id, ok: true, capabilities: [PI_WEB_CAPABILITIES.sessionsDeleteArchived] }); expect(requestJson).toHaveBeenCalledWith("GET", "/api/pi-web/runtime", undefined, { timeoutMs: 3000 }); }); it("proxies allowlisted remote HTTP routes through the selected machine", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const request = vi.fn(() => Promise.resolve({ statusCode: 200, headers: { "content-type": "application/json", connection: "close" }, body: Readable.from([JSON.stringify([{ id: "p1", name: "Remote Project", path: "/repo", createdAt: "now" }])]), })); remoteClient = fakeRemoteClient({ request }); const response = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/projects?active=true` }); expect(response.statusCode).toBe(200); expect(response.headers["content-type"]).toContain("application/json"); expect(response.json()).toEqual([{ id: "p1", name: "Remote Project", path: "/repo", createdAt: "now" }]); expect(request).toHaveBeenCalledWith("GET", "/api/projects?active=true", undefined); }); it("preserves remote file preview security headers while proxying safe response metadata", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const request = vi.fn(() => Promise.resolve({ statusCode: 200, headers: { "content-type": "image/svg+xml", "content-security-policy": "sandbox; default-src 'none'; img-src 'self' data: blob:; style-src 'unsafe-inline'", "x-content-type-options": "nosniff", "set-cookie": "session=secret", }, body: Readable.from([""]), })); remoteClient = fakeRemoteClient({ request }); const response = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/projects/p1/workspaces/w1/file/preview?path=${encodeURIComponent("diagram.svg")}` }); expect(response.statusCode).toBe(200); expect(response.headers["content-type"]).toContain("image/svg+xml"); expect(response.headers["content-security-policy"]).toContain("sandbox"); expect(response.headers["x-content-type-options"]).toBe("nosniff"); expect(response.headers["set-cookie"]).toBeUndefined(); expect(response.body).toBe(""); expect(request).toHaveBeenCalledWith("GET", "/api/projects/p1/workspaces/w1/file/preview?path=diagram.svg", undefined); }); it("proxies remote terminal command-run and continue routes", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const request = vi.fn((method: string, path: string) => Promise.resolve({ statusCode: 200, headers: { "content-type": "application/json" }, body: Readable.from([JSON.stringify({ method, path })]), })); remoteClient = fakeRemoteClient({ request }); const createBody = { origin: "core", title: "Build", command: "npm test", metadata: { "pi.operation": "test" } }; const deleteWorkspaceResponse = await app.inject({ method: "DELETE", url: `/api/machines/${remote.id}/projects/p1/workspaces/w1` }); const createResponse = await app.inject({ method: "POST", url: `/api/machines/${remote.id}/projects/p1/workspaces/w1/terminal-command-runs`, payload: createBody }); const listResponse = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/terminal-command-runs?projectId=p1&statuses=running` }); const getResponse = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/terminal-command-runs/run1` }); const cancelResponse = await app.inject({ method: "POST", url: `/api/machines/${remote.id}/terminal-command-runs/run1/cancel` }); const closeWorkspaceTerminalsResponse = await app.inject({ method: "DELETE", url: `/api/machines/${remote.id}/projects/p1/workspaces/w1/terminals` }); const continueResponse = await app.inject({ method: "POST", url: `/api/machines/${remote.id}/projects/p1/workspaces/w1/terminals/t1/continue` }); expect(deleteWorkspaceResponse.json()).toEqual({ method: "DELETE", path: "/api/projects/p1/workspaces/w1" }); expect(createResponse.json()).toEqual({ method: "POST", path: "/api/projects/p1/workspaces/w1/terminal-command-runs" }); expect(listResponse.json()).toEqual({ method: "GET", path: "/api/terminal-command-runs?projectId=p1&statuses=running" }); expect(getResponse.json()).toEqual({ method: "GET", path: "/api/terminal-command-runs/run1" }); expect(cancelResponse.json()).toEqual({ method: "POST", path: "/api/terminal-command-runs/run1/cancel" }); expect(closeWorkspaceTerminalsResponse.json()).toEqual({ method: "DELETE", path: "/api/projects/p1/workspaces/w1/terminals" }); expect(continueResponse.json()).toEqual({ method: "POST", path: "/api/projects/p1/workspaces/w1/terminals/t1/continue" }); expect(request).toHaveBeenCalledWith("POST", "/api/projects/p1/workspaces/w1/terminal-command-runs", createBody); }); it("forwards remote JSON request bodies and normalizes remote timeouts", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const request = vi.fn(() => Promise.reject(new RemoteMachineRequestError("timed out", 504))); remoteClient = fakeRemoteClient({ request }); const response = await app.inject({ method: "POST", url: `/api/machines/${remote.id}/sessions/s1/prompt`, payload: { text: "hello" } }); expect(response.statusCode).toBe(504); expect(response.json()).toMatchObject({ error: "Remote machine timeout", machineId: remote.id, statusCode: 504 }); expect(request).toHaveBeenCalledWith("POST", "/api/sessions/s1/prompt", { text: "hello" }); }); it("adds, lists, and closes projects through the HTTP contract", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/projects", payload: { name: "Example", path: projectDir, create: true }, }); expect(addResponse.statusCode).toBe(200); const project = addResponse.json(); expect(project).toMatchObject({ name: "Example", path: projectDir }); expect(project.id).not.toBe(""); const listResponse = await app.inject({ method: "GET", url: "/api/projects" }); expect(listResponse.statusCode).toBe(200); expect(listResponse.json()).toEqual([project]); const closeResponse = await app.inject({ method: "DELETE", url: `/api/projects/${project.id}` }); expect(closeResponse.statusCode).toBe(200); expect(closeResponse.json()).toEqual({ closed: true }); const emptyListResponse = await app.inject({ method: "GET", url: "/api/projects" }); expect(emptyListResponse.json()).toEqual([]); }); it("serves local session and terminal proxy routes through machine-scoped aliases", async () => { const sessionsResponse = await app.inject({ method: "GET", url: `/api/machines/local/sessions?cwd=${encodeURIComponent(projectDir)}` }); expect(sessionsResponse.statusCode).toBe(200); expect(sessionsResponse.json()).toEqual({ method: "GET", path: `/sessions?cwd=${encodeURIComponent(projectDir)}` }); expect(sessionDaemonRequests).toEqual([{ method: "GET", path: `/sessions?cwd=${encodeURIComponent(projectDir)}` }]); const addResponse = await app.inject({ method: "POST", url: "/api/machines/local/projects", payload: { name: "Machine Local", path: projectDir, create: true }, }); const project = addResponse.json(); const workspacesResponse = await app.inject({ method: "GET", url: `/api/machines/local/projects/${project.id}/workspaces` }); const workspace = workspacesResponse.json()[0]; if (workspace === undefined) throw new Error("Expected workspace"); const terminalResponse = await app.inject({ method: "POST", url: `/api/machines/local/projects/${project.id}/workspaces/${workspace.id}/terminal-command-runs`, payload: { origin: "core", title: "Build", command: "npm test", metadata: { "pi.operation": "test" } }, }); const closeTerminalsResponse = await app.inject({ method: "DELETE", url: `/api/machines/local/projects/${project.id}/workspaces/${workspace.id}/terminals` }); expect(terminalResponse.statusCode).toBe(200); expect(terminalResponse.json()).toEqual({ method: "POST", path: "/terminal-command-runs", body: { origin: "core", projectId: project.id, workspaceId: workspace.id, cwd: projectDir, title: "Build", command: "npm test", metadata: { "pi.operation": "test" }, }, }); expect(closeTerminalsResponse.statusCode).toBe(200); expect(closeTerminalsResponse.json()).toEqual({ method: "DELETE", path: `/terminals?cwd=${encodeURIComponent(projectDir)}` }); expect(sessionDaemonRequests[1]).toEqual({ method: "POST", path: "/terminal-command-runs", body: { origin: "core", projectId: project.id, workspaceId: workspace.id, cwd: projectDir, title: "Build", command: "npm test", metadata: { "pi.operation": "test" }, }, }); expect(sessionDaemonRequests[2]).toEqual({ method: "DELETE", path: `/terminals?cwd=${encodeURIComponent(projectDir)}` }); }); it("serves local projects and workspaces through machine-scoped aliases", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines/local/projects", payload: { name: "Machine Local", path: projectDir, create: true }, }); expect(addResponse.statusCode).toBe(200); const project = addResponse.json(); const listResponse = await app.inject({ method: "GET", url: "/api/machines/local/projects" }); expect(listResponse.statusCode).toBe(200); expect(listResponse.json()).toEqual([project]); const workspacesResponse = await app.inject({ method: "GET", url: `/api/machines/local/projects/${project.id}/workspaces` }); expect(workspacesResponse.statusCode).toBe(200); expect(workspacesResponse.json()).toEqual([expect.objectContaining({ projectId: project.id, path: projectDir })]); }); it("serves the PI WEB plugin manifest and plugin assets", async () => { const manifestResponse = await app.inject({ method: "GET", url: "/pi-web-plugins/manifest.json" }); expect(manifestResponse.statusCode).toBe(200); expect(manifestResponse.json()).toEqual({ plugins: [{ id: "fake", module: "/pi-web-plugins/fake/plugin.js?v=1", source: "test", scope: "local", machineSpecific: false }] }); const pluginsResponse = await app.inject({ method: "GET", url: "/api/plugins" }); expect(pluginsResponse.statusCode).toBe(200); expect(pluginsResponse.json()).toEqual({ plugins: [{ id: "fake", module: "/pi-web-plugins/fake/plugin.js?v=1", source: "test", scope: "local", machineSpecific: false, enabled: true }] }); const assetResponse = await app.inject({ method: "GET", url: "/pi-web-plugins/fake/plugin.js?v=1" }); expect(assetResponse.statusCode).toBe(200); expect(assetResponse.headers["content-type"]).toContain("application/javascript"); expect(assetResponse.body).toBe("export default {};"); const missingResponse = await app.inject({ method: "GET", url: "/pi-web-plugins/fake/missing.js" }); expect(missingResponse.statusCode).toBe(404); }); it("rewrites and proxies remote machine plugin manifests and assets", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const requestJson = vi.fn(() => Promise.resolve({ statusCode: 200, headers: { "content-type": "application/json" }, body: { plugins: [{ id: "remote-tools", module: "/pi-web-plugins/remote-tools/pi-web-plugin.js?v=123", source: "local", scope: "local", machineSpecific: true }] }, })); const request = vi.fn(() => Promise.resolve({ statusCode: 200, headers: { "content-type": "application/javascript", "set-cookie": "secret=1" }, body: Readable.from(["export default {};"]), })); remoteClient = fakeRemoteClient({ requestJson, request }); const manifestResponse = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/pi-web-plugins/manifest.json` }); const scopedPluginId = machineScopedPluginId(remote.id, "remote-tools"); expect(manifestResponse.statusCode).toBe(200); expect(manifestResponse.json()).toEqual({ plugins: [{ id: "remote-tools", module: `/pi-web-plugins/${scopedPluginId}/pi-web-plugin.js?v=123`, source: "local", scope: "local", machineSpecific: true }], }); expect(requestJson).toHaveBeenCalledWith("GET", "/pi-web-plugins/manifest.json", undefined, { timeoutMs: 10000 }); const assetResponse = await app.inject({ method: "GET", url: `/pi-web-plugins/${scopedPluginId}/pi-web-plugin.js?v=123` }); expect(assetResponse.statusCode).toBe(200); expect(assetResponse.headers["content-type"]).toContain("application/javascript"); expect(assetResponse.headers["set-cookie"]).toBeUndefined(); expect(assetResponse.body).toBe("export default {};"); expect(request).toHaveBeenCalledWith("GET", "/pi-web-plugins/remote-tools/pi-web-plugin.js?v=123"); }); it("drops unsafe remote machine plugin manifest modules", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); remoteClient = fakeRemoteClient({ requestJson: vi.fn(() => Promise.resolve({ statusCode: 200, headers: { "content-type": "application/json" }, body: { plugins: [ { id: "safe-tools", module: "nested/pi-web-plugin.js?v=1", source: "local", scope: "local" }, { id: "traversal-tools", module: "..%2F..%2Fapi%2Fconfig", source: "local", scope: "local" }, { id: "wrong-root", module: "/pi-web-plugins/other/pi-web-plugin.js", source: "local", scope: "local" }, ], }, })), }); const manifestResponse = await app.inject({ method: "GET", url: `/api/machines/${remote.id}/pi-web-plugins/manifest.json` }); expect(manifestResponse.statusCode).toBe(200); expect(manifestResponse.json()).toEqual({ plugins: [{ id: "safe-tools", module: `/pi-web-plugins/${machineScopedPluginId(remote.id, "safe-tools")}/nested/pi-web-plugin.js?v=1`, source: "local", scope: "local" }], }); }); it("rejects remote machine plugin asset traversal before proxying", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/machines", payload: { name: "Remote", baseUrl: "https://remote.example.test/" } }); const remote = addResponse.json<{ id: string }>(); const request = vi.fn(() => Promise.resolve({ statusCode: 200, headers: {}, body: Readable.from([]) })); remoteClient = fakeRemoteClient({ request }); const scopedPluginId = machineScopedPluginId(remote.id, "remote-tools"); const response = await app.inject({ method: "GET", url: `/pi-web-plugins/${scopedPluginId}/..%2F..%2Fapi%2Fconfig` }); expect(response.statusCode).toBe(400); expect(response.json()).toEqual({ error: "Invalid remote PI WEB plugin asset path" }); expect(request).not.toHaveBeenCalled(); }); it("returns stable errors for invalid project requests", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/projects", payload: { name: "Missing", path: join(tempDir, "missing") }, }); expect(addResponse.statusCode).toBe(400); expect(addResponse.json()).toHaveProperty("error"); const closeResponse = await app.inject({ method: "DELETE", url: "/api/projects/does-not-exist" }); expect(closeResponse.statusCode).toBe(404); expect(closeResponse.json()).toEqual({ error: "Project not found" }); }); it("lists a non-git project as a single workspace", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/projects", payload: { name: "Plain", path: projectDir, create: true }, }); const project = addResponse.json(); const workspacesResponse = await app.inject({ method: "GET", url: `/api/projects/${project.id}/workspaces` }); expect(workspacesResponse.statusCode).toBe(200); expect(workspacesResponse.json()).toEqual([ expect.objectContaining({ projectId: project.id, path: projectDir, label: "Plain", isMain: true, isGitRepo: false, isGitWorktree: false, }), ]); }); it("serves supported workspace images as previews", async () => { const addResponse = await app.inject({ method: "POST", url: "/api/projects", payload: { name: "Images", path: projectDir, create: true }, }); const project = addResponse.json(); const svg = ""; await writeFile(join(projectDir, "diagram.svg"), svg); await writeFile(join(projectDir, "note.txt"), "hello"); await writeFile(join(projectDir, "huge.png"), ""); await truncate(join(projectDir, "huge.png"), MAX_IMAGE_PREVIEW_BYTES + 1); const workspacesResponse = await app.inject({ method: "GET", url: `/api/projects/${project.id}/workspaces` }); const workspace = workspacesResponse.json()[0]; if (workspace === undefined) throw new Error("Expected workspace"); const previewResponse = await app.inject({ method: "GET", url: `/api/projects/${project.id}/workspaces/${workspace.id}/file/preview?path=${encodeURIComponent("diagram.svg")}` }); expect(previewResponse.statusCode).toBe(200); expect(previewResponse.headers["content-type"]).toContain("image/svg+xml"); expect(previewResponse.headers["cache-control"]).toBe("private, max-age=3600"); expect(previewResponse.headers["content-security-policy"]).toContain("sandbox"); expect(previewResponse.headers["x-content-type-options"]).toBe("nosniff"); expect(previewResponse.body).toBe(svg); const rejectedResponse = await app.inject({ method: "GET", url: `/api/projects/${project.id}/workspaces/${workspace.id}/file/preview?path=${encodeURIComponent("note.txt")}` }); expect(rejectedResponse.statusCode).toBe(400); expect(rejectedResponse.json()).toEqual({ error: "Image preview is not supported for this file type" }); const tooLargeResponse = await app.inject({ method: "GET", url: `/api/projects/${project.id}/workspaces/${workspace.id}/file/preview?path=${encodeURIComponent("huge.png")}` }); expect(tooLargeResponse.statusCode).toBe(400); expect(tooLargeResponse.json()).toEqual({ error: "Image is too large to preview (limit 10 MB)" }); }); }); interface CapturedSessionDaemonRequest { method: string; path: string; body?: unknown; } function fakeSessionDaemon(): SessionProxyDaemon { return { request: (method, path, body) => { const captured = { method, path, ...(body === undefined ? {} : { body }) } satisfies CapturedSessionDaemonRequest; sessionDaemonRequests.push(captured); return Promise.resolve({ statusCode: 200, headers: { "content-type": "application/json" }, body: JSON.stringify(captured), }); }, connectWebSocket: () => { throw new Error("WebSocket not configured for test"); }, }; } function fakeRemoteClient(overrides: Partial): MachineClient { return { request: () => Promise.resolve({ statusCode: 200, headers: {}, body: Readable.from([]) }), requestJson: () => Promise.resolve({ statusCode: 200, headers: {}, body: undefined }), connectWebSocket: () => { throw new Error("WebSocket not configured for test"); }, ...overrides, }; }