gongxiangp2/lib/__tests__/work-review-policy.test.ts
2026-08-05 16:49:54 +08:00

191 lines
7.5 KiB
TypeScript

import {
canDeleteWorkReviewProject,
canResubmitWorkReviewProject,
canSubmitWorkReview,
deriveCompletedWorkReviewStatus,
deriveWorkReviewDecision,
getWorkReviewEmployeeAliasKeys,
getWorkReviewEmployeeIdentityKeys,
getWorkReviewEmployeeMaterialUserIds,
getWorkReviewReviewerSelectionError,
isSameWorkReviewEmployeeAlias,
isSameWorkReviewEmployeeIdentity,
validateWorkReviewAnnotations,
} from '@/lib/work-review-policy'
describe('work review policy', () => {
test('allows any matched employee level to submit work', () => {
expect(canSubmitWorkReview({ levelType: 'P' })).toBe(true)
expect(canSubmitWorkReview({ levelType: 'M' })).toBe(true)
expect(canSubmitWorkReview({ levelType: null })).toBe(true)
expect(canSubmitWorkReview(null)).toBe(false)
})
test('only grants project deletion to the wuxie account', () => {
expect(canDeleteWorkReviewProject({ username: 'wuxie' })).toBe(true)
expect(canDeleteWorkReviewProject({ username: ' WUXIE ' })).toBe(true)
expect(canDeleteWorkReviewProject({ username: '无邪' })).toBe(true)
expect(canDeleteWorkReviewProject({ username: '无邪管理员' })).toBe(false)
expect(canDeleteWorkReviewProject({ username: 'admin' })).toBe(false)
expect(canDeleteWorkReviewProject(null)).toBe(false)
})
test('requires a distinct manual reviewer in addition to the fixed reviewer', () => {
expect(getWorkReviewReviewerSelectionError({
reviewerEmployeeId: null,
submitterEmployeeId: 'submitter',
finalReviewerEmployeeId: 'yuanze',
})).toBe('请选择评审人')
expect(getWorkReviewReviewerSelectionError({
reviewerEmployeeId: 'submitter',
submitterEmployeeId: 'submitter',
finalReviewerEmployeeId: 'yuanze',
})).toContain('不能选择自己')
expect(getWorkReviewReviewerSelectionError({
reviewerEmployeeId: 'yuanze',
submitterEmployeeId: 'submitter',
finalReviewerEmployeeId: 'yuanze',
})).toContain('元泽已是固定评审人')
expect(getWorkReviewReviewerSelectionError({
reviewerEmployeeId: 'reviewer',
submitterEmployeeId: 'submitter',
finalReviewerEmployeeId: 'yuanze',
})).toBeNull()
})
test('recognizes historical profiles that belong to the same material account', () => {
const current = {
id: 'profile-current',
dingtalkUserId: 'ding-current',
dingEmail: 'wuxie@example.com',
jobNumber: 'JS001',
sourcePayload: { materialUserId: 'USER-WUXIE' },
}
const historical = {
id: 'profile-old',
sourcePayload: { userId: 'user-wuxie', materialUserEmail: 'old@example.com' },
}
expect(getWorkReviewEmployeeMaterialUserIds(historical)).toEqual(['user-wuxie'])
expect(getWorkReviewEmployeeIdentityKeys(current)).toContain('user:user-wuxie')
expect(isSameWorkReviewEmployeeIdentity(current, historical)).toBe(true)
expect(isSameWorkReviewEmployeeIdentity(current, {
id: 'profile-other',
jobNumber: 'JS002',
})).toBe(false)
})
test('recognizes simplified and traditional variants of the same employee alias', () => {
const current = { flowerName: '无邪', realName: '江林铖', displayName: '无邪' }
const historical = { flowerName: '無邪', realName: '無邪', displayName: '無邪' }
expect(getWorkReviewEmployeeAliasKeys(historical)).toContain('无邪')
expect(isSameWorkReviewEmployeeAlias(current, historical)).toBe(true)
expect(isSameWorkReviewEmployeeAlias(current, { flowerName: '若兰' })).toBe(false)
})
test('marks one reviewer region as revision required when any image needs changes', () => {
expect(deriveWorkReviewDecision([
{ decision: 'approved' },
{ decision: 'revision_required' },
])).toBe('revision_required')
})
test('does not complete the version until both independent reviewer regions exist', () => {
expect(deriveCompletedWorkReviewStatus([
{ reviewerRole: 'manager', decision: 'approved' },
])).toBeNull()
expect(deriveCompletedWorkReviewStatus([
{ reviewerRole: 'final_reviewer', decision: 'approved' },
])).toBeNull()
})
test('requires both reviewer regions to approve the final version', () => {
expect(deriveCompletedWorkReviewStatus([
{ reviewerRole: 'manager', decision: 'approved' },
{ reviewerRole: 'final_reviewer', decision: 'approved' },
])).toBe('approved')
expect(deriveCompletedWorkReviewStatus([
{ reviewerRole: 'manager', decision: 'revision_required' },
{ reviewerRole: 'final_reviewer', decision: 'approved' },
])).toBe('revision_required')
})
test('completes a manager-only test round immediately after the manager review', () => {
expect(deriveCompletedWorkReviewStatus([
{ reviewerRole: 'manager', decision: 'approved' },
], ['manager'])).toBe('approved')
expect(deriveCompletedWorkReviewStatus([
{ reviewerRole: 'manager', decision: 'revision_required' },
], ['manager'])).toBe('revision_required')
})
test('only allows a new version when both project and latest version require changes', () => {
const baseProject = {
status: 'revision_required',
versions: [{ status: 'revision_required' }],
permissions: { canResubmit: true },
}
expect(canResubmitWorkReviewProject(baseProject)).toBe(true)
expect(canResubmitWorkReviewProject({
...baseProject,
status: 'approved',
versions: [{ status: 'approved' }],
})).toBe(false)
expect(canResubmitWorkReviewProject({
...baseProject,
permissions: { canResubmit: false },
})).toBe(false)
})
test('normalizes image annotations without storing viewport pixels', () => {
expect(validateWorkReviewAnnotations([
{ id: 'point-1', x: 0.123456789, y: 0.75, comment: ' 调整人物位置 ' },
{
id: 'box-1',
x: 0.11111119,
y: 0.22222229,
x2: 0.55555559,
y2: 0.66666669,
shape: 'rectangle',
color: '#D92D20',
comment: ' 框选人物 ',
},
])).toEqual({
ok: true,
annotations: [
{ id: 'point-1', x: 0.123457, y: 0.75, comment: '调整人物位置' },
{
id: 'box-1',
x: 0.111111,
y: 0.222222,
x2: 0.555556,
y2: 0.666667,
shape: 'rectangle',
color: '#d92d20',
comment: '框选人物',
},
],
})
expect(validateWorkReviewAnnotations(undefined)).toEqual({ ok: true, annotations: [] })
})
test.each([
[[{ id: 'point-1', x: -0.1, y: 0.5, comment: '越界' }], '图片批注位置无效'],
[[{ id: 'point-1', x: 0.5, y: '0.5', comment: '类型错误' }], '图片批注位置无效'],
[[{ id: 'box-1', x: 0.2, y: 0.2, shape: 'rectangle', color: '#0050cb', comment: '缺少终点' }], '图片框选范围无效'],
[[{ id: 'box-1', x: 0.2, y: 0.2, x2: 0.5, y2: 0.5, shape: 'triangle', color: '#0050cb', comment: '形状错误' }], '图片批注形状无效'],
[[{ id: 'box-1', x: 0.2, y: 0.2, x2: 0.5, y2: 0.5, shape: 'rectangle', color: '#ff00ff', comment: '颜色错误' }], '图片批注颜色无效'],
[[{ id: 'point-1', x: 0.5, y: 0.5, comment: ' ' }], '请填写每个标注点的修改意见'],
[[
{ id: 'point-1', x: 0.2, y: 0.2, comment: 'A' },
{ id: 'point-1', x: 0.4, y: 0.4, comment: 'B' },
], '图片批注编号无效或重复'],
])('rejects invalid image annotations', (annotations, message) => {
const result = validateWorkReviewAnnotations(annotations)
expect(result.ok).toBe(false)
if (!result.ok) expect(result.error).toBe(message)
})
})