import { Database } from "bun:sqlite"; import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { drizzle } from "drizzle-orm/bun-sqlite"; import { migrate } from "drizzle-orm/bun-sqlite/migrator"; import { MIGRATIONS_FOLDER } from "../db/fixtures"; import { devices, timelineEvents } from "../db/schema"; import { getUnassignedWork } from "./unassigned"; let tmpDir: string; let sqlite: Database; // biome-ignore lint/suspicious/noExplicitAny: drizzle bun-sqlite handle let db: any; beforeEach(() => { tmpDir = mkdtempSync(join(tmpdir(), "scribe-unassigned-")); sqlite = new Database(join(tmpDir, "scribe.db"), { create: true }); db = drizzle({ client: sqlite }); migrate(db, { migrationsFolder: MIGRATIONS_FOLDER }); }); afterEach(() => { sqlite.close(); rmSync(tmpDir, { recursive: true, force: true }); }); async function insertDevice(id: string, name: string) { await db.insert(devices).values({ id, name, userId: "u1" }); } // Insert an UNASSIGNED (no projectRootId) file event. async function insertFileEvent( id: string, path: string, timestamp: Date, deviceId?: string, ) { await db.insert(timelineEvents).values({ id, timestamp, source: "file", deviceId: deviceId ?? null, metadata: { path, size: 1, extension: "ts" }, }); } async function insertGitEvent(id: string, remote: string, timestamp: Date) { await db.insert(timelineEvents).values({ id, timestamp, source: "git", metadata: { message: "commit", author: "me", filesChanged: 1, gitRemote: remote, }, }); } const OPTS = { maxEvents: 50000, canClassify: true }; describe("getUnassignedWork", () => { test("returns an empty result for an empty db", async () => { const work = await getUnassignedWork(db, OPTS); expect(work.totalUnassigned).toBe(0); expect(work.limitReached).toBe(false); expect(work.fileGroups).toEqual([]); expect(work.claudeGroups).toEqual([]); expect(work.gitGroups).toEqual([]); expect(work.canClassify).toBe(true); }); test("groups events by source and folder", async () => { await insertFileEvent("f1", "/Users/x/code/scribe/a.ts", new Date()); await insertFileEvent("f2", "/Users/x/code/scribe/b.ts", new Date()); await insertGitEvent("g1", "github.com/o/repo", new Date()); const work = await getUnassignedWork(db, OPTS); expect(work.totalUnassigned).toBe(3); // Two file events under code/scribe collapse into one group of count 2. expect(work.fileGroups.length).toBe(1); expect(work.fileGroups[0].count).toBe(2); expect(work.fileGroups[0].displayName).toBe("scribe"); expect(work.gitGroups.length).toBe(1); expect(work.gitGroups[0].count).toBe(1); }); test("reports the true total even when the grouping scan is capped", async () => { await insertFileEvent("f1", "/Users/x/code/scribe/a.ts", new Date()); await insertFileEvent("f2", "/Users/x/code/scribe/b.ts", new Date()); const work = await getUnassignedWork(db, { ...OPTS, maxEvents: 1 }); // totalUnassigned is a real COUNT (both events), not the capped scan length. expect(work.totalUnassigned).toBe(2); // limitReached flags that the grouping scan (not the total) was capped. expect(work.limitReached).toBe(true); }); test("passes canClassify through (hosted/read-only)", async () => { const work = await getUnassignedWork(db, { ...OPTS, canClassify: false }); expect(work.canClassify).toBe(false); }); describe("device scope (hub multi-tenant seam)", () => { test("undefined deviceIds means no filter (desktop/local)", async () => { await insertDevice("d1", "Laptop"); await insertDevice("d2", "Desktop"); await insertFileEvent("f1", "/Users/x/code/a/x.ts", new Date(), "d1"); await insertFileEvent("f2", "/Users/x/code/b/y.ts", new Date(), "d2"); const work = await getUnassignedWork(db, { ...OPTS, deviceIds: undefined, }); expect(work.totalUnassigned).toBe(2); }); test("an empty deviceIds array yields an empty result", async () => { await insertFileEvent("f1", "/Users/x/code/a/x.ts", new Date(), "d1"); const work = await getUnassignedWork(db, { ...OPTS, deviceIds: [] }); expect(work.totalUnassigned).toBe(0); expect(work.fileGroups).toEqual([]); }); test("filters events to the scoped device IDs only", async () => { await insertDevice("d1", "Laptop"); await insertDevice("d2", "Desktop"); await insertFileEvent("f1", "/Users/x/code/a/x.ts", new Date(), "d1"); await insertFileEvent("f2", "/Users/x/code/b/y.ts", new Date(), "d2"); const work = await getUnassignedWork(db, { ...OPTS, deviceIds: ["d1"] }); expect(work.totalUnassigned).toBe(1); expect(work.fileGroups.length).toBe(1); expect(work.fileGroups[0].displayName).toBe("a"); }); }); });