/** * 纯权限 reducer 单测(契约 P6 / D11 / 2.3)。 * 矩阵覆盖:个人/Group/祖先继承/max 取最高/不降权/空权限/toggle 冻结; * 外加确定性随机化不变量(单调性:任何可用 grant 都不超过 effective)。 */ import { describe, expect, it } from "vitest"; import { checkAccess, effectiveRole, type EffectiveRoleInput, type FileLibGrantFact } from "../../src/database/filelib/permission.js"; const base: EffectiveRoleInput = { nodeId: "N", nodeKind: "FOLDER", ancestorIds: ["A", "R"], // N ⊂ A ⊂ R independentPermissionsEnabled: false, userId: "u1", groupIds: ["g1"], grants: [], }; function grant(partial: Partial): FileLibGrantFact { return { nodeId: "N", principalType: "USER", principalId: "u1", role: "VIEW", isCreatorGrant: false, ...partial, }; } describe("effectiveRole · 契约 P6 矩阵", () => { it("无任何 grant → null(无权限)", () => { expect(effectiveRole(base)).toBeNull(); }); it("个人直接 grant 在 self 上 → 生效", () => { expect(effectiveRole({ ...base, grants: [grant({ role: "EDIT" })] })).toBe("EDIT"); }); it("Group grant 且用户在组内 → 生效", () => { expect(effectiveRole({ ...base, grants: [grant({ principalType: "GROUP", principalId: "g1", role: "MANAGE" })] })).toBe("MANAGE"); }); it("Group grant 但用户不在组内 → null", () => { expect(effectiveRole({ ...base, grants: [grant({ principalType: "GROUP", principalId: "g9" })] })).toBeNull(); }); it("祖先链上的 grant 向下继承", () => { expect(effectiveRole({ ...base, grants: [grant({ nodeId: "A", role: "EDIT" })] })).toBe("EDIT"); expect(effectiveRole({ ...base, grants: [grant({ nodeId: "R", role: "VIEW" })] })).toBe("VIEW"); }); it("取最高:个人低权限 + Group 高权限 → 高权限(个人不能降权)", () => { const grants = [ grant({ role: "VIEW" }), grant({ principalType: "GROUP", principalId: "g1", role: "MANAGE" }), ]; expect(effectiveRole({ ...base, grants })).toBe("MANAGE"); }); it("取最高:链上多处 grant,最高者胜", () => { const grants = [ grant({ nodeId: "R", role: "MANAGE" }), grant({ nodeId: "A", role: "VIEW" }), grant({ nodeId: "N", role: "EDIT" }), ]; expect(effectiveRole({ ...base, grants })).toBe("MANAGE"); }); it("链外节点的 grant 不参与", () => { expect(effectiveRole({ ...base, grants: [grant({ nodeId: "X", role: "MANAGE" })] })).toBeNull(); }); it("他人的个人 grant 不参与", () => { expect(effectiveRole({ ...base, grants: [grant({ principalId: "u2", role: "MANAGE" })] })).toBeNull(); }); }); describe("effectiveRole · D11 独立权限开关", () => { const project: EffectiveRoleInput = { ...base, nodeKind: "PROJECT", nodeId: "P" }; it("开关关闭:项目级非创建者 grant 冻结", () => { const grants = [grant({ nodeId: "P", role: "EDIT" })]; expect(effectiveRole({ ...project, grants })).toBeNull(); }); it("开关关闭:创建者 grant 仍生效", () => { const grants = [grant({ nodeId: "P", role: "MANAGE", isCreatorGrant: true })]; expect(effectiveRole({ ...project, grants })).toBe("MANAGE"); }); it("开关关闭:祖先链 grant 不受影响", () => { const grants = [ grant({ nodeId: "P", role: "MANAGE" }), // 冻结 grant({ nodeId: "A", role: "VIEW" }), // 生效 ]; expect(effectiveRole({ ...project, grants })).toBe("VIEW"); }); it("开关开启:项目级 grant 恢复参与", () => { const grants = [grant({ nodeId: "P", role: "EDIT" })]; expect(effectiveRole({ ...project, independentPermissionsEnabled: true, grants })).toBe("EDIT"); }); it("文件夹忽略开关(self grant 照常参与)", () => { const grants = [grant({ nodeId: "N", role: "EDIT" })]; expect(effectiveRole({ ...base, grants })).toBe("EDIT"); }); }); describe("effectiveRole · 随机化不变量", () => { // 确定性 LCG,替代 property-based 框架(不新增依赖)。 function lcg(seed: number): () => number { let s = seed; return () => { s = (s * 1103515245 + 12345) % 2147483648; return s / 2147483648; }; } const roles = ["VIEW", "EDIT", "MANAGE"] as const; it("effective 恰为所有可用 grant 的最高秩(单调、顺序无关)", () => { const rand = lcg(42); for (let round = 0; round < 200; round += 1) { const nodes = ["N", "A", "R", "X"]; const principals = ["u1", "u2", "g1", "g9"]; const grants: FileLibGrantFact[] = Array.from({ length: Math.floor(rand() * 12) }, () => { const principalId = principals[Math.floor(rand() * principals.length)]!; return grant({ nodeId: nodes[Math.floor(rand() * nodes.length)]!, principalType: principalId.startsWith("g") ? "GROUP" : "USER", principalId, role: roles[Math.floor(rand() * roles.length)]!, }); }); const input: EffectiveRoleInput = { ...base, grants }; const applicableRanks = grants .filter((g) => ["N", "A", "R"].includes(g.nodeId)) .filter((g) => (g.principalType === "USER" ? g.principalId === "u1" : g.principalId === "g1")) .map((g) => ({ VIEW: 1, EDIT: 2, MANAGE: 3 })[g.role]); const expected = applicableRanks.length === 0 ? 0 : Math.max(...applicableRanks); const actual = effectiveRole(input); expect(actual === null ? 0 : { VIEW: 1, EDIT: 2, MANAGE: 3 }[actual]).toBe(expected); // 顺序无关 expect(effectiveRole({ ...input, grants: [...grants].reverse() })).toBe(actual); } }); }); describe("checkAccess · D8 语义", () => { it("无权限 → not_found(404 不泄露)", () => { expect(checkAccess(null, "VIEW")).toEqual({ allowed: false, reason: "not_found" }); }); it("权限不足 → forbidden(403)", () => { expect(checkAccess("VIEW", "EDIT")).toEqual({ allowed: false, reason: "forbidden" }); }); it("权限足够 → allowed 并带回实际角色", () => { expect(checkAccess("MANAGE", "EDIT")).toEqual({ allowed: true, role: "MANAGE" }); }); });