import * as Schema from "effect/Schema"; import { describe, expect, it } from "vite-plus/test"; import { ProjectReadFileError, ProjectSearchContentsError, ProjectSearchContentsInput, ProjectSearchEntriesError, ProjectSearchEntriesInput, ProjectWriteFileError, } from "./project.ts"; const decodeSearchEntriesInput = Schema.decodeUnknownSync(ProjectSearchEntriesInput); const decodeSearchContentsInput = Schema.decodeUnknownSync(ProjectSearchContentsInput); describe("project search inputs", () => { it("allows an empty entries query for bounded frecency browsing", () => { const decoded = decodeSearchEntriesInput({ cwd: "/workspace", query: " ", limit: 10, kind: "file", }); expect(decoded.query).toBe(""); }); it("preserves whitespace in content search queries", () => { const decoded = decodeSearchContentsInput({ cwd: "/workspace", query: " foo ", limit: 10, caseSensitive: false, wholeWord: false, useRegex: false, }); expect(decoded.query).toBe(" foo "); }); }); describe("project RPC errors", () => { it("derives stable messages from structured request context while retaining causes", () => { const cause = new Error("sensitive platform detail"); const searchError = new ProjectSearchEntriesError({ cwd: "/workspace", queryLength: "authorization: Bearer secret-token".length, limit: 20, failure: "search_index_search_failed", normalizedCwd: "/workspace", detail: "index unavailable", cause, }); const readError = new ProjectReadFileError({ cwd: "/workspace", relativePath: "src/index.ts", failure: "operation_failed", operation: "read", operationPath: "/workspace/src/index.ts", resolvedPath: "/workspace/src/index.ts", cause, }); expect(searchError.message).toBe("Failed to search workspace entries in '/workspace'."); expect(searchError.message).not.toContain(cause.message); expect(searchError.normalizedCwd).toBe("/workspace"); expect(searchError.queryLength).toBe("authorization: Bearer secret-token".length); expect(searchError).not.toHaveProperty("query"); expect(searchError.message).not.toMatch(/Bearer|secret-token/); expect(searchError.cause).toBe(cause); expect(readError.message).toBe("Failed to read workspace file 'src/index.ts' in '/workspace'."); expect(readError.message).not.toContain(cause.message); expect(readError.cause).toBe(cause); const contentSearchError = new ProjectSearchContentsError({ cwd: "/workspace", queryLength: "authorization: Bearer secret-token".length, limit: 100, failure: "search_index_search_failed", cause, }); expect(contentSearchError.message).toBe("Failed to search workspace contents in '/workspace'."); expect(contentSearchError.message).not.toContain(cause.message); expect(contentSearchError).not.toHaveProperty("query"); expect(contentSearchError.cause).toBe(cause); }); it("decodes legacy message-only errors during rolling upgrades", () => { const decodeSearchError = Schema.decodeUnknownSync(ProjectSearchEntriesError); const decodeWriteError = Schema.decodeUnknownSync(ProjectWriteFileError); const searchError = decodeSearchError({ _tag: "ProjectSearchEntriesError", message: "Legacy project search failure.", query: "legacy sensitive query", }); const writeError = decodeWriteError({ _tag: "ProjectWriteFileError", message: "Legacy project write failure.", }); expect(searchError.message).toBe("Legacy project search failure."); expect(searchError.cwd).toBeUndefined(); expect(searchError.queryLength).toBeUndefined(); expect(searchError).not.toHaveProperty("query"); expect(searchError.failure).toBeUndefined(); expect(writeError.message).toBe("Legacy project write failure."); expect(writeError.relativePath).toBeUndefined(); expect(writeError.failure).toBeUndefined(); }); });