124 lines
4.8 KiB
TypeScript
124 lines
4.8 KiB
TypeScript
/** @jest-environment node */
|
|
|
|
import { getServerSession } from 'next-auth'
|
|
|
|
import {
|
|
GET as listChanges,
|
|
POST as submitChange,
|
|
} from '@/app/api/materials/[id]/assets/change-requests/route'
|
|
import { GET as getChange } from '@/app/api/task-detail-change-requests/[id]/route'
|
|
import { POST as decideChange } from '@/app/api/task-detail-change-requests/[id]/decision/route'
|
|
import {
|
|
decideTaskDetailChange,
|
|
getTaskDetailChangeRequest,
|
|
listTaskDetailChanges,
|
|
submitTaskDetailChange,
|
|
TaskDetailChangeApprovalError,
|
|
} from '@/lib/task-detail-change-approval'
|
|
|
|
jest.mock('next-auth', () => ({ getServerSession: jest.fn() }))
|
|
jest.mock('@/lib/auth', () => ({ authOptions: {} }))
|
|
jest.mock('@/lib/task-detail-change-approval', () => ({
|
|
TaskDetailChangeApprovalError: class TaskDetailChangeApprovalError extends Error {
|
|
statusCode: number
|
|
constructor(message: string, statusCode = 400) {
|
|
super(message)
|
|
this.statusCode = statusCode
|
|
}
|
|
},
|
|
decideTaskDetailChange: jest.fn(),
|
|
getTaskDetailChangeRequest: jest.fn(),
|
|
listTaskDetailChanges: jest.fn(),
|
|
submitTaskDetailChange: jest.fn(),
|
|
}))
|
|
|
|
const actor = { id: 'user-1', role: 'USER', isSuperAdmin: false }
|
|
|
|
function requestWith(body: unknown) {
|
|
return { json: async () => body } as any
|
|
}
|
|
|
|
describe('任务详情图片变更审批 API', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
})
|
|
|
|
test('四个入口都拒绝未登录请求', async () => {
|
|
;(getServerSession as jest.Mock).mockResolvedValue(null)
|
|
|
|
const responses = await Promise.all([
|
|
listChanges({} as any, { params: { id: 'material-1' } }),
|
|
submitChange(requestWith({ operations: [] }), { params: { id: 'material-1' } }),
|
|
getChange({} as any, { params: { id: 'request-1' } }),
|
|
decideChange(requestWith({ decision: 'approve' }), { params: { id: 'request-1' } }),
|
|
])
|
|
|
|
expect(responses.map((response) => response.status)).toEqual([401, 401, 401, 401])
|
|
expect(submitTaskDetailChange).not.toHaveBeenCalled()
|
|
expect(decideTaskDetailChange).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('提交和列表使用当前登录人并返回审批结果', async () => {
|
|
;(getServerSession as jest.Mock).mockResolvedValue({ user: actor })
|
|
;(submitTaskDetailChange as jest.Mock).mockResolvedValue({
|
|
mode: 'pending_approval',
|
|
request: { id: 'request-1', status: 'pending_manager_approval' },
|
|
})
|
|
;(listTaskDetailChanges as jest.Mock).mockResolvedValue([{ id: 'request-1' }])
|
|
const operations = [{ kind: 'delete', assetId: 'asset-1' }]
|
|
|
|
const submitted = await submitChange(requestWith({ operations }), { params: { id: 'material-1' } })
|
|
const listed = await listChanges({} as any, { params: { id: 'material-1' } })
|
|
|
|
expect(submitted.status).toBe(200)
|
|
await expect(submitted.json()).resolves.toMatchObject({
|
|
success: true,
|
|
mode: 'pending_approval',
|
|
request: { id: 'request-1' },
|
|
})
|
|
expect(submitTaskDetailChange).toHaveBeenCalledWith('material-1', actor, operations)
|
|
expect(listTaskDetailChanges).toHaveBeenCalledWith('material-1', actor)
|
|
await expect(listed.json()).resolves.toEqual({ success: true, data: [{ id: 'request-1' }] })
|
|
})
|
|
|
|
test('详情与批准接口透传审批服务结果', async () => {
|
|
;(getServerSession as jest.Mock).mockResolvedValue({ user: actor })
|
|
;(getTaskDetailChangeRequest as jest.Mock).mockResolvedValue({ id: 'request-1', canDecide: true })
|
|
;(decideTaskDetailChange as jest.Mock).mockResolvedValue({ status: 'applied' })
|
|
|
|
const detail = await getChange({} as any, { params: { id: 'request-1' } })
|
|
const decision = await decideChange(
|
|
requestWith({ decision: 'approve' }),
|
|
{ params: { id: 'request-1' } },
|
|
)
|
|
|
|
await expect(detail.json()).resolves.toEqual({ success: true, data: { id: 'request-1', canDecide: true } })
|
|
await expect(decision.json()).resolves.toEqual({ success: true, data: { status: 'applied' } })
|
|
expect(decideTaskDetailChange).toHaveBeenCalledWith('request-1', actor, 'approve')
|
|
})
|
|
|
|
test('非法决定在路由层返回 400', async () => {
|
|
;(getServerSession as jest.Mock).mockResolvedValue({ user: actor })
|
|
|
|
const response = await decideChange(
|
|
requestWith({ decision: 'later' }),
|
|
{ params: { id: 'request-1' } },
|
|
)
|
|
|
|
expect(response.status).toBe(400)
|
|
expect(decideTaskDetailChange).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('审批业务错误保留状态码和用户可读文案', async () => {
|
|
;(getServerSession as jest.Mock).mockResolvedValue({ user: actor })
|
|
;(getTaskDetailChangeRequest as jest.Mock).mockRejectedValue(
|
|
new TaskDetailChangeApprovalError('您无权查看这条图片变更申请', 403),
|
|
)
|
|
|
|
const response = await getChange({} as any, { params: { id: 'request-1' } })
|
|
|
|
expect(response.status).toBe(403)
|
|
await expect(response.json()).resolves.toEqual({ success: false, error: '您无权查看这条图片变更申请' })
|
|
})
|
|
})
|