225 lines
6.9 KiB
TypeScript
225 lines
6.9 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
DEFAULT_ROLES,
|
|
hasPermission,
|
|
isDeliverableVisible,
|
|
filterVisibleDeliverablesWithRBAC,
|
|
canPerformDocAction,
|
|
} from './rbac';
|
|
import { UserProfile } from '../types/docgov';
|
|
import { DeliverableItem } from '../types/workbench';
|
|
import { RoleDefinition } from '../types/rbac';
|
|
|
|
test('集成场景 1: 动态新增合规审计员角色,并配置其仅能查阅特定单文档覆盖', () => {
|
|
// 1. 动态新增一个审计员角色
|
|
const customAuditorRole: RoleDefinition = {
|
|
id: 'role-auditor',
|
|
name: '外部合规审计专家',
|
|
code: 'AUDITOR',
|
|
description: '第三方飞行检查专家,默认无内部工作流权限,仅可查阅指定公开或白名单文档',
|
|
isSystem: false,
|
|
permissions: ['doc:export'], // 默认无 workflow:view_all, 无 project:create, 无 project:assign_lead
|
|
createdAt: '2026-09-08 15:00',
|
|
updatedAt: '2026-09-08 15:00',
|
|
};
|
|
|
|
const activeRoles: RoleDefinition[] = [...DEFAULT_ROLES, customAuditorRole];
|
|
|
|
// 2. 创建审计员用户
|
|
const auditorUser: UserProfile = {
|
|
id: 99,
|
|
name: '钱审计',
|
|
employeeId: 'AUDIT-001',
|
|
department: '国家器审中心第三方合规组',
|
|
position: '主任审核员',
|
|
roleId: 'role-auditor',
|
|
};
|
|
|
|
// 3. 准备一批项目交付物
|
|
const projectDocs: DeliverableItem[] = [
|
|
{
|
|
docId: 'doc-wf-normal',
|
|
projectId: 1,
|
|
taskKey: 'feasibility',
|
|
title: '常规内部立项可行性报告',
|
|
stage: 'V0.1',
|
|
category: 'workflow',
|
|
creatorId: 1,
|
|
creatorName: '张工',
|
|
isFixedWorkflow: true,
|
|
isTemplateOnly: false,
|
|
lifecycleState: 'drafting',
|
|
isClaimed: true,
|
|
author: '张工',
|
|
reviewer: '李主管',
|
|
approver: '王总监',
|
|
updatedAt: '2026-09-08 12:00',
|
|
},
|
|
{
|
|
docId: 'doc-wf-audited',
|
|
projectId: 1,
|
|
taskKey: 'regulatory',
|
|
title: '器械注册与临床评价白皮书 (开放审计)',
|
|
stage: 'V0.5',
|
|
category: 'workflow',
|
|
creatorId: 1,
|
|
creatorName: '张工',
|
|
isFixedWorkflow: true,
|
|
isTemplateOnly: false,
|
|
lifecycleState: 'approving',
|
|
isClaimed: true,
|
|
author: '张工',
|
|
reviewer: '李主管',
|
|
approver: '王总监',
|
|
updatedAt: '2026-09-08 12:00',
|
|
visibilityRule: {
|
|
mode: 'role_whitelist',
|
|
allowedRoleIds: ['role-director', 'role-auditor'],
|
|
overrideReason: '飞行检查开放白名单',
|
|
},
|
|
},
|
|
{
|
|
docId: 'doc-private-zhang',
|
|
projectId: 1,
|
|
taskKey: 'draft',
|
|
title: '张工敏感商业测算草案',
|
|
stage: 'V0.1',
|
|
category: 'private',
|
|
creatorId: 1,
|
|
creatorName: '张工',
|
|
isFixedWorkflow: false,
|
|
isTemplateOnly: false,
|
|
lifecycleState: 'drafting',
|
|
isClaimed: true,
|
|
author: '张工',
|
|
reviewer: '',
|
|
approver: '',
|
|
updatedAt: '2026-09-08 12:00',
|
|
},
|
|
];
|
|
|
|
// 4. 验证钱审计视角下的可见交付物:
|
|
// - doc-wf-normal: 无 workflow:view_all -> 不可见
|
|
// - doc-private-zhang: private 且非创建者 -> 绝对不可见
|
|
// - doc-wf-audited: 单文档覆盖 role_whitelist 包含 role-auditor -> 可见!
|
|
const auditorVisible = filterVisibleDeliverablesWithRBAC(projectDocs, auditorUser, activeRoles);
|
|
assert.equal(auditorVisible.length, 1);
|
|
assert.equal(auditorVisible[0].docId, 'doc-wf-audited');
|
|
|
|
// 5. 验证操作权限拦截
|
|
assert.equal(canPerformDocAction('create_project', auditorUser, activeRoles), false);
|
|
assert.equal(canPerformDocAction('assign_lead', auditorUser, activeRoles), false);
|
|
assert.equal(canPerformDocAction('override_visibility', auditorUser, activeRoles), false);
|
|
});
|
|
|
|
test('集成场景 2: 动态修改角色权限矩阵,权限即刻全量生效', () => {
|
|
// 1. 复制角色库
|
|
let roles = [...DEFAULT_ROLES];
|
|
const engineerRole = roles.find((r) => r.code === 'ENGINEER')!;
|
|
assert.equal(engineerRole.permissions.includes('project:create'), false);
|
|
|
|
const zhangUser: UserProfile = {
|
|
id: 1,
|
|
name: '张工',
|
|
employeeId: 'SMDF-0821',
|
|
department: '微创中心',
|
|
position: '资深方案工程师',
|
|
roleId: 'role-engineer',
|
|
};
|
|
|
|
// 初始时张工无权创建项目
|
|
assert.equal(hasPermission(zhangUser, roles, 'project:create'), false);
|
|
|
|
// 2. 模拟管理员在 SettingsModal 中为 ENGINEER 勾选了 project:create 权限
|
|
roles = roles.map((r) =>
|
|
r.code === 'ENGINEER'
|
|
? { ...r, permissions: [...r.permissions, 'project:create' as const] }
|
|
: r
|
|
);
|
|
|
|
// 3. 再次断言,权限立即响应式生效!
|
|
assert.equal(hasPermission(zhangUser, roles, 'project:create'), true);
|
|
});
|
|
|
|
test('集成场景 3: 单文档覆盖 designated_only 严格收紧与撤回恢复全流程', () => {
|
|
const roles = DEFAULT_ROLES;
|
|
|
|
const zhangAuthor: UserProfile = {
|
|
id: 1,
|
|
name: '张工',
|
|
employeeId: 'SMDF-0821',
|
|
department: '微创中心',
|
|
position: '资深方案工程师',
|
|
roleId: 'role-engineer',
|
|
};
|
|
|
|
const liReviewer: UserProfile = {
|
|
id: 2,
|
|
name: '李主管',
|
|
employeeId: 'SMDF-0205',
|
|
department: '微创中心',
|
|
position: '技术主管',
|
|
roleId: 'role-tech-lead',
|
|
};
|
|
|
|
const sunOtherEngineer: UserProfile = {
|
|
id: 8,
|
|
name: '孙工',
|
|
employeeId: 'SMDF-0999',
|
|
department: '微创中心',
|
|
position: '方案工程师',
|
|
roleId: 'role-engineer',
|
|
};
|
|
|
|
let targetDoc: DeliverableItem = {
|
|
docId: 'doc-secret-001',
|
|
projectId: 1,
|
|
taskKey: 'feasibility',
|
|
title: '涉密荧光滤光片设计图',
|
|
stage: 'V0.5',
|
|
category: 'workflow',
|
|
creatorId: 1,
|
|
creatorName: '张工',
|
|
isFixedWorkflow: true,
|
|
isTemplateOnly: false,
|
|
lifecycleState: 'drafting',
|
|
isClaimed: true,
|
|
author: '张工',
|
|
reviewer: '李主管',
|
|
approver: '王总监',
|
|
updatedAt: '2026-09-08 16:00',
|
|
};
|
|
|
|
// 步骤 A: 未设置覆盖时,同项目的孙工(具备 workflow:view_all)可以查阅
|
|
assert.equal(isDeliverableVisible(sunOtherEngineer, roles, targetDoc), true);
|
|
|
|
// 步骤 B: 李主管配置单文档覆盖为 designated_only
|
|
targetDoc = {
|
|
...targetDoc,
|
|
visibilityRule: {
|
|
mode: 'designated_only',
|
|
overrideReason: '核心专利技术交底,严格限制仅编制/审核/批准人可见',
|
|
configuredBy: '李主管',
|
|
configuredAt: '2026-09-08 16:05',
|
|
},
|
|
};
|
|
|
|
// 步骤 C: 重新判定
|
|
// 张工(编制人)可见
|
|
assert.equal(isDeliverableVisible(zhangAuthor, roles, targetDoc), true);
|
|
// 李主管(审核人)可见
|
|
assert.equal(isDeliverableVisible(liReviewer, roles, targetDoc), true);
|
|
// 孙工(非责任人)被单文档覆盖精确拦截 -> 不可见!
|
|
assert.equal(isDeliverableVisible(sunOtherEngineer, roles, targetDoc), false);
|
|
|
|
// 步骤 D: 撤回覆盖,恢复为 inherit
|
|
targetDoc = {
|
|
...targetDoc,
|
|
visibilityRule: undefined,
|
|
};
|
|
|
|
// 孙工再次可见
|
|
assert.equal(isDeliverableVisible(sunOtherEngineer, roles, targetDoc), true);
|
|
});
|