| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /**
- * Unit tests for PermissionMapper
- *
- * Tests permission translation between OAC granular and binary formats.
- */
- import { describe, it, expect } from "vitest";
- import {
- resolvePermissionRule,
- isGranularRule,
- mapPermissionsFromOAC,
- mapPermissionsToOAC,
- createGranularRule,
- extractPatterns,
- mergePermissionRules,
- hasGranularPermissions,
- hasAskPermissions,
- analyzePermissionDegradation,
- } from "../../../src/mappers/PermissionMapper";
- describe("PermissionMapper", () => {
- // ==========================================================================
- // resolvePermissionRule
- // ==========================================================================
- describe("resolvePermissionRule()", () => {
- describe("boolean rules", () => {
- it("returns true for true", () => {
- expect(resolvePermissionRule(true)).toBe(true);
- });
- it("returns false for false", () => {
- expect(resolvePermissionRule(false)).toBe(false);
- });
- });
- describe("literal rules", () => {
- it("returns true for 'allow'", () => {
- expect(resolvePermissionRule("allow")).toBe(true);
- });
- it("returns false for 'deny'", () => {
- expect(resolvePermissionRule("deny")).toBe(false);
- });
- it("returns true for 'ask' with permissive strategy", () => {
- expect(resolvePermissionRule("ask", "permissive")).toBe(true);
- });
- it("returns false for 'ask' with restrictive strategy", () => {
- expect(resolvePermissionRule("ask", "restrictive")).toBe(false);
- });
- it("returns false for 'ask' with ask-as-deny strategy", () => {
- expect(resolvePermissionRule("ask", "ask-as-deny")).toBe(false);
- });
- });
- describe("granular rules (records)", () => {
- it("returns true if any allow exists with permissive strategy", () => {
- const rule = { "*.ts": "allow" as const, "*.js": "deny" as const };
- expect(resolvePermissionRule(rule, "permissive")).toBe(true);
- });
- it("returns false if no allow and has deny with restrictive strategy", () => {
- const rule = { "*.ts": "deny" as const };
- expect(resolvePermissionRule(rule, "restrictive")).toBe(false);
- });
- it("returns true if allow and no deny with restrictive strategy", () => {
- const rule = { "*.ts": "allow" as const };
- expect(resolvePermissionRule(rule, "restrictive")).toBe(true);
- });
- });
- });
- // ==========================================================================
- // isGranularRule
- // ==========================================================================
- describe("isGranularRule()", () => {
- it("returns false for boolean", () => {
- expect(isGranularRule(true)).toBe(false);
- expect(isGranularRule(false)).toBe(false);
- });
- it("returns false for literal strings", () => {
- expect(isGranularRule("allow")).toBe(false);
- expect(isGranularRule("deny")).toBe(false);
- expect(isGranularRule("ask")).toBe(false);
- });
- it("returns true for object rules", () => {
- expect(isGranularRule({ "*.ts": "allow" })).toBe(true);
- expect(isGranularRule({ "*": "deny" })).toBe(true);
- });
- });
- // ==========================================================================
- // mapPermissionsFromOAC
- // ==========================================================================
- describe("mapPermissionsFromOAC()", () => {
- it("converts simple allow/deny to binary", () => {
- const permissions = { bash: "allow" as const, write: "deny" as const };
- const result = mapPermissionsFromOAC(permissions, "claude");
- expect(result.permissions).toEqual({ bash: true, write: false });
- expect(result.warnings).toHaveLength(0);
- });
- it("generates warnings for granular permissions", () => {
- const permissions = {
- bash: { "rm *": "deny" as const, "*": "allow" as const },
- };
- const result = mapPermissionsFromOAC(permissions, "claude");
- expect(result.warnings.length).toBeGreaterThan(0);
- expect(result.warnings[0]).toContain("Granular");
- });
- it("generates warnings for 'ask' permissions", () => {
- const permissions = { bash: "ask" as const };
- const result = mapPermissionsFromOAC(permissions, "claude");
- expect(result.warnings.length).toBeGreaterThan(0);
- expect(result.warnings[0]).toContain("ask");
- });
- it("respects degradation strategy for 'ask'", () => {
- const permissions = { bash: "ask" as const };
- const permissive = mapPermissionsFromOAC(permissions, "claude", "permissive");
- expect(permissive.permissions).toEqual({ bash: true });
- const restrictive = mapPermissionsFromOAC(permissions, "claude", "restrictive");
- expect(restrictive.permissions).toEqual({ bash: false });
- });
- });
- // ==========================================================================
- // mapPermissionsToOAC
- // ==========================================================================
- describe("mapPermissionsToOAC()", () => {
- it("converts binary to allow/deny", () => {
- const permissions = { bash: true, write: false };
- const result = mapPermissionsToOAC(permissions, "claude");
- expect(result.permissions).toEqual({ bash: "allow", write: "deny" });
- expect(result.warnings).toHaveLength(0);
- });
- it("skips undefined permissions", () => {
- const permissions = { bash: true, write: undefined };
- const result = mapPermissionsToOAC(permissions, "claude");
- expect(result.permissions).toEqual({ bash: "allow" });
- });
- });
- // ==========================================================================
- // createGranularRule
- // ==========================================================================
- describe("createGranularRule()", () => {
- it("creates rule from allow patterns", () => {
- const rule = createGranularRule(["*.ts", "*.tsx"]);
- expect(rule).toEqual({ "*.ts": "allow", "*.tsx": "allow" });
- });
- it("creates rule from allow and deny patterns", () => {
- const rule = createGranularRule(["*"], ["node_modules/**"]);
- expect(rule).toEqual({ "*": "allow", "node_modules/**": "deny" });
- });
- it("creates rule with ask patterns", () => {
- const rule = createGranularRule([], [], ["package.json"]);
- expect(rule).toEqual({ "package.json": "ask" });
- });
- it("creates combined rule", () => {
- const rule = createGranularRule(["src/**"], ["dist/**"], ["*.config.js"]);
- expect(rule).toEqual({
- "src/**": "allow",
- "dist/**": "deny",
- "*.config.js": "ask",
- });
- });
- });
- // ==========================================================================
- // extractPatterns
- // ==========================================================================
- describe("extractPatterns()", () => {
- it("extracts patterns from boolean", () => {
- expect(extractPatterns(true)).toEqual({
- allow: ["*"],
- deny: [],
- ask: [],
- });
- expect(extractPatterns(false)).toEqual({
- allow: [],
- deny: ["*"],
- ask: [],
- });
- });
- it("extracts patterns from literals", () => {
- expect(extractPatterns("allow")).toEqual({
- allow: ["*"],
- deny: [],
- ask: [],
- });
- expect(extractPatterns("deny")).toEqual({
- allow: [],
- deny: ["*"],
- ask: [],
- });
- expect(extractPatterns("ask")).toEqual({
- allow: [],
- deny: [],
- ask: ["*"],
- });
- });
- it("extracts patterns from granular rule", () => {
- const rule = { "*.ts": "allow" as const, "*.js": "deny" as const, "*.config": "ask" as const };
- const result = extractPatterns(rule);
- expect(result.allow).toContain("*.ts");
- expect(result.deny).toContain("*.js");
- expect(result.ask).toContain("*.config");
- });
- });
- // ==========================================================================
- // mergePermissionRules
- // ==========================================================================
- describe("mergePermissionRules()", () => {
- it("merges multiple rules", () => {
- const rule1 = { "*.ts": "allow" as const };
- const rule2 = { "*.js": "deny" as const };
- const merged = mergePermissionRules(rule1, rule2);
- expect(merged).toEqual({ "*.ts": "allow", "*.js": "deny" });
- });
- it("later rules override earlier ones", () => {
- const rule1 = { "*": "allow" as const };
- const rule2 = { "*": "deny" as const };
- const merged = mergePermissionRules(rule1, rule2);
- expect(merged).toBe("deny"); // Simplified to single value
- });
- it("merges boolean and literal rules", () => {
- const merged = mergePermissionRules(true, "deny");
- expect(merged).toBe("deny");
- });
- });
- // ==========================================================================
- // hasGranularPermissions
- // ==========================================================================
- describe("hasGranularPermissions()", () => {
- it("returns false for simple permissions", () => {
- const permissions = { bash: "allow" as const, write: true };
- expect(hasGranularPermissions(permissions)).toBe(false);
- });
- it("returns true for granular permissions", () => {
- const permissions = { bash: { "*": "allow" as const } };
- expect(hasGranularPermissions(permissions)).toBe(true);
- });
- });
- // ==========================================================================
- // hasAskPermissions
- // ==========================================================================
- describe("hasAskPermissions()", () => {
- it("returns false when no ask permissions", () => {
- const permissions = { bash: "allow" as const, write: "deny" as const };
- expect(hasAskPermissions(permissions)).toBe(false);
- });
- it("returns true for top-level ask", () => {
- const permissions = { bash: "ask" as const };
- expect(hasAskPermissions(permissions)).toBe(true);
- });
- it("returns true for nested ask", () => {
- const permissions = { bash: { "rm *": "ask" as const } };
- expect(hasAskPermissions(permissions)).toBe(true);
- });
- });
- // ==========================================================================
- // analyzePermissionDegradation
- // ==========================================================================
- describe("analyzePermissionDegradation()", () => {
- it("returns empty warnings for simple permissions", () => {
- const permissions = { bash: "allow" as const };
- const warnings = analyzePermissionDegradation(permissions, "claude");
- expect(warnings).toHaveLength(0);
- });
- it("warns about granular permission degradation", () => {
- const permissions = { bash: { "*": "allow" as const } };
- const warnings = analyzePermissionDegradation(permissions, "claude");
- expect(warnings.length).toBeGreaterThan(0);
- expect(warnings[0]).toContain("granular");
- });
- it("warns about ask permission conversion", () => {
- const permissions = { bash: "ask" as const };
- const warnings = analyzePermissionDegradation(permissions, "claude");
- expect(warnings.length).toBeGreaterThan(0);
- expect(warnings[0]).toContain("ask");
- });
- });
- });
|