import { describe, expect, test } from "bun:test"; import type { CapabilityContext } from "./context"; import { clearLocalMappings } from "./mapping"; /** * Minimal awaitable stand-in for the Drizzle query builder used by * clearLocalMappings: `select().from().where()` resolves the next array from a * FIFO queue; `update().set().where()` captures the set payload and how many * times it ran. Mirrors the mock style in billing.test.ts. */ function makeClearDb(selectQueue: unknown[][]) { let si = 0; let mode: "select" | "update" | null = null; const captured = { sets: [] as Array>, updates: 0 }; const chain = { select: () => { mode = "select"; return chain; }, from: () => chain, where: () => chain, update: () => { mode = "update"; captured.updates++; return chain; }, set: (v: Record) => { captured.sets.push(v); return chain; }, // biome-ignore lint/suspicious/noThenProperty: deliberately-awaitable Drizzle stand-in then: (resolve: (rows: unknown[]) => unknown) => resolve(mode === "select" ? (selectQueue[si++] ?? []) : []), }; return { chain, captured }; } function ctxFor( db: unknown, organizationId?: string | null, ): CapabilityContext { return { db, organizationId } as unknown as CapabilityContext; } describe("clearLocalMappings", () => { test("refuses to run on the hosted hub (organizationId present)", async () => { const { chain, captured } = makeClearDb([]); const result = await clearLocalMappings.execute( ctxFor(chain, "org_123"), {}, ); expect("error" in result).toBe(true); // Never touched the DB on the hub path. expect(captured.updates).toBe(0); }); test("un-assigns every mapped root locally and reports the count", async () => { const { chain, captured } = makeClearDb([[{ id: "r1" }, { id: "r2" }]]); const result = await clearLocalMappings.execute(ctxFor(chain, null), {}); expect(result).toMatchObject({ success: true, cleared: 2 }); expect(captured.updates).toBe(1); // Roots return to Ungrouped and lose the manual-assignment flag. expect(captured.sets[0]).toEqual({ projectId: null, manuallyAssigned: false, }); }); test("is a no-op when nothing is mapped", async () => { const { chain, captured } = makeClearDb([[]]); const result = await clearLocalMappings.execute(ctxFor(chain, null), {}); expect(result).toMatchObject({ success: true, cleared: 0 }); // No update issued when there is nothing to clear. expect(captured.updates).toBe(0); }); });