Files
ctms/frontend/src/utils/projectRoutePermissions.test.ts
T
Cheng Zhou ab4f0d93ed 统一项目权限读取模型
1、合并列表与详情读取权限,统一使用 subjects:read、project_members:read、monitoring_issues:read 等业务读取 key。

2、增加历史读取权限 key 的兼容映射,确保存量项目权限覆盖值不会丢失。

3、同步前端权限工具、路由权限与权限管理页面,避免继续引用已合并的旧权限 key。

4、补充项目角色启停保护与抽屉未保存变更守卫,防止停用 PM 或仍有关联成员的角色。
2026-06-04 11:06:03 +08:00

93 lines
4.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { findFirstAccessibleProjectPath, getProjectRoutePermission, hasProjectPermission } from "./projectRoutePermissions";
describe("project route permissions", () => {
it("maps project routes to backend operation keys", () => {
expect(getProjectRoutePermission("/project/overview")).toEqual({ operationKey: "project_overview:read" });
expect(getProjectRoutePermission("/project/milestones")).toEqual({ operationKey: "project_milestones:read" });
expect(getProjectRoutePermission("/fees/contracts")).toEqual({ operationKey: "fees_contracts:read" });
expect(getProjectRoutePermission("/finance/contracts")).toBeNull();
expect(getProjectRoutePermission("/finance/contracts/new")).toBeNull();
expect(getProjectRoutePermission("/finance/contracts/abc/edit")).toBeNull();
expect(getProjectRoutePermission("/drug/shipments")).toEqual({ operationKey: "drug_shipments:read" });
expect(getProjectRoutePermission("/drug/shipments/abc")).toEqual({ operationKey: "drug_shipments:read" });
expect(getProjectRoutePermission("/subjects/new")).toEqual({ operationKey: "subjects:create" });
expect(getProjectRoutePermission("/subjects/abc/edit")).toEqual({ operationKey: "subjects:update" });
expect(getProjectRoutePermission("/risk-issues")).toEqual({ operationKey: "subject_aes:read" });
expect(getProjectRoutePermission("/risk-issues/sae")).toEqual({ operationKey: "subject_aes:read" });
expect(getProjectRoutePermission("/risk-issues/pd")).toEqual({ operationKey: "subject_pds:list" });
expect(getProjectRoutePermission("/risk-issues/monitoring-visits")).toEqual({ operationKey: "monitoring_issues:read" });
expect(getProjectRoutePermission("/etmf")).toEqual({ operationKey: "documents:read" });
expect(getProjectRoutePermission("/startup/feasibility/new")).toEqual({ operationKey: "startup_initiation:create" });
expect(getProjectRoutePermission("/startup/feasibility/abc/edit")).toEqual({ operationKey: "startup_initiation:update" });
expect(getProjectRoutePermission("/startup/ethics/new")).toEqual({ operationKey: "startup_ethics:create" });
expect(getProjectRoutePermission("/startup/ethics/abc/edit")).toEqual({ operationKey: "startup_ethics:update" });
expect(getProjectRoutePermission("/startup/feasibility-ethics")).toEqual({
operationKeys: ["startup_initiation:read", "startup_ethics:read"],
});
expect(getProjectRoutePermission("/startup/kickoff/new")).toEqual({ operationKey: "startup_auth:create" });
expect(getProjectRoutePermission("/startup/kickoff/abc/edit")).toBeNull();
expect(getProjectRoutePermission("/startup/training/new")).toBeNull();
expect(getProjectRoutePermission("/startup/training/abc/edit")).toBeNull();
expect(getProjectRoutePermission("/knowledge/medical-consult/abc")).toEqual({ operationKey: "faq:read" });
expect(getProjectRoutePermission("/knowledge/precautions/new")).toEqual({ operationKey: "precautions:create" });
expect(getProjectRoutePermission("/knowledge/precautions/abc/edit")).toEqual({ operationKey: "precautions:update" });
expect(getProjectRoutePermission("/knowledge/notes/new")).toBeNull();
});
it("rejects project routes when the role lacks operation permission", () => {
const matrix = {
CRA: {
"project_overview:read": { allowed: false },
"precautions:read": { allowed: true },
},
} as any;
expect(hasProjectPermission(matrix, "CRA", getProjectRoutePermission("/project/overview"), false)).toBe(false);
expect(hasProjectPermission(matrix, "CRA", getProjectRoutePermission("/knowledge/precautions"), false)).toBe(true);
expect(findFirstAccessibleProjectPath(matrix, "CRA", false)).toBe("/knowledge/precautions");
});
it("can fall back to every accessible risk issue child module", () => {
expect(
findFirstAccessibleProjectPath(
{
QA: {
"subject_aes:read": { allowed: false },
"subject_pds:list": { allowed: true },
"monitoring_issues:read": { allowed: false },
},
} as any,
"QA",
false,
),
).toBe("/risk-issues/pd");
expect(
findFirstAccessibleProjectPath(
{
QA: {
"subject_aes:read": { allowed: false },
"subject_pds:list": { allowed: false },
"monitoring_issues:read": { allowed: true },
},
} as any,
"QA",
false,
),
).toBe("/risk-issues/monitoring-visits");
});
it("allows combined landing pages when any child module is readable", () => {
const matrix = {
CRA: {
"startup_initiation:read": { allowed: false },
"startup_ethics:read": { allowed: true },
},
} as any;
expect(hasProjectPermission(matrix, "CRA", getProjectRoutePermission("/startup/feasibility-ethics"), false)).toBe(true);
expect(findFirstAccessibleProjectPath(matrix, "CRA", false)).toBe("/startup/feasibility-ethics");
});
});