import { describe, expect, test } from "bun:test"; import { defineAuthz } from "../src/registry.ts"; import { emptyCatalog, mergeCatalogs } from "../src/catalog.ts"; describe("mergeCatalogs", () => { test("merges disjoint modules", () => { const catalog = mergeCatalogs([ { source: "a.ts", module: defineAuthz({ permissions: { "post:read": {} } }) }, { source: "b.ts", module: defineAuthz({ permissions: { "user:read": {} } }) }, ]); expect([...catalog.permissions.keys()].sort()).toEqual(["post:read", "user:read"]); }); test("re-declaring a permission with deep-equal metadata is a no-op", () => { const meta = { title: "View posts", risk: "low" as const }; const catalog = mergeCatalogs([ { source: "a.ts", module: defineAuthz({ permissions: { "post:read": meta } }) }, { source: "b.ts", module: defineAuthz({ permissions: { "post:read": { ...meta } } }) }, ]); expect(catalog.permissions.size).toBe(1); }); test("conflicting metadata is a boot error naming both files", () => { expect(() => mergeCatalogs([ { source: "a.ts", module: defineAuthz({ permissions: { "post:read": { risk: "low" } } }) }, { source: "b.ts", module: defineAuthz({ permissions: { "post:read": { risk: "high" } } }) }, ]), ).toThrow(/a\.ts.*b\.ts|b\.ts.*a\.ts/s); }); test("conflicting role definitions are a boot error", () => { expect(() => mergeCatalogs([ { source: "a.ts", module: defineAuthz({ roles: { editor: ["post:read"] } }) }, { source: "b.ts", module: defineAuthz({ roles: { editor: ["post:write"] } }) }, ]), ).toThrow(/editor/); }); test("bindings for the same permission union across modules", () => { const p1 = defineAuthz({ permissions: { "post:write": {} }, policies: { ownsPost: async () => ({ allowed: true }) }, bindings: { "post:write": ["ownsPost"] }, }); const p2 = defineAuthz({ policies: { notLocked: async () => ({ allowed: true }) }, bindings: { "post:write": ["notLocked"] }, }); const catalog = mergeCatalogs([ { source: "a.ts", module: p1 }, { source: "b.ts", module: p2 }, ]); expect([...catalog.bindings.get("post:write")!].sort()).toEqual(["notLocked", "ownsPost"]); }); test("a binding referencing a policy no module declares is a boot error", () => { expect(() => mergeCatalogs([ { source: "a.ts", module: { permissions: { "post:write": {} }, bindings: { "post:write": ["ghost"] } }, }, ]), ).toThrow(/ghost/); }); test("the merged catalog is frozen", () => { const catalog = mergeCatalogs([]); expect(() => (catalog.permissions as Map).set("x:y", {} as never)).toThrow(); }); test("emptyCatalog has no entries", () => { expect(emptyCatalog().permissions.size).toBe(0); }); });