618 lines
21 KiB
TypeScript
618 lines
21 KiB
TypeScript
/** @jest-environment node */
|
||
|
||
import { prisma } from '@/lib/prisma'
|
||
import {
|
||
assertPerformanceSurveyPositionPlanPreviewMatches,
|
||
copyPerformanceSurveyTemplateVersion,
|
||
createPerformanceSurveyManualPositionAssignments,
|
||
getPerformanceSurveyPositionAdminConfig,
|
||
listPerformanceSurveyManualPositionAssignments,
|
||
listPerformanceSurveyPositionGroups,
|
||
removePerformanceSurveyTemplate,
|
||
savePerformanceSurveyPositionGroupDraft,
|
||
savePerformanceSurveyTemplateDraft,
|
||
} from '@/lib/performance-survey-position-config'
|
||
import { DEFAULT_PERFORMANCE_SURVEY_TEMPLATES } from '@/lib/performance-survey-templates'
|
||
|
||
jest.mock('@/lib/prisma', () => ({
|
||
prisma: {
|
||
performanceSurveyPositionGroup: {
|
||
findUnique: jest.fn(),
|
||
findMany: jest.fn(),
|
||
update: jest.fn(),
|
||
},
|
||
performanceSurveyPositionRule: {
|
||
deleteMany: jest.fn(),
|
||
createMany: jest.fn(),
|
||
update: jest.fn(),
|
||
},
|
||
performanceSurveyTemplateVersion: {
|
||
findUnique: jest.fn(),
|
||
findMany: jest.fn(),
|
||
},
|
||
performanceSurveyTemplate: {
|
||
create: jest.fn(),
|
||
findUnique: jest.fn(),
|
||
findMany: jest.fn(),
|
||
},
|
||
performanceSurveyPositionPlanItem: {
|
||
count: jest.fn(),
|
||
},
|
||
performanceSurvey: {
|
||
count: jest.fn(),
|
||
},
|
||
performanceSurveyEmployeeProfile: {
|
||
findMany: jest.fn(),
|
||
},
|
||
performanceSurveyEmployeePositionAssignment: {
|
||
findMany: jest.fn(),
|
||
},
|
||
$transaction: jest.fn(),
|
||
},
|
||
}))
|
||
|
||
describe('performance survey position admin', () => {
|
||
beforeEach(() => {
|
||
jest.clearAllMocks()
|
||
})
|
||
|
||
test('保存岗位组草稿只更新方案草稿,不改线上岗位组和匹配规则', async () => {
|
||
const liveGroup = {
|
||
id: 'group-1',
|
||
code: 'brand_designer',
|
||
name: '线上岗位名',
|
||
sortOrder: 10,
|
||
isActive: true,
|
||
isSystem: true,
|
||
archivedAt: null,
|
||
}
|
||
const existingDraft = {
|
||
id: 'draft-1',
|
||
positionGroupId: liveGroup.id,
|
||
version: 2,
|
||
status: 'draft',
|
||
effectiveMonth: '2026-08',
|
||
matchRules: [{ positionTitle: '线上规则' }],
|
||
groupConfig: {
|
||
name: liveGroup.name,
|
||
sortOrder: liveGroup.sortOrder,
|
||
isActive: liveGroup.isActive,
|
||
},
|
||
items: [],
|
||
}
|
||
const updatedDraft = {
|
||
...existingDraft,
|
||
matchRules: [{ positionTitle: '草稿规则' }],
|
||
groupConfig: {
|
||
name: '草稿岗位名',
|
||
sortOrder: 20,
|
||
isActive: false,
|
||
},
|
||
}
|
||
const tx = {
|
||
performanceSurveyPositionPlan: {
|
||
findFirst: jest.fn().mockResolvedValue(existingDraft),
|
||
update: jest.fn().mockResolvedValue(updatedDraft),
|
||
findUnique: jest.fn().mockResolvedValue(updatedDraft),
|
||
},
|
||
performanceSurveyPositionPlanItem: {
|
||
deleteMany: jest.fn(),
|
||
createMany: jest.fn(),
|
||
},
|
||
performanceSurveyPositionGroup: {
|
||
findUnique: jest.fn().mockResolvedValue(liveGroup),
|
||
update: jest.fn(),
|
||
},
|
||
performanceSurveyPositionRule: {
|
||
deleteMany: jest.fn(),
|
||
createMany: jest.fn(),
|
||
update: jest.fn(),
|
||
},
|
||
performanceSurveyConfigAuditLog: {
|
||
create: jest.fn().mockResolvedValue({ id: 'audit-1' }),
|
||
},
|
||
$queryRawUnsafe: jest.fn().mockResolvedValue([{ locked: '1' }]),
|
||
}
|
||
|
||
;(prisma.performanceSurveyPositionGroup.findUnique as jest.Mock).mockResolvedValue(liveGroup)
|
||
;(prisma.$transaction as jest.Mock).mockImplementation(async (callback) => callback(tx))
|
||
|
||
await expect(savePerformanceSurveyPositionGroupDraft({
|
||
groupId: liveGroup.id,
|
||
name: '草稿岗位名',
|
||
sortOrder: 20,
|
||
isActive: false,
|
||
rules: [{ positionTitle: '草稿规则' }],
|
||
effectiveMonth: '2026-08',
|
||
reason: '配置调整',
|
||
actorUserId: 'admin-1',
|
||
actorName: '管理员',
|
||
})).resolves.toMatchObject({
|
||
groupId: liveGroup.id,
|
||
draftPlanId: existingDraft.id,
|
||
version: existingDraft.version,
|
||
})
|
||
|
||
expect(tx.performanceSurveyPositionPlan.update).toHaveBeenCalledWith(expect.objectContaining({
|
||
where: { id: existingDraft.id },
|
||
data: expect.objectContaining({
|
||
matchRules: [{ positionTitle: '草稿规则', departmentPathPrefix: null }],
|
||
groupConfig: {
|
||
name: '草稿岗位名',
|
||
sortOrder: 20,
|
||
isActive: false,
|
||
},
|
||
}),
|
||
}))
|
||
expect(tx.performanceSurveyConfigAuditLog.create).toHaveBeenCalledTimes(1)
|
||
expect(prisma.performanceSurveyPositionGroup.update).not.toHaveBeenCalled()
|
||
expect(prisma.performanceSurveyPositionRule.deleteMany).not.toHaveBeenCalled()
|
||
expect(prisma.performanceSurveyPositionRule.createMany).not.toHaveBeenCalled()
|
||
expect(tx.performanceSurveyPositionGroup.update).not.toHaveBeenCalled()
|
||
expect(tx.performanceSurveyPositionRule.deleteMany).not.toHaveBeenCalled()
|
||
expect(tx.performanceSurveyPositionRule.createMany).not.toHaveBeenCalled()
|
||
expect(tx.$queryRawUnsafe).toHaveBeenCalledWith(
|
||
expect.stringContaining('pg_advisory_xact_lock'),
|
||
`performance-survey-position-group:${liveGroup.id}`,
|
||
)
|
||
})
|
||
|
||
test('发布只能使用发布前检查确认过的同一份草稿', () => {
|
||
const draft = {
|
||
id: 'draft-1',
|
||
version: 3,
|
||
updatedAt: new Date('2026-07-23T10:00:00.000Z'),
|
||
}
|
||
expect(() => assertPerformanceSurveyPositionPlanPreviewMatches(draft, {
|
||
draftPlanId: draft.id,
|
||
draftVersion: draft.version,
|
||
draftUpdatedAt: '2026-07-23T10:00:00.000Z',
|
||
})).not.toThrow()
|
||
|
||
expect(() => assertPerformanceSurveyPositionPlanPreviewMatches(draft, {
|
||
draftPlanId: draft.id,
|
||
draftVersion: draft.version,
|
||
draftUpdatedAt: '2026-07-23T10:00:01.000Z',
|
||
})).toThrow('岗位方案草稿已发生变化,请重新执行发布前检查')
|
||
})
|
||
|
||
test('人员手动分配原因可以留空', async () => {
|
||
const assignment = {
|
||
id: 'assignment-1',
|
||
employeeProfileId: 'employee-1',
|
||
assignmentType: 'position',
|
||
positionGroupId: 'group-1',
|
||
startMonth: '2026-07',
|
||
endMonth: null,
|
||
reason: '',
|
||
status: 'active',
|
||
}
|
||
const tx = {
|
||
$queryRawUnsafe: jest.fn().mockResolvedValue([{ locked: '1' }]),
|
||
performanceSurveyEmployeePositionAssignment: {
|
||
findFirst: jest.fn().mockResolvedValue(null),
|
||
create: jest.fn().mockResolvedValue(assignment),
|
||
},
|
||
performanceSurveyConfigAuditLog: {
|
||
create: jest.fn().mockResolvedValue({ id: 'audit-1' }),
|
||
},
|
||
}
|
||
|
||
;(prisma.performanceSurveyPositionGroup.findUnique as jest.Mock).mockResolvedValue({
|
||
id: 'group-1',
|
||
archivedAt: null,
|
||
})
|
||
;(prisma.performanceSurveyEmployeeProfile.findMany as jest.Mock).mockResolvedValue([{
|
||
id: 'employee-1',
|
||
displayName: '张三',
|
||
}])
|
||
;(prisma.$transaction as jest.Mock).mockImplementation(async (callback) => callback(tx))
|
||
|
||
await expect(createPerformanceSurveyManualPositionAssignments({
|
||
employeeIds: ['employee-1'],
|
||
positionGroupId: 'group-1',
|
||
assignmentType: 'position',
|
||
effectiveFromMonth: '2026-07',
|
||
effectiveToMonth: null,
|
||
reason: '',
|
||
actorUserId: 'admin-1',
|
||
actorName: '管理员',
|
||
})).resolves.toMatchObject({ count: 1 })
|
||
|
||
expect(tx.performanceSurveyEmployeePositionAssignment.create).toHaveBeenCalledWith({
|
||
data: expect.objectContaining({ reason: '' }),
|
||
})
|
||
})
|
||
|
||
test('管理员人员分配可搜索没有素材库账号的在职花名册人员', async () => {
|
||
;(prisma.performanceSurveyEmployeeProfile.findMany as jest.Mock).mockResolvedValue([{
|
||
id: 'employee-xiangzhang',
|
||
displayName: '香樟',
|
||
flowerName: '香樟',
|
||
realName: '马震宇',
|
||
jobNumber: 'JS06002901',
|
||
departmentName: '品牌设计部',
|
||
departmentPath: '江苏简尚品牌管理有限公司/品牌中心/品牌设计部',
|
||
positionTitle: '品牌设计师',
|
||
positionLevel: 'P6',
|
||
levelType: 'P',
|
||
isLeadership: false,
|
||
isDingtalkAdmin: false,
|
||
isDingtalkBoss: false,
|
||
isDingtalkSenior: false,
|
||
departmentMemberships: [],
|
||
}])
|
||
;(prisma.performanceSurveyPositionGroup.findMany as jest.Mock).mockResolvedValue([])
|
||
;(prisma.performanceSurveyEmployeePositionAssignment.findMany as jest.Mock).mockResolvedValue([])
|
||
|
||
const params = new URLSearchParams({
|
||
month: '2026-07',
|
||
search: '香樟',
|
||
})
|
||
await expect(listPerformanceSurveyManualPositionAssignments(params)).resolves.toMatchObject({
|
||
total: 1,
|
||
items: [{
|
||
employeeId: 'employee-xiangzhang',
|
||
displayName: '香樟',
|
||
}],
|
||
})
|
||
|
||
const query = (prisma.performanceSurveyEmployeeProfile.findMany as jest.Mock).mock.calls[0][0]
|
||
expect(JSON.stringify(query.where)).toContain('employmentStatus')
|
||
expect(JSON.stringify(query.where)).not.toContain('materialUserId')
|
||
expect(JSON.stringify(query.where)).not.toContain('materialUserEmail')
|
||
})
|
||
|
||
test('模板管理配置只查询模板,不扫描岗位和人员档案', async () => {
|
||
;(prisma.performanceSurveyTemplate.findMany as jest.Mock).mockResolvedValue([{
|
||
id: 'template-1',
|
||
code: 'visual_review',
|
||
name: '视觉评分',
|
||
description: '视觉作品评分',
|
||
surveyType: 'visual_case',
|
||
isActive: true,
|
||
archivedAt: null,
|
||
archiveReason: null,
|
||
versions: [{
|
||
id: 'version-1',
|
||
version: 1,
|
||
status: 'published',
|
||
effectiveMonth: '2026-07',
|
||
definition: structuredClone(DEFAULT_PERFORMANCE_SURVEY_TEMPLATES[0]),
|
||
publishedAt: new Date('2026-07-01T00:00:00.000Z'),
|
||
createdByUserId: 'admin-1',
|
||
createdByName: '管理员',
|
||
createdAt: new Date('2026-07-01T00:00:00.000Z'),
|
||
updatedAt: new Date('2026-07-01T00:00:00.000Z'),
|
||
}],
|
||
}])
|
||
|
||
await expect(getPerformanceSurveyPositionAdminConfig('2026-07', 'templates')).resolves.toMatchObject({
|
||
month: '2026-07',
|
||
groups: [],
|
||
templates: [{ id: 'template-1', code: 'visual_review' }],
|
||
issueCounts: { unmatched: 0, conflicts: 0, excluded: 0 },
|
||
})
|
||
|
||
expect(prisma.performanceSurveyTemplate.findMany).toHaveBeenCalledTimes(1)
|
||
expect(prisma.performanceSurveyPositionGroup.findMany).not.toHaveBeenCalled()
|
||
expect(prisma.performanceSurveyEmployeeProfile.findMany).not.toHaveBeenCalled()
|
||
})
|
||
|
||
test('岗位管理配置返回每个岗位的具体人员名单', async () => {
|
||
;(prisma.performanceSurveyPositionGroup.findMany as jest.Mock).mockResolvedValue([{
|
||
id: 'group-creative',
|
||
code: 'ai_planning_creative',
|
||
name: 'AI策划-创意部',
|
||
sortOrder: 10,
|
||
isActive: true,
|
||
isSystem: true,
|
||
archivedAt: null,
|
||
archiveReason: null,
|
||
rules: [],
|
||
plans: [],
|
||
_count: { surveys: 1, manualAssignments: 1 },
|
||
}])
|
||
;(prisma.performanceSurveyTemplate.findMany as jest.Mock).mockResolvedValue([])
|
||
;(prisma.performanceSurveyEmployeeProfile.findMany as jest.Mock).mockResolvedValue([{
|
||
id: 'employee-ruolan',
|
||
displayName: '若兰',
|
||
departmentName: '品牌创意B部',
|
||
departmentPath: '集团/品牌中心/品牌创意B部',
|
||
positionTitle: '营销策划主管',
|
||
departmentMemberships: [],
|
||
}])
|
||
;(prisma.performanceSurveyEmployeePositionAssignment.findMany as jest.Mock).mockResolvedValue([{
|
||
id: 'assignment-ruolan',
|
||
employeeProfileId: 'employee-ruolan',
|
||
assignmentType: 'position',
|
||
positionGroupId: 'group-creative',
|
||
status: 'active',
|
||
startMonth: '2026-07',
|
||
endMonth: null,
|
||
createdAt: new Date('2026-07-01T00:00:00.000Z'),
|
||
}])
|
||
|
||
await expect(getPerformanceSurveyPositionAdminConfig('2026-07', 'positions')).resolves.toMatchObject({
|
||
groups: [{
|
||
id: 'group-creative',
|
||
memberCount: 1,
|
||
members: [{
|
||
id: 'employee-ruolan',
|
||
displayName: '若兰',
|
||
departmentName: '品牌创意B部',
|
||
positionTitle: '营销策划主管',
|
||
}],
|
||
}],
|
||
})
|
||
})
|
||
|
||
test('复制模板会创建独立模板和完整草稿版本', async () => {
|
||
const definition = structuredClone(DEFAULT_PERFORMANCE_SURVEY_TEMPLATES[0])
|
||
;(prisma.performanceSurveyTemplateVersion.findUnique as jest.Mock).mockResolvedValue({
|
||
id: 'version-1',
|
||
templateId: 'template-1',
|
||
version: 2,
|
||
status: 'published',
|
||
definition,
|
||
template: {
|
||
id: 'template-1',
|
||
code: definition.code,
|
||
name: definition.name,
|
||
archivedAt: null,
|
||
},
|
||
})
|
||
const tx = {
|
||
performanceSurveyTemplate: {
|
||
create: jest.fn().mockImplementation(async ({ data }) => ({ id: 'template-copy', ...data })),
|
||
},
|
||
performanceSurveyTemplateVersion: {
|
||
create: jest.fn().mockImplementation(async ({ data }) => ({ id: 'version-copy', ...data })),
|
||
},
|
||
performanceSurveyConfigAuditLog: {
|
||
create: jest.fn().mockResolvedValue({ id: 'audit-copy' }),
|
||
},
|
||
}
|
||
;(prisma.$transaction as jest.Mock).mockImplementation(async (callback) => callback(tx))
|
||
|
||
await expect(copyPerformanceSurveyTemplateVersion({
|
||
versionId: 'version-1',
|
||
actorUserId: 'admin-1',
|
||
actorName: '管理员',
|
||
})).resolves.toMatchObject({
|
||
id: 'version-copy',
|
||
templateId: 'template-copy',
|
||
name: `${definition.name}(副本)`,
|
||
})
|
||
|
||
const templateData = tx.performanceSurveyTemplate.create.mock.calls[0][0].data
|
||
const versionData = tx.performanceSurveyTemplateVersion.create.mock.calls[0][0].data
|
||
expect(templateData.code).toMatch(/^template_copy_[a-f0-9]{32}$/)
|
||
expect(templateData.name).toBe(`${definition.name}(副本)`)
|
||
expect(versionData).toMatchObject({
|
||
templateId: 'template-copy',
|
||
version: 1,
|
||
status: 'draft',
|
||
definition: expect.objectContaining({
|
||
code: templateData.code,
|
||
name: `${definition.name}(副本)`,
|
||
shortName: `${definition.name}(副本)`,
|
||
questions: definition.questions,
|
||
workflow: definition.workflow,
|
||
}),
|
||
})
|
||
expect(tx.performanceSurveyConfigAuditLog.create).toHaveBeenCalledWith({
|
||
data: expect.objectContaining({
|
||
action: 'template.copied',
|
||
targetId: 'template-copy',
|
||
}),
|
||
})
|
||
})
|
||
|
||
test('修改已发布模板直接更新原记录并同步模板资料', async () => {
|
||
const definition = {
|
||
...structuredClone(DEFAULT_PERFORMANCE_SURVEY_TEMPLATES[0]),
|
||
name: '更新后的模板名称',
|
||
shortName: '更新后的模板名称',
|
||
}
|
||
const existing = {
|
||
id: 'version-1',
|
||
templateId: 'template-1',
|
||
version: 2,
|
||
status: 'published',
|
||
definition: structuredClone(DEFAULT_PERFORMANCE_SURVEY_TEMPLATES[0]),
|
||
updatedAt: new Date('2026-07-29T08:00:00.000Z'),
|
||
template: {
|
||
id: 'template-1',
|
||
code: definition.code,
|
||
name: '原模板名称',
|
||
archivedAt: null,
|
||
},
|
||
}
|
||
const saved = {
|
||
...existing,
|
||
definition,
|
||
updatedAt: new Date('2026-07-29T09:00:00.000Z'),
|
||
}
|
||
const tx = {
|
||
performanceSurveyTemplateVersion: {
|
||
findUnique: jest.fn()
|
||
.mockResolvedValueOnce(existing)
|
||
.mockResolvedValueOnce(saved),
|
||
updateMany: jest.fn().mockResolvedValue({ count: 1 }),
|
||
},
|
||
performanceSurveyTemplate: {
|
||
update: jest.fn().mockResolvedValue({ id: 'template-1' }),
|
||
},
|
||
performanceSurveyConfigAuditLog: {
|
||
create: jest.fn().mockResolvedValue({ id: 'audit-update' }),
|
||
},
|
||
$queryRawUnsafe: jest.fn().mockResolvedValue([{ locked: '1' }]),
|
||
}
|
||
;(prisma.performanceSurveyTemplateVersion.findUnique as jest.Mock).mockResolvedValue(existing)
|
||
;(prisma.$transaction as jest.Mock).mockImplementation(async (callback) => callback(tx))
|
||
|
||
await expect(savePerformanceSurveyTemplateDraft({
|
||
versionId: existing.id,
|
||
definition,
|
||
actorUserId: 'admin-1',
|
||
actorName: '管理员',
|
||
})).resolves.toMatchObject({
|
||
id: existing.id,
|
||
status: 'published',
|
||
definition: expect.objectContaining({ name: '更新后的模板名称' }),
|
||
})
|
||
|
||
expect(tx.performanceSurveyTemplateVersion.updateMany).toHaveBeenCalledWith({
|
||
where: {
|
||
id: existing.id,
|
||
status: 'published',
|
||
updatedAt: existing.updatedAt,
|
||
},
|
||
data: {
|
||
definition,
|
||
createdByUserId: 'admin-1',
|
||
createdByName: '管理员',
|
||
},
|
||
})
|
||
expect(tx.performanceSurveyTemplate.update).toHaveBeenCalledWith({
|
||
where: { id: 'template-1' },
|
||
data: expect.objectContaining({ name: '更新后的模板名称' }),
|
||
})
|
||
expect(tx.performanceSurveyConfigAuditLog.create).toHaveBeenCalledWith({
|
||
data: expect.objectContaining({ action: 'template.version_updated' }),
|
||
})
|
||
})
|
||
|
||
test('当前或未来岗位方案仍在使用模板时拒绝删除', async () => {
|
||
;(prisma.performanceSurveyTemplate.findUnique as jest.Mock).mockResolvedValue({
|
||
id: 'template-1',
|
||
code: 'visual_review',
|
||
versions: [{ id: 'version-1', status: 'published' }],
|
||
})
|
||
;(prisma.performanceSurveyPositionPlanItem.count as jest.Mock).mockResolvedValue(1)
|
||
;(prisma.performanceSurvey.count as jest.Mock).mockResolvedValue(2)
|
||
;(prisma.performanceSurveyPositionGroup.findMany as jest.Mock).mockResolvedValue([{
|
||
name: '品牌设计师',
|
||
plans: [{
|
||
id: 'plan-1',
|
||
status: 'published',
|
||
effectiveMonth: '2026-07',
|
||
version: 1,
|
||
items: [{ id: 'item-1' }],
|
||
}],
|
||
}])
|
||
|
||
await expect(removePerformanceSurveyTemplate({
|
||
templateId: 'template-1',
|
||
actorUserId: 'admin-1',
|
||
actorName: '管理员',
|
||
reason: '不再使用',
|
||
})).rejects.toThrow('请先从岗位考核项中移除并发布新方案')
|
||
|
||
expect(prisma.$transaction).not.toHaveBeenCalled()
|
||
})
|
||
|
||
test('历史查询保留已删除岗位和模板,常规查询默认排除已删除岗位', async () => {
|
||
const definition = structuredClone(DEFAULT_PERFORMANCE_SURVEY_TEMPLATES[0])
|
||
;(prisma.performanceSurveyPositionGroup.findMany as jest.Mock).mockResolvedValueOnce([{
|
||
id: 'group-archived',
|
||
code: 'archived-group',
|
||
name: '已删除岗位',
|
||
sortOrder: 10,
|
||
isActive: true,
|
||
isSystem: false,
|
||
archivedAt: new Date('2026-07-01T00:00:00.000Z'),
|
||
plans: [{
|
||
id: 'plan-history',
|
||
status: 'published',
|
||
effectiveMonth: '2026-06',
|
||
version: 1,
|
||
groupConfig: {
|
||
name: '已删除岗位',
|
||
sortOrder: 10,
|
||
isActive: true,
|
||
},
|
||
matchRules: [],
|
||
items: [{
|
||
id: 'item-history',
|
||
isEnabled: true,
|
||
sortOrder: 10,
|
||
templateVersion: {
|
||
id: 'version-history',
|
||
version: 1,
|
||
status: 'published',
|
||
effectiveMonth: '2026-01',
|
||
definition,
|
||
template: {
|
||
archivedAt: new Date('2026-07-01T00:00:00.000Z'),
|
||
},
|
||
},
|
||
}],
|
||
}],
|
||
}])
|
||
|
||
await expect(listPerformanceSurveyPositionGroups('2026-06', {
|
||
includeArchived: true,
|
||
})).resolves.toMatchObject([{
|
||
id: 'group-archived',
|
||
status: 'ready',
|
||
templates: [{
|
||
templateVersionId: 'version-history',
|
||
templateArchivedAt: new Date('2026-07-01T00:00:00.000Z'),
|
||
}],
|
||
}])
|
||
expect(prisma.performanceSurveyPositionGroup.findMany).toHaveBeenNthCalledWith(
|
||
1,
|
||
expect.objectContaining({ where: undefined }),
|
||
)
|
||
|
||
;(prisma.performanceSurveyPositionGroup.findMany as jest.Mock).mockResolvedValueOnce([])
|
||
await expect(listPerformanceSurveyPositionGroups('2026-07')).resolves.toEqual([])
|
||
expect(prisma.performanceSurveyPositionGroup.findMany).toHaveBeenNthCalledWith(
|
||
2,
|
||
expect.objectContaining({ where: { archivedAt: null } }),
|
||
)
|
||
})
|
||
|
||
test('包含历史岗位时,已删除岗位不会在删除月份及之后重新激活', async () => {
|
||
const archivedGroup = {
|
||
id: 'group-archived',
|
||
code: 'archived-group',
|
||
name: '旧岗位',
|
||
sortOrder: 10,
|
||
isActive: true,
|
||
isSystem: false,
|
||
// 北京时间 7 月 1 日 00:30,UTC 仍是 6 月;业务月份必须按北京时间判断。
|
||
archivedAt: new Date('2026-06-30T16:30:00.000Z'),
|
||
plans: [],
|
||
}
|
||
const activeGroup = {
|
||
id: 'group-active',
|
||
code: 'active-group',
|
||
name: '新岗位',
|
||
sortOrder: 20,
|
||
isActive: true,
|
||
isSystem: false,
|
||
archivedAt: null,
|
||
plans: [],
|
||
}
|
||
;(prisma.performanceSurveyPositionGroup.findMany as jest.Mock)
|
||
.mockResolvedValue([archivedGroup, activeGroup])
|
||
|
||
await expect(listPerformanceSurveyPositionGroups('2026-07', {
|
||
includeArchived: true,
|
||
})).resolves.toMatchObject([
|
||
{
|
||
id: 'group-archived',
|
||
isActive: false,
|
||
status: 'disabled',
|
||
},
|
||
{
|
||
id: 'group-active',
|
||
isActive: true,
|
||
},
|
||
])
|
||
})
|
||
})
|