ab4f0d93ed
1、合并列表与详情读取权限,统一使用 subjects:read、project_members:read、monitoring_issues:read 等业务读取 key。 2、增加历史读取权限 key 的兼容映射,确保存量项目权限覆盖值不会丢失。 3、同步前端权限工具、路由权限与权限管理页面,避免继续引用已合并的旧权限 key。 4、补充项目角色启停保护与抽屉未保存变更守卫,防止停用 PM 或仍有关联成员的角色。
93 lines
4.9 KiB
TypeScript
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");
|
|
});
|
|
});
|