import { describe, it, expect } from "vitest"; import { listRegistries, createRegistry, getRegistry } from "../src/services/registries.js"; import { createScan, listScans, getScan } from "../src/services/scans.js"; import type { PoolLike } from "../src/services/registries.js"; function fakePool(handlers: { query: (text: string, values?: unknown[]) => { rows: Record[] }; }): PoolLike & { queries: { text: string; values?: unknown[] }[] } { const queries: { text: string; values?: unknown[] }[] = []; return { queries, query(text, values) { queries.push({ text, values }); return handlers.query(text, values); }, }; } const ROW = { id: "11111111-1111-1111-1111-111111111111", owner_sub: "svc-user", name: "example", url: "https://example.com", config: {}, created_at: new Date("2026-09-16T10:00:00Z"), }; describe("registries service", () => { it("listRegistries filters by owner and tenant", async () => { const db = fakePool({ query: () => ({ rows: [ROW] }) }); const out = await listRegistries(db, "svc-user", "org-1"); expect(out).toHaveLength(1); expect(out[0]).toMatchObject({ id: ROW.id, name: "example", createdAt: "2026-09-16T10:00:00.000Z" }); expect(db.queries[0].values).toEqual(["svc-user", "org-1"]); expect(db.queries[0].text).toContain("owner_sub = $1"); expect(db.queries[0].text).toContain("tenant_id = $2"); }); it("createRegistry inserts with owner, tenant and config jsonb", async () => { const db = fakePool({ query: () => ({ rows: [ROW] }) }); const out = await createRegistry(db, "svc-user", "org-1", { name: "example", url: "https://example.com" }); expect(out.id).toBe(ROW.id); expect(db.queries[0].values?.[0]).toBe("svc-user"); expect(db.queries[0].values?.[1]).toBe("org-1"); expect(db.queries[0].text).toContain("RETURNING"); }); it("getRegistry returns null when not owner (fail-closed)", async () => { const db = fakePool({ query: () => ({ rows: [] }) }); const out = await getRegistry(db, "other-user", "org-1", ROW.id); expect(out).toBeNull(); }); }); describe("scans service", () => { const SCAN_ROW = { ...ROW, registry_id: ROW.id, tenant_id: "org-1", status: "pending", result: {}, updated_at: ROW.created_at, }; it("createScan enforces registry ownership+tenant in SQL and returns null otherwise", async () => { const db = fakePool({ query: () => ({ rows: [] }) }); const out = await createScan(db, "svc-user", "org-1", ROW.id); expect(out).toBeNull(); expect(db.queries[0].text).toContain("owner_sub = $1"); expect(db.queries[0].text).toContain("tenant_id = $2"); }); it("createScan returns scan for owned registry", async () => { const db = fakePool({ query: () => ({ rows: [SCAN_ROW] }) }); const out = await createScan(db, "svc-user", "org-1", ROW.id); expect(out).toMatchObject({ id: SCAN_ROW.id, registryId: SCAN_ROW.id, status: "pending" }); }); it("listScans supports optional registryId filter", async () => { const db = fakePool({ query: () => ({ rows: [SCAN_ROW] }) }); await listScans(db, "svc-user", "org-1"); expect(db.queries[0].text).not.toContain("registry_id = $3"); await listScans(db, "svc-user", "org-1", SCAN_ROW.id); expect(db.queries[1].values).toEqual(["svc-user", "org-1", SCAN_ROW.id]); }); it("getScan returns null for foreign scan", async () => { const db = fakePool({ query: () => ({ rows: [] }) }); expect(await getScan(db, "other-user", "org-1", SCAN_ROW.id)).toBeNull(); }); });