import { beforeEach, describe, expect, mock, test } from "bun:test"; import { Database } from "bun:sqlite"; const encoder = new TextEncoder(); let head = "c1"; const commits: Record = { c1: "t1", c2: "t2" }; const blobs: Record = { app: encoder.encode("export const a = 1;\n".repeat(50)), util: encoder.encode("package main\n".repeat(400)), vendored: encoder.encode("var x = 1;\n".repeat(500)), image: new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0, 0, 0]), big: encoder.encode("x".repeat(200 * 1024)), attrs: encoder.encode("*.go linguist-language=Golang\n"), }; const trees: Record> = { t1: [ { path: "src/app.ts", sha: "app", mode: "100644" }, { path: "cmd/util.go", sha: "util", mode: "100644" }, { path: "vendor/lib.js", sha: "vendored", mode: "100644" }, { path: "logo.png", sha: "image", mode: "100644" }, { path: "gen/huge.py", sha: "big", mode: "100644" }, ], }; const writes: Array<{ sql: string; params: unknown[] }> = []; mock.module("cloudflare:workers", () => ({ DurableObject: class { constructor( protected ctx: unknown, protected env: unknown, ) {} }, env: {}, })); mock.module("../repos", () => ({ loadRepoById: async () => ({ id: "r1", status: "ready", default_branch: "main", artifacts_name: "art" }), })); mock.module("../git/refs", () => ({ branchSha: () => head, listRefs: async () => [] })); mock.module("../db", () => ({ run: async (sql: string, ...params: unknown[]) => void writes.push({ sql, params }), })); mock.module("../git/diff", () => ({ diffTreesBounded: async () => ({ truncated: false, changes: [ { path: "src/app.ts", status: "deleted", newSha: null, newMode: null, oldPath: null }, { path: ".gitattributes", status: "added", newSha: "attrs", newMode: "100644", oldPath: null }, ], }), })); mock.module("../git/store", () => ({ withStore: async (_name: string, run: (store: unknown) => Promise) => run({ commit: async (sha: string) => ({ treeSha: commits[sha] }), walk: async (tree: string) => ({ entries: trees[tree] ?? [], truncated: false }), blob: async (sha: string) => blobs[sha] ?? null, entryAtPath: async (tree: string, path: string) => tree === "t2" && path === ".gitattributes" ? { sha: "attrs", type: "blob" } : null, }), })); const { RepoSearch } = await import("./repo-search-do"); let db = new Database(":memory:"); 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), delete: async (key: string) => void stored.delete(key), getAlarm: async () => null, setAlarm: async () => undefined, transactionSync: (run: () => void) => run(), }; return new RepoSearch({ storage } as never, {} as never); } beforeEach(() => { db = new Database(":memory:"); stored.clear(); writes.length = 0; head = "c1"; }); describe("language breakdown", () => { test("a pass sums text files per language, skipping vendored and binary files, and writes the row once", async () => { stored.set("repoId", "r1"); await object().alarm(); expect(writes).toHaveLength(1); expect(writes[0].params).toEqual([ JSON.stringify([ { name: "Python", bytes: 200 * 1024 }, { name: "Go", bytes: 5200 }, { name: "TypeScript", bytes: 1000 }, ]), "Python", "r1", ]); stored.delete("state"); await object().alarm(); expect(writes).toHaveLength(1); }); test("an incremental pass applies deletions and the new .gitattributes", async () => { stored.set("repoId", "r1"); await object().alarm(); head = "c2"; await object().alarm(); expect(writes.at(-1)?.params).toEqual([ JSON.stringify([ { name: "Python", bytes: 200 * 1024 }, { name: "Golang", bytes: 5200 }, ]), "Python", "r1", ]); }); test("an index from before file sizes were kept is rebuilt in full", async () => { stored.set("repoId", "r1"); stored.set("state", { commitSha: "c1", treeSha: "t1", fileCount: 0, truncated: false, updatedAt: 0 }); await object().alarm(); expect(writes).toHaveLength(1); expect((stored.get("state") as { schema: number }).schema).toBe(2); }); });