import { parsePermissionId } from "@ekklesia/contracts"; import { describe, expect, it } from "vitest"; import { CORE_AUTHORIZATION_GRANTS_MANAGE, CORE_MODULES_UI_READ, CORE_PERMISSION_DECLARATIONS, CORE_PERMISSION_REGISTRY, createPermissionRegistry, PermissionRegistryError, } from "../src/authorization/index.js"; describe("createPermissionRegistry", () => { it("builds an immutable registry from a fixed declaration list", () => { const id = parsePermissionId("test.example.read"); const registry = createPermissionRegistry([ { baseline: "public", description: "An example permission.", id }, ]); expect(registry.has(id)).toBe(true); expect(registry.get(id)).toEqual({ baseline: "public", description: "An example permission.", id, }); expect(Object.isFrozen(registry)).toBe(true); expect(Object.isFrozen(registry.declarations)).toBe(true); expect(Object.isFrozen(registry.get(id))).toBe(true); }); it("rejects a duplicate permission id at composition time, not per request", () => { const id = parsePermissionId("test.example.read"); expect(() => createPermissionRegistry([ { baseline: "public", description: "First.", id }, { baseline: "restricted", description: "Second.", id }, ]), ).toThrow(PermissionRegistryError); }); it("carries the offending permission id on the duplicate-declaration error (a composition-time, developer-facing detail, not a runtime response)", () => { const id = parsePermissionId("test.example.read"); let captured: unknown; try { createPermissionRegistry([ { baseline: "public", description: "First.", id }, { baseline: "restricted", description: "Second.", id }, ]); } catch (error) { captured = error; } expect(captured).toBeInstanceOf(PermissionRegistryError); expect(captured).toMatchObject({ code: "PERMISSION_REGISTRY_DUPLICATE_DECLARATION", permissionId: id, }); }); it("treats an unregistered permission the same as a malformed one: get/has both fail closed", () => { const registered = parsePermissionId("test.example.read"); const unregistered = parsePermissionId("test.example.unregistered"); const registry = createPermissionRegistry([ { baseline: "public", description: "Registered.", id: registered }, ]); expect(registry.has(unregistered)).toBe(false); expect(registry.get(unregistered)).toBeUndefined(); }); it("fails closed for a malformed permission id passed to get/has, without throwing", () => { const registry = createPermissionRegistry([]); expect(registry.has("not a valid permission")).toBe(false); expect(registry.get(null)).toBeUndefined(); expect(registry.get(undefined)).toBeUndefined(); expect(registry.get(42)).toBeUndefined(); expect(registry.get("media.*")).toBeUndefined(); }); it("builds an empty registry from an empty list without error", () => { const registry = createPermissionRegistry([]); expect(registry.declarations).toEqual([]); }); }); describe("CORE_PERMISSION_REGISTRY", () => { it("registers exactly core.authorization.grants.manage as restricted", () => { const declaration = CORE_PERMISSION_REGISTRY.get(CORE_AUTHORIZATION_GRANTS_MANAGE); expect(declaration).toBeDefined(); expect(declaration?.baseline).toBe("restricted"); expect(declaration?.id).toBe("core.authorization.grants.manage"); }); it("registers core.modules.ui.read as authenticated, not restricted", () => { const declaration = CORE_PERMISSION_REGISTRY.get(CORE_MODULES_UI_READ); expect(declaration).toBeDefined(); // Any signed-in user may learn which module UIs the browser host can mount; a shell that // rendered nothing until an operator granted each user a permission would be unusable, and the // module UIs gate their own content on their own permissions regardless (ADR 0026). expect(declaration?.baseline).toBe("authenticated"); expect(declaration?.id).toBe("core.modules.ui.read"); }); it("contains exactly these two permissions and no others", () => { expect(CORE_PERMISSION_REGISTRY.declarations.map((declaration) => declaration.id)).toEqual([ CORE_AUTHORIZATION_GRANTS_MANAGE, CORE_MODULES_UI_READ, ]); }); }); describe("CORE_PERMISSION_DECLARATIONS", () => { it("is Core's own declaration list, exported separately from the registry in deeply immutable form", () => { expect(CORE_PERMISSION_DECLARATIONS).toEqual([ { baseline: "restricted", description: "Create, list, and revoke another user's explicit authorization grants.", id: CORE_AUTHORIZATION_GRANTS_MANAGE, }, { baseline: "authenticated", description: "List the installed module UIs a browser host may mount.", id: CORE_MODULES_UI_READ, }, ]); expect(Object.isFrozen(CORE_PERMISSION_DECLARATIONS)).toBe(true); expect(Object.isFrozen(CORE_PERMISSION_DECLARATIONS[0])).toBe(true); expect(Object.isFrozen(CORE_PERMISSION_DECLARATIONS[1])).toBe(true); }); it("matches exactly what CORE_PERMISSION_REGISTRY exposes as its own declarations, so a composer building on top of it never diverges from what Core itself registers", () => { expect(CORE_PERMISSION_DECLARATIONS).toEqual(CORE_PERMISSION_REGISTRY.declarations); }); });