import { expect, test, vi } from "vitest"; import { runWorkspaceMaintenanceCli, type WorkspaceMaintenanceIo, } from "../src/workspace-maintenance.js"; import type { WorkspaceOperationResult } from "../src/workspaces/preprocessing-service.js"; function io(stdin: string): WorkspaceMaintenanceIo & { stdout: string[]; stderr: string[] } { const stdout: string[] = []; const stderr: string[] = []; return { stdin, stdout, stderr, writeStdout: (value) => void stdout.push(value), writeStderr: (value) => void stderr.push(value), }; } function ok(operation: string): WorkspaceOperationResult { return { schemaVersion: 1, status: "succeeded", code: "ok", workspaceId: "psd-clinical", workspaceRevision: "a".repeat(40), descriptorBlob: "b".repeat(40), operation, completedStages: [], }; } test("entrypoint emits exactly one pristine JSON document and maps success/block/failure exits", async () => { const service = { inspect: vi.fn(async () => ok("inspect")), preprocessDwh: vi.fn(async () => ({ ...ok("preprocess dwh"), status: "blocked", code: "manual_review_required" as const })), run: vi.fn(async () => ({ ...ok("preprocess run"), status: "failed", code: "semantic_index_incompatible" as const })), } as any; const inspectIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical" })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "inspect"], service, inspectIo)).toBe(0); expect(JSON.parse(inspectIo.stdout.join(""))).toMatchObject({ operation: "inspect", code: "ok" }); expect(inspectIo.stderr.join("")).toBe(""); const blockedIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical" })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "preprocess-dwh"], service, blockedIo)).toBe(3); expect(JSON.parse(blockedIo.stdout.join(""))).toMatchObject({ code: "manual_review_required" }); const failedIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical" })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "preprocess-run"], service, failedIo)).toBe(1); expect(JSON.parse(failedIo.stdout.join(""))).toMatchObject({ code: "semantic_index_incompatible" }); }); test("malformed stdin, unknown commands, and extra fields fail with exit 2 but still return bounded JSON", async () => { const service = {} as any; const malformedIo = io("not-json"); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "inspect"], service, malformedIo)).toBe(2); expect(JSON.parse(malformedIo.stdout.join(""))).toMatchObject({ status: "failed", operation: "inspect" }); const extraFieldIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical", unexpected: true })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "inspect"], service, extraFieldIo)).toBe(2); expect(JSON.parse(extraFieldIo.stdout.join(""))).toMatchObject({ status: "failed", operation: "inspect" }); const unknownIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical" })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "explode"], service, unknownIo)).toBe(2); expect(JSON.parse(unknownIo.stdout.join(""))).toMatchObject({ status: "failed", operation: "explode" }); }); test("raw exception text is redacted from stderr and stdout remains within the public result contract", async () => { const service = { inspect: vi.fn(async () => { throw new Error("https://secret.example.invalid?q=token SELECT * FROM sensitive_table"); }), } as any; const captured = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical" })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "inspect"], service, captured)).toBe(1); expect(JSON.parse(captured.stdout.join(""))).toMatchObject({ status: "failed", operation: "inspect", }); expect(captured.stderr.join("")).not.toContain("secret.example.invalid"); expect(captured.stderr.join("")).not.toContain("SELECT *"); }); test("vector-inspect and vector-rebuild dispatch to the service with the exact envelope", async () => { const service = { vectorInspect: vi.fn(async () => ok("vector inspect")), vectorRebuild: vi.fn(async () => ok("vector rebuild")), } as any; const inspectIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical" })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "vector-inspect"], service, inspectIo)).toBe(0); expect(service.vectorInspect).toHaveBeenCalledWith({ workspaceId: "psd-clinical" }); expect(JSON.parse(inspectIo.stdout.join(""))).toMatchObject({ operation: "vector inspect", code: "ok" }); const rebuildIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical", collection: "psd-clinical", confirm: "psd-clinical", destroy: true })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "vector-rebuild"], service, rebuildIo)).toBe(0); expect(service.vectorRebuild).toHaveBeenCalledWith({ workspaceId: "psd-clinical", collection: "psd-clinical", confirm: "psd-clinical", destroy: true, }); }); test("vector-rebuild without exact confirmation is refused by the service", async () => { const service = { vectorRebuild: vi.fn(async () => ({ ...ok("vector rebuild"), status: "failed", code: "semantic_index_incompatible" as const })), } as any; const rebuildIo = io(JSON.stringify({ schemaVersion: 1, workspaceId: "psd-clinical", collection: "other", confirm: "other", destroy: true })); expect(await runWorkspaceMaintenanceCli(["node", "workspace-maintenance", "vector-rebuild"], service, rebuildIo)).toBe(1); });