gongxiangp2/lib/__tests__/team-functions.test.ts

63 lines
4.8 KiB
TypeScript

/** @jest-environment node */
import { prisma } from '@/lib/prisma'
import { getTeamFunctions, saveTeamFunction } from '@/lib/team-functions'
jest.mock('@/lib/prisma', () => ({ prisma: {
organizationUnit: { findMany: jest.fn() }, performanceSurveyEmployeeProfile: { findMany: jest.fn() },
teamFunctionConfig: { findMany: jest.fn(), findUnique: jest.fn(), findUniqueOrThrow: jest.fn(), create: jest.fn(), updateMany: jest.fn() },
teamFunctionConfigAudit: { create: jest.fn() }, $transaction: jest.fn(),
} }))
const config = { id: 'cfg', organizationUnitId: 'team', leadEmployeeProfileId: 'lead', description: '职责', branches: [{ label: '职责', items: ['设计'] }], revision: 2 }
const input = { organizationUnitId: 'team', leadEmployeeProfileId: 'lead', description: '更新职责', branches: config.branches, revision: 2 }
const profiles = [{ id: 'lead', displayName: '新花名', realName: '姓名', jobNumber: '001', employmentState: 'ACTIVE', departmentPath: '公司/旧部门', sourcePayload: null,
departmentMemberships: [{ sourceDeptId: '2', departmentPath: '公司/旧部门' }] }]
beforeEach(() => {
jest.clearAllMocks()
;(prisma.organizationUnit.findMany as jest.Mock).mockResolvedValue([
{ id: 'brand', name: '品牌中心', path: '公司/品牌中心', sourceDeptId: 1, parentId: null },
{ id: 'team', name: '新部门名', path: '公司/品牌中心/新部门名', sourceDeptId: 2, parentId: 'brand' },
{ id: 'outside', name: '公司部门', path: '公司/公司部门', sourceDeptId: 3, parentId: null },
])
;(prisma.performanceSurveyEmployeeProfile.findMany as jest.Mock).mockResolvedValue(profiles)
;(prisma.teamFunctionConfig.findMany as jest.Mock).mockResolvedValue([config])
;(prisma.teamFunctionConfig.findUnique as jest.Mock).mockResolvedValue(config)
;(prisma.teamFunctionConfig.updateMany as jest.Mock).mockResolvedValue({ count: 1 })
;(prisma.teamFunctionConfig.findUniqueOrThrow as jest.Mock).mockResolvedValue({ ...config, ...input, revision: 3 })
;(prisma.$transaction as jest.Mock).mockImplementation(callback => callback(prisma))
})
test('名称从当前花名册读取,过期部门快照不改变稳定 ID 归属', async () => {
const result = await getTeamFunctions(false)
expect(result.teams).toHaveLength(1)
expect(result.teams[0]).toMatchObject({ name: '新部门名', lead: { employeeProfileId: 'lead', name: '新花名' }, candidates: [] })
expect(prisma.teamFunctionConfig.create).not.toHaveBeenCalled()
})
test('新增 HR 部门自动出现,未配置职责保持为空', async () => {
;(prisma.teamFunctionConfig.findMany as jest.Mock).mockResolvedValue([])
expect((await getTeamFunctions(true)).teams[0]).toMatchObject({ revision: 0, description: '', branches: [], leadStatus: 'unset', candidates: [{ employeeProfileId: 'lead' }] })
})
test.each(['RESIGNED', 'moved'])('负责人离职或调出后显示失效,不能继续保存该负责人 %s', async state => {
;(prisma.performanceSurveyEmployeeProfile.findMany as jest.Mock).mockResolvedValue([{ ...profiles[0], employmentState: state === 'RESIGNED' ? state : 'ACTIVE', departmentMemberships: [{ sourceDeptId: state === 'moved' ? '3' : '2' }] }])
expect((await getTeamFunctions(true)).teams[0].leadStatus).toBe('invalid')
await expect(saveTeamFunction(input, 'actor')).rejects.toMatchObject({ status: 400 })
expect(prisma.teamFunctionConfig.updateMany).not.toHaveBeenCalled()
})
test('同名品牌中心节点拒绝猜测范围', async () => {
;(prisma.organizationUnit.findMany as jest.Mock).mockResolvedValue([{ id: 'a', name: '品牌中心' }, { id: 'b', name: '品牌中心' }])
await expect(getTeamFunctions(false)).rejects.toMatchObject({ status: 409 })
})
test('过期修订不覆盖其他人的修改', async () => {
await expect(saveTeamFunction({ ...input, revision: 1 }, 'actor')).rejects.toMatchObject({ status: 409 })
expect(prisma.teamFunctionConfig.updateMany).not.toHaveBeenCalled()
expect(prisma.teamFunctionConfigAudit.create).not.toHaveBeenCalled()
})
test('保存及前后快照同一事务,部门改名不会新建配置', async () => {
expect(await saveTeamFunction(input, 'actor')).toEqual({ revision: 3 })
expect(prisma.teamFunctionConfig.updateMany).toHaveBeenCalledWith(expect.objectContaining({ where: { id: 'cfg', revision: 2 } }))
expect(prisma.teamFunctionConfigAudit.create).toHaveBeenCalledWith({ data: expect.objectContaining({ actorUserId: 'actor', before: expect.objectContaining({ revision: 2 }), after: expect.objectContaining({ revision: 3 }) }) })
expect(prisma.$transaction).toHaveBeenCalledWith(expect.any(Function), { isolationLevel: 'Serializable' })
})
test('非当前品牌中心部门不得写入', async () => {
await expect(saveTeamFunction({ ...input, organizationUnitId: 'outside' }, 'actor')).rejects.toMatchObject({ status: 409 })
expect(prisma.teamFunctionConfig.updateMany).not.toHaveBeenCalled()
})