import { test, expect } from "bun:test"; import { createContext } from "@wrnexus/core"; import { defineRbac, hasRole, authorize, requireRole, requirePermission, any, all, attr, type Policy, } from "../src/index.ts"; const rbac = defineRbac({ admin: ["*"], editor: ["post:read", "post:write"], viewer: ["post:read"], moderator: ["role:editor", "comment:delete"], // inherits editor }); test("RBAC: roles, wildcards, namespaces, inheritance", () => { expect(rbac.can({ roles: ["viewer"] }, "post:read")).toBe(true); expect(rbac.can({ roles: ["viewer"] }, "post:write")).toBe(false); expect(rbac.can({ roles: ["admin"] }, "anything:goes")).toBe(true); // "*" expect(rbac.can({ roles: ["moderator"] }, "post:write")).toBe(true); // inherited from editor expect(rbac.can({ roles: ["moderator"] }, "comment:delete")).toBe(true); expect(rbac.can(undefined, "post:read")).toBe(false); expect(defineRbac({ ed: ["post:*"] }).can({ roles: ["ed"] }, "post:write")).toBe(true); // ns wildcard }); test("hasRole", () => { expect(hasRole({ roles: ["a", "b"] }, "a")).toBe(true); expect(hasRole({ roles: ["a"] }, "a", "b")).toBe(false); }); interface User extends Record { id?: string; roles?: string[]; tenant?: string; } interface Post { authorId: string; } test("PBAC/ABAC: policies compose (any/all) + attribute match", async () => { const ownsPost: Policy = (u, post) => u.id === post?.authorId; const isAdmin: Policy = (u) => (u.roles ?? []).includes("admin"); const canEdit = any(ownsPost, isAdmin); expect(await canEdit({ id: "u1" }, { authorId: "u1" })).toBe(true); // owner expect(await canEdit({ id: "u2", roles: ["admin"] }, { authorId: "u1" })).toBe(true); // admin expect(await canEdit({ id: "u2" }, { authorId: "u1" })).toBe(false); const sameTenant = all(isAdmin, attr("tenant", "acme")); expect(await sameTenant({ roles: ["admin"], tenant: "acme" })).toBe(true); expect(await sameTenant({ roles: ["admin"], tenant: "other" })).toBe(false); }); function ctx(user?: unknown) { const url = new URL("http://x/admin"); const c = createContext(new Request(url), url); c.user = user; return c; } test("guards: authorize / requireRole / requirePermission", async () => { const ok = () => new Response("ok"); expect((await requireRole("admin")(ctx({ roles: ["admin"] }), ok)).status).toBe(200); expect((await requireRole("admin")(ctx({ roles: ["viewer"] }), ok)).status).toBe(403); expect((await requirePermission(rbac, "post:write")(ctx({ roles: ["editor"] }), ok)).status).toBe( 200, ); expect((await requirePermission(rbac, "post:write")(ctx({ roles: ["viewer"] }), ok)).status).toBe( 403, ); expect( (await authorize((c) => (c.user as User)?.id === "u1")(ctx({ id: "u1" }), ok)).status, ).toBe(200); });