gongxiangp2/lib/__tests__/work-review-first-pass.test.ts
2026-09-21 17:49:40 +08:00

221 lines
6.9 KiB
TypeScript

/** @jest-environment node */
import type { Session } from 'next-auth'
import { prisma } from '@/lib/prisma'
import { getPerformanceSurveyContext } from '@/lib/performance-survey-service'
import { getFirstPassReviewProjects } from '@/lib/work-review-service'
jest.mock('@/lib/prisma', () => ({
prisma: {
workReviewVersion: { groupBy: jest.fn() },
workReviewProject: { count: jest.fn(), findMany: jest.fn() },
},
}))
jest.mock('@/lib/performance-survey-service', () => ({
getPerformanceSurveyContext: jest.fn(),
}))
jest.mock('@/lib/performance-survey-report-sync', () => ({
getPerformanceSurveyAccountProfileWhere: jest.fn(async () => ({})),
}))
jest.mock('@/lib/work-review-notifications', () => ({
ensureWorkReviewNotificationWorkerStarted: jest.fn(),
queueWorkReviewNotification: jest.fn(async () => undefined),
}))
const session = { user: { id: 'manager-user' } } as Session
describe('department dashboard first-pass review details', () => {
beforeEach(() => {
jest.clearAllMocks()
;(getPerformanceSurveyContext as jest.Mock).mockResolvedValue({
user: { id: 'manager-user', username: 'manager' },
employee: { id: 'manager-employee', displayName: '主管' },
scopeDepartmentPath: '江苏简尚品牌管理有限公司/品牌中心',
isSystemAdmin: true,
isDepartmentAdmin: true,
isLeadership: true,
})
;(prisma.workReviewVersion.groupBy as jest.Mock).mockResolvedValue([
{ status: 'approved', _count: { _all: 2 } },
{ status: 'pending_review', _count: { _all: 1 } },
])
;(prisma.workReviewProject.count as jest.Mock).mockImplementation(({ where }) => (
JSON.stringify(where).includes('"status":"approved"') ? Promise.resolve(5) : Promise.resolve(1)
))
;(prisma.workReviewProject.findMany as jest.Mock)
.mockResolvedValueOnce([{
id: 'project-1',
title: '品牌主视觉',
brand: null,
description: '第一版视觉作品',
status: 'revision_required',
latestVersion: 2,
submitterName: '提交人',
managerName: '主管',
finalReviewerName: '元泽',
workDate: new Date('2026-08-28T00:00:00.000Z'),
createdAt: new Date('2026-08-28T01:00:00.000Z'),
updatedAt: new Date('2026-08-30T01:00:00.000Z'),
versions: [{
status: 'approved',
submittedAt: new Date('2026-08-28T01:00:00.000Z'),
completedAt: new Date('2026-08-28T03:00:00.000Z'),
reviews: [{
reviewerRole: 'manager',
reviewerName: '主管',
decision: 'approved',
reviewedAt: new Date('2026-08-28T02:00:00.000Z'),
}],
}],
}])
.mockResolvedValueOnce([
{ submitterName: '林二', status: 'approved', versions: [{ status: 'revision_required' }] },
{ submitterName: '林一', status: 'approved', versions: [{ status: 'approved' }] },
{ submitterName: '林一', status: 'approved', versions: [] },
{ submitterName: '提交人', status: 'revision_required', versions: [{ status: 'approved' }] },
])
})
test('summary keeps the full brand-center V1 population while search narrows only detail rows', async () => {
const result = await getFirstPassReviewProjects(session, {
page: 2,
pageSize: 10,
query: '主视觉',
})
expect(prisma.workReviewVersion.groupBy).toHaveBeenCalledWith({
by: ['status'],
where: {
version: 1,
project: { is: {} },
},
_count: { _all: true },
})
expect(prisma.workReviewProject.count).toHaveBeenCalledWith({
where: {
AND: [
{},
{ status: 'approved' },
],
},
})
expect(prisma.workReviewProject.count).toHaveBeenCalledWith({
where: expect.objectContaining({
AND: expect.arrayContaining([
expect.objectContaining({ AND: expect.arrayContaining([
{},
{ versions: { some: { version: 1 } } },
]) }),
expect.objectContaining({ OR: expect.any(Array) }),
]),
}),
})
expect(prisma.workReviewProject.findMany).toHaveBeenCalledWith(expect.objectContaining({
select: expect.objectContaining({
id: true,
title: true,
description: true,
createdAt: true,
versions: {
where: { version: 1 },
select: {
status: true,
submittedAt: true,
completedAt: true,
reviews: {
select: {
reviewerRole: true,
reviewerName: true,
decision: true,
reviewedAt: true,
},
orderBy: [{ reviewedAt: 'asc' }, { reviewerRole: 'asc' }],
},
},
take: 1,
},
}),
orderBy: [{ updatedAt: 'desc' }, { workDate: 'desc' }, { id: 'desc' }],
skip: 10,
take: 10,
}))
expect(prisma.workReviewProject.findMany).not.toHaveBeenCalledWith(expect.objectContaining({
select: expect.objectContaining({ imageUrls: true }),
}))
expect(prisma.workReviewProject.findMany).toHaveBeenNthCalledWith(2, {
where: {
AND: [
{},
{ versions: { some: { version: 1 } } },
],
},
select: {
submitterName: true,
status: true,
versions: {
where: { version: 1 },
select: { status: true },
take: 1,
},
},
})
expect(result.summary).toMatchObject({
totalUploads: 3,
totalApproved: 5,
firstPassApproved: 2,
rate: 40,
})
expect(result.submitterRates).toEqual([
{
submitterName: '林一',
totalUploads: 1,
totalApproved: 2,
firstPassApproved: 1,
rate: 50,
},
{
submitterName: '林二',
totalUploads: 1,
totalApproved: 1,
firstPassApproved: 0,
rate: 0,
},
{
submitterName: '提交人',
totalUploads: 1,
totalApproved: 0,
firstPassApproved: 1,
rate: null,
},
])
expect(result.items).toEqual([{
id: 'project-1',
title: '品牌主视觉',
brand: null,
description: '第一版视觉作品',
currentStatus: 'revision_required',
latestVersion: 2,
submitterName: '提交人',
managerName: '主管',
finalReviewerName: '元泽',
workDate: '2026-08-28T00:00:00.000Z',
createdAt: '2026-08-28T01:00:00.000Z',
updatedAt: '2026-08-30T01:00:00.000Z',
firstVersionStatus: 'approved',
firstVersionSubmittedAt: '2026-08-28T01:00:00.000Z',
firstVersionCompletedAt: '2026-08-28T03:00:00.000Z',
firstVersionReviews: [{
reviewerRole: 'manager',
reviewerName: '主管',
decision: 'approved',
reviewedAt: '2026-08-28T02:00:00.000Z',
}],
}])
expect(result.pagination).toEqual({ page: 2, pageSize: 10, total: 1, totalPages: 1 })
})
})