import { afterAll, describe, expect, test } from "bun:test"; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { loadAppAuthzCatalog } from "../src/authz-boot.ts"; // Fixtures must live inside the repo tree, not os.tmpdir(). A scaffolded file // under app/authz importing "@wrnexus/authz" by bare specifier resolves via // the root tsconfig.json `paths` map, walked from the *imported file's* // location — an out-of-tree path (os.tmpdir(), often a different drive on // Windows) never reaches it and fails to resolve the module. const scratchRoot = join(import.meta.dir, ".tmp-authz-boot"); mkdirSync(scratchRoot, { recursive: true }); function scaffold(body: string): string { const root = mkdtempSync(join(scratchRoot, "app-")); mkdirSync(join(root, "app", "authz"), { recursive: true }); mkdirSync(join(root, "app", "pages"), { recursive: true }); writeFileSync(join(root, "app", "authz", "main.ts"), body, "utf8"); return join(root, "app"); } afterAll(() => { rmSync(scratchRoot, { recursive: true, force: true }); }); describe("loadAppAuthzCatalog", () => { test("loads declarations from app/authz", async () => { const appDir = scaffold( `import { defineAuthz } from "@wrnexus/authz"; export default defineAuthz({ permissions: { "post:read": {} } });`, ); const catalog = await loadAppAuthzCatalog(appDir); expect(catalog.permissions.has("post:read")).toBe(true); }); test("an app with no declarations gets an empty catalog rather than an error", async () => { const root = mkdtempSync(join(scratchRoot, "empty-")); mkdirSync(join(root, "app", "pages"), { recursive: true }); const catalog = await loadAppAuthzCatalog(join(root, "app")); expect(catalog.permissions.size).toBe(0); }); test("a conflicting declaration fails the boot loudly", async () => { const appDir = scaffold( `import { defineAuthz } from "@wrnexus/authz"; export default defineAuthz({ permissions: { "post:read": { risk: "low" } } });`, ); writeFileSync( join(appDir, "authz", "other.ts"), `import { defineAuthz } from "@wrnexus/authz"; export default defineAuthz({ permissions: { "post:read": { risk: "high" } } });`, "utf8", ); await expect(loadAppAuthzCatalog(appDir)).rejects.toThrow(/WRN-AUTHZ-CONFLICT/); }); test("a declaration with no default export is skipped, not fatal", async () => { const appDir = scaffold(`export const notDefault = 1;`); const catalog = await loadAppAuthzCatalog(appDir); expect(catalog.permissions.size).toBe(0); }); });