import { type PermissionBaseline, parseModuleManifest } from "@ekklesia/contracts"; import { describe, expect, it } from "vitest"; import { CORE_AUTHORIZATION_GRANTS_MANAGE, CORE_MODULES_UI_READ, CORE_PERMISSION_DECLARATIONS, PermissionRegistryError, } from "../src/authorization/index.js"; import type { DiscoveredModule } from "../src/modules/discovery.js"; import { composePlatformPermissionRegistry, ModulePermissionCompositionError, } from "../src/modules/permissions.js"; interface PermissionInput { readonly id: string; readonly baseline: PermissionBaseline; readonly description: string; } /** A minimal, in-memory `DiscoveredModule` double — no filesystem, no real discovery. The manifest * is still built through `parseModuleManifest`, so a fixture that would not actually pass manifest * validation cannot silently misrepresent what real discovery would ever produce. Only `manifest` * is read by `composePlatformPermissionRegistry`; the rest exist only to satisfy the type. */ function fakeModule(id: string, permissions: readonly PermissionInput[] = []): DiscoveredModule { const manifest = parseModuleManifest({ manifestVersion: 4, id, name: `${id} module`, version: "0.1.0", compatibility: { platform: "^0.1.0", moduleSdk: "^0.1.0" }, permissions, entrypoints: { // Deliberately a path nothing will ever resolve, proving composition never imports or // otherwise touches a module's server entrypoint. server: "./dist/does-not-exist/index.js", }, }); return { directory: `/modules/${id}`, entrypoints: { server: `/modules/${id}/dist/does-not-exist/index.js` }, manifest, manifestPath: `/modules/${id}/ekklesia.module.json`, packageName: `@test/${id}`, }; } describe("composePlatformPermissionRegistry", () => { it("composes a Core-only registry when no modules are supplied", () => { const registry = composePlatformPermissionRegistry([]); expect(registry.declarations).toEqual(CORE_PERMISSION_DECLARATIONS); expect(registry.has(CORE_AUTHORIZATION_GRANTS_MANAGE)).toBe(true); }); it("composes Core and multiple discovered modules' declarations into one registry", () => { const calendar = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "restricted", description: "Read events." }, ]); const people = fakeModule("people", [ { id: "people.directory.read", baseline: "authenticated", description: "Read directory." }, ]); const registry = composePlatformPermissionRegistry([calendar, people]); expect(registry.has(CORE_AUTHORIZATION_GRANTS_MANAGE)).toBe(true); expect(registry.has(CORE_MODULES_UI_READ)).toBe(true); expect(registry.has("calendar.events.read")).toBe(true); expect(registry.has("people.directory.read")).toBe(true); // Core's own two declarations plus one from each module. expect(registry.declarations).toHaveLength(4); }); it("preserves deterministic order: Core first, then modules in the supplied order, then declarations in manifest order", () => { const calendar = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "restricted", description: "Read events." }, { id: "calendar.events.write", baseline: "restricted", description: "Write events." }, ]); const people = fakeModule("people", [ { id: "people.directory.read", baseline: "authenticated", description: "Read directory." }, ]); const registry = composePlatformPermissionRegistry([calendar, people]); expect(registry.declarations.map((declaration) => declaration.id)).toEqual([ CORE_AUTHORIZATION_GRANTS_MANAGE, CORE_MODULES_UI_READ, "calendar.events.read", "calendar.events.write", "people.directory.read", ]); }); it.each([ "public", "authenticated", "restricted", ] as const)("preserves the exact %s baseline and description of a module declaration", (baseline) => { const calendar = fakeModule("calendar", [ { id: "calendar.events.read", baseline, description: "Read calendar events." }, ]); const registry = composePlatformPermissionRegistry([calendar]); expect(registry.get("calendar.events.read")).toEqual({ id: "calendar.events.read", baseline, description: "Read calendar events.", }); }); it("still fails closed for a permission no module or Core ever declared", () => { const registry = composePlatformPermissionRegistry([]); expect(registry.has("calendar.events.read")).toBe(false); expect(registry.get("calendar.events.read")).toBeUndefined(); }); it("fails composition on an exact duplicate permission id supplied by two module entries", () => { // A permission id must start with its own module's id, so a genuine cross-module collision is // only reachable if the supplied list itself contains the same module id twice — real // discovery already guarantees module-id uniqueness (`MODULE_ID_DUPLICATE`), but composition // re-derives duplicate detection from the flat declaration list regardless, rather than // trusting that upstream guarantee alone. const first = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "restricted", description: "Read events." }, ]); const second = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "public", description: "Also read events." }, ]); expect(() => composePlatformPermissionRegistry([first, second])).toThrow( PermissionRegistryError, ); }); it("rejects a module contributing a permission in the reserved core.* namespace, without leaking a module path", () => { // Only a module whose own id is literally "core" can produce a "core."-prefixed permission // that still passes the manifest's own module-namespace check — this proves composition // catches that case rather than trusting the manifest's self-consistency alone. const impostor = fakeModule("core", [ { id: "core.rogue.permission", baseline: "restricted", description: "Not really Core's." }, ]); let captured: unknown; try { composePlatformPermissionRegistry([impostor]); } catch (error) { captured = error; } expect(captured).toBeInstanceOf(ModulePermissionCompositionError); expect(captured).toMatchObject({ code: "MODULE_PERMISSION_RESERVED_NAMESPACE", moduleId: "core", permissionId: "core.rogue.permission", }); const publicText = String(captured); expect(publicText).not.toContain("/modules/core"); expect(publicText).not.toContain("ekklesia.module.json"); }); it("rejects a reserved-namespace collision even when it would not otherwise duplicate an existing id", () => { const impostor = fakeModule("core", [ { id: "core.authorization.grants.manage", baseline: "restricted", description: "Attempting to shadow Core's own permission.", }, ]); expect(() => composePlatformPermissionRegistry([impostor])).toThrow( ModulePermissionCompositionError, ); }); it("removes a module's permissions from the resulting registry once that module is absent from the supplied list", () => { const calendar = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "restricted", description: "Read events." }, ]); const withCalendar = composePlatformPermissionRegistry([calendar]); expect(withCalendar.has("calendar.events.read")).toBe(true); const withoutCalendar = composePlatformPermissionRegistry([]); expect(withoutCalendar.has("calendar.events.read")).toBe(false); }); it("performs no I/O: composition is synchronous and never imports a module's entrypoint", () => { const calendar = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "restricted", description: "Read events." }, ]); // A synchronous return (not a Promise) from a fixture whose entrypoint deliberately resolves // to nothing on disk is direct evidence no filesystem or module-code operation occurred. const result = composePlatformPermissionRegistry([calendar]); expect(result).not.toBeInstanceOf(Promise); expect(result.has("calendar.events.read")).toBe(true); }); it("does not mutate the supplied module list or Core's own declaration list", () => { const calendar = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "restricted", description: "Read events." }, ]); const modules = Object.freeze([calendar]); const coreDeclarationsBefore = [...CORE_PERMISSION_DECLARATIONS]; composePlatformPermissionRegistry(modules); expect(modules).toEqual([calendar]); expect(CORE_PERMISSION_DECLARATIONS).toEqual(coreDeclarationsBefore); }); it("returns a registry and declarations that remain immutable, including module-contributed ones", () => { const calendar = fakeModule("calendar", [ { id: "calendar.events.read", baseline: "restricted", description: "Read events." }, ]); const registry = composePlatformPermissionRegistry([calendar]); expect(Object.isFrozen(registry)).toBe(true); expect(Object.isFrozen(registry.declarations)).toBe(true); for (const declaration of registry.declarations) { expect(Object.isFrozen(declaration)).toBe(true); } }); });