import { describe, expect, test } from "bun:test";
import { defineRbac } from "../src/index.ts";

describe("RBAC namespace wildcards", () => {
  const rbac = defineRbac({
    admin: ["*"],
    editor: ["post:*"],
    moderator: ["post:comment:*"],
    reader: ["post:read"],
  });

  test("a wildcard grants every depth beneath it", () => {
    expect(rbac.can({ roles: ["editor"] }, "post:write")).toBe(true);
    expect(rbac.can({ roles: ["editor"] }, "post:comment:delete")).toBe(true);
    expect(rbac.can({ roles: ["editor"] }, "post:comment:flag:undo")).toBe(true);
  });

  test("a deeper wildcard grants its own subtree", () => {
    expect(rbac.can({ roles: ["moderator"] }, "post:comment:delete")).toBe(true);
    expect(rbac.can({ roles: ["moderator"] }, "post:comment:flag:undo")).toBe(true);
  });

  test("a wildcard does not leak sideways or upward", () => {
    expect(rbac.can({ roles: ["moderator"] }, "post:write")).toBe(false);
    expect(rbac.can({ roles: ["moderator"] }, "post")).toBe(false);
    expect(rbac.can({ roles: ["editor"] }, "page:write")).toBe(false);
    expect(rbac.can({ roles: ["reader"] }, "post:write")).toBe(false);
  });

  test("root wildcard and unknown subjects behave", () => {
    expect(rbac.can({ roles: ["admin"] }, "anything:at:all")).toBe(true);
    expect(rbac.can({ roles: [] }, "post:read")).toBe(false);
    expect(rbac.can(undefined, "post:read")).toBe(false);
  });

  test("role inheritance terminates on cycles", () => {
    const cyclic = defineRbac({ a: ["role:b", "p:a"], b: ["role:a", "p:b"] });
    expect([...cyclic.permissionsFor(["a"])].sort()).toEqual(["p:a", "p:b"]);
  });
});
