import { beforeEach, describe, expect, mock, test } from "bun:test"; import { Database } from "bun:sqlite"; mock.module("cloudflare:workers", () => ({ DurableObject: class { constructor( protected ctx: unknown, protected env: unknown, ) {} }, env: {}, })); mock.module("../repos", () => ({ loadRepoById: async () => null })); mock.module("../git/refs", () => ({ branchSha: () => null, listRefs: async () => [] })); mock.module("../git/store", () => ({ withStore: async () => undefined })); mock.module("../git/diff", () => ({ diffTreesBounded: async () => ({ changes: [], truncated: false }) })); const { RepoSearch } = await import("./repo-search-do"); let db = new Database(":memory:"); let alarm: number | null = null; const stored = new Map(); function object() { const storage = { sql: { exec: (query: string, ...params: Array) => { const rows = db.query(query).all(...params) as Record[]; return Object.assign(rows[Symbol.iterator](), { toArray: () => rows, one: () => rows[0] }); }, }, get: async (key: string) => stored.get(key), put: async (key: string, value: unknown) => void stored.set(key, value), getAlarm: async () => alarm, setAlarm: async (at: number) => void (alarm = at), transactionSync: (run: () => void) => run(), }; return new RepoSearch({ storage } as never, {} as never); } function index(path: string, sha: string, content: string): void { const id = Number( db.query("INSERT INTO code_files (path, blob_sha) VALUES (?, ?) RETURNING id").get(path, sha)?.["id" as never], ); db.query("INSERT INTO code_fts (rowid, path, content) VALUES (?, ?, ?)").run(id, path, content); } beforeEach(async () => { db = new Database(":memory:"); alarm = null; stored.clear(); await object().grep("r1", { pattern: "x" }, [], 10, 1000); stored.set("state", { commitSha: "c", treeSha: "t" }); }); describe("indexed editor grep", () => { test("searches only head files whose indexed blob matches, and hands the rest back", async () => { index("src/a.ts", "a1", "const handler = 1;\nfunction handleRequest() {}"); index("src/b.ts", "b-old", "handleRequest stale"); index("src/c.ts", "c1", "nothing here"); const result = await object().grep( "r1", { pattern: "handle\\w+", isRegExp: true }, [ ["src/a.ts", "a1"], ["src/b.ts", "b-new"], ["src/c.ts", "c1"], ["src/new.ts", "n1"], ], 10, 1000, ); expect(result.files).toEqual([ { path: "src/a.ts", matches: [ { line: 0, text: "const handler = 1;", ranges: [[6, 13]] }, { line: 1, text: "function handleRequest() {}", ranges: [[9, 22]] }, ], }, ]); expect(result.uncovered).toEqual(["src/b.ts", "src/new.ts"]); expect(result.limitHit).toBe(false); }); test("short or unprovable patterns scan the index without the trigram prefilter, within the limit", async () => { index("a", "1", "ab ab\nab"); index("b", "2", "ab"); const result = await object().grep( "r1", { pattern: "ab" }, [ ["a", "1"], ["b", "2"], ], 2, 1000, ); expect(result.files.flatMap((file) => file.matches)).toHaveLength(2); expect(result.limitHit).toBe(true); }); test("a repository never indexed starts indexing and searches nothing itself", async () => { stored.clear(); alarm = null; const result = await object().grep("r1", { pattern: "abc" }, [["a", "1"]], 10, 1000); expect(result).toEqual({ files: [], uncovered: ["a"], limitHit: false }); expect(stored.get("repoId")).toBe("r1"); expect(alarm).not.toBeNull(); }); });