Why: U6 矩阵页签的门控/载荷/来源标签纯逻辑先行红证。 Evidence: npx vitest run test/rdm-management.test.ts → 3 failed(导出缺失);收据 /tmp/fe-matrix-red.txt。 Impact: 仅测试。 Refs: 方案 r1 U6/R4/KTD5;RDM #266
99 lines
5.6 KiB
TypeScript
99 lines
5.6 KiB
TypeScript
// U6 页面接线契约测试(方案 r1 U6/KTD5):RDM 管理/项目页面的能力门控与命令
|
||
// 载荷构造是纯逻辑——先红后实现。门控语义与 rdmAuditReady 同源:命令清单不可用
|
||
//(未登录/请求失败/null)一律视为未就绪(fail-closed),普通基座不出现 RDM 页面。
|
||
import { describe, expect, it } from 'vitest'
|
||
import {
|
||
RDM_ROLES_COMMANDS, RDM_MEMBER_COMMANDS, RDM_DOC_REVIEW_COMMANDS, ROLE_ACTIONS,
|
||
rdmRolesReady, rdmMembersReady, rdmDocReviewReady,
|
||
roleCreatePayload, roleActionPayload, roleAssignPayload, roleDisablePayload,
|
||
memberAddPayload, memberRemovePayload, assignDocDutiesPayload,
|
||
docSubmitPayload, docApprovePayload, docRejectPayload, docFinalPayload, docStatusPayload,
|
||
RDM_PERM_MATRIX_COMMANDS, rdmPermMatrixReady, permCheckPayload, describeSource,
|
||
parseReviewers,
|
||
} from '../src/rdmManagement'
|
||
|
||
const cmd = (id: string, enabled = true) => ({ id, enabled })
|
||
const all = (ids: readonly string[]) => ids.map(id => cmd(id))
|
||
|
||
describe('RDM 页面能力门控', () => {
|
||
it('命令齐备且 enabled 才就绪', () => {
|
||
expect(rdmRolesReady(all(RDM_ROLES_COMMANDS))).toBe(true)
|
||
expect(rdmMembersReady(all(RDM_MEMBER_COMMANDS))).toBe(true)
|
||
expect(rdmDocReviewReady(all(RDM_DOC_REVIEW_COMMANDS))).toBe(true)
|
||
})
|
||
it('缺一命令即未就绪', () => {
|
||
const drop = (ids: readonly string[], skip: string) => ids.filter(i => i !== skip).map(id => cmd(id))
|
||
expect(rdmRolesReady(drop(RDM_ROLES_COMMANDS, 'rdm.role.list'))).toBe(false)
|
||
expect(rdmMembersReady(drop(RDM_MEMBER_COMMANDS, 'rdm.project.member.add'))).toBe(false)
|
||
expect(rdmDocReviewReady(drop(RDM_DOC_REVIEW_COMMANDS, 'rdm.review.doc.final'))).toBe(false)
|
||
})
|
||
it('disabled / null / 非数组 / 请求失败均未就绪(fail-closed)', () => {
|
||
expect(rdmRolesReady(RDM_ROLES_COMMANDS.map(id => cmd(id, false)))).toBe(false)
|
||
expect(rdmRolesReady(null)).toBe(false)
|
||
expect(rdmMembersReady(undefined)).toBe(false)
|
||
expect(rdmDocReviewReady('not-array' as unknown as never)).toBe(false)
|
||
})
|
||
})
|
||
|
||
describe('RDM 动作清单(12 项,与 perms.AllActions 对齐)', () => {
|
||
it('精确等于 RDM 动作枚举且不重复', () => {
|
||
expect([...ROLE_ACTIONS].sort()).toEqual([
|
||
'doc.publish', 'doc.read', 'doc.write', 'duty.assign', 'knowledge.publish',
|
||
'member.manage', 'project.archive', 'project.create', 'project.settings',
|
||
'review.participate', 'stage.advance', 'trace.edit',
|
||
])
|
||
expect(new Set(ROLE_ACTIONS).size).toBe(12)
|
||
})
|
||
})
|
||
|
||
describe('命令载荷构造(身份只经认证会话——载荷不含任何身份字段)', () => {
|
||
it('角色管理载荷', () => {
|
||
expect(roleCreatePayload('工程师')).toEqual({ name: '工程师' })
|
||
expect(roleActionPayload('role_ab', 'doc.write', false, true))
|
||
.toEqual({ roleId: 'role_ab', action: 'doc.write', default: false, cap: true })
|
||
expect(roleAssignPayload('bob', 'role_ab')).toEqual({ owner: 'bob', roleId: 'role_ab' })
|
||
expect(roleDisablePayload('role_ab', true)).toEqual({ roleId: 'role_ab', disabled: true })
|
||
})
|
||
it('项目成员载荷(role 缺省 member)', () => {
|
||
expect(memberAddPayload('p1', 'carol')).toEqual({ projectId: 'p1', member: 'carol', role: 'member' })
|
||
expect(memberAddPayload('p1', 'carol', 'owner')).toEqual({ projectId: 'p1', member: 'carol', role: 'owner' })
|
||
expect(memberRemovePayload('p1', 'carol')).toEqual({ projectId: 'p1', member: 'carol' })
|
||
})
|
||
it('文档职责与整文审签载荷', () => {
|
||
expect(assignDocDutiesPayload('p1', 'd1', 'alice', ['bob', 'carol'], 'dave'))
|
||
.toEqual({ projectId: 'p1', docId: 'd1', author: 'alice', reviewers: ['bob', 'carol'], approver: 'dave' })
|
||
expect(docSubmitPayload('p1', 'd1')).toEqual({ projectId: 'p1', docId: 'd1' })
|
||
expect(docApprovePayload('p1', 'd1', '通过')).toEqual({ projectId: 'p1', docId: 'd1', note: '通过' })
|
||
expect(docRejectPayload('p1', 'd1', '依据不足')).toEqual({ projectId: 'p1', docId: 'd1', reason: '依据不足' })
|
||
expect(docFinalPayload('p1', 'd1')).toEqual({ projectId: 'p1', docId: 'd1' })
|
||
expect(docFinalPayload('p1', 'd1', 3)).toEqual({ projectId: 'p1', docId: 'd1', round: 3 })
|
||
expect(docStatusPayload('p1', 'd1')).toEqual({ projectId: 'p1', docId: 'd1' })
|
||
})
|
||
it('审阅人 CSV 解析:去空白、去空项、保序去重', () => {
|
||
expect(parseReviewers('bob, carol,,bob')).toEqual(['bob', 'carol'])
|
||
expect(parseReviewers(' ')).toEqual([])
|
||
expect(parseReviewers('bob')).toEqual(['bob'])
|
||
})
|
||
})
|
||
|
||
describe('项目权限矩阵页(rdm.perm.check 消费)', () => {
|
||
it('命令面齐备才就绪(perm.check + 成员清单);缺一/disabled/清单不可用均未就绪', () => {
|
||
const all2 = RDM_PERM_MATRIX_COMMANDS.map(id => ({ id, enabled: true }))
|
||
expect(rdmPermMatrixReady(all2)).toBe(true)
|
||
expect(rdmPermMatrixReady(RDM_PERM_MATRIX_COMMANDS.slice(1).map(id => ({ id, enabled: true })))).toBe(false)
|
||
expect(rdmPermMatrixReady(RDM_PERM_MATRIX_COMMANDS.map(id => ({ id, enabled: false })))).toBe(false)
|
||
expect(rdmPermMatrixReady(null)).toBe(false)
|
||
})
|
||
it('矩阵查询载荷(身份只经认证会话)', () => {
|
||
expect(permCheckPayload('p1', 'bob')).toEqual({ projectId: 'p1', member: 'bob' })
|
||
})
|
||
it('来源标签:解释读面的人话映射;未知来源回退原文', () => {
|
||
expect(describeSource('admin')).toBe('系统管理员')
|
||
expect(describeSource('prof-role-default')).toContain('角色')
|
||
expect(describeSource('override')).toContain('覆盖')
|
||
expect(describeSource('relation-default')).toContain('关系')
|
||
expect(describeSource('non-member')).toContain('成员')
|
||
expect(describeSource('weird')).toBe('weird')
|
||
})
|
||
})
|