gongxiangp2/lib/__tests__/product-package-complete-route-security.test.ts
2026-07-30 18:00:28 +08:00

127 lines
3.8 KiB
TypeScript

/** @jest-environment node */
import { getServerSession } from 'next-auth'
import { prisma } from '@/lib/prisma'
import { POST } from '@/app/api/materials/upload/product-package/complete/route'
import {
completeProductPackageUpload,
} from '@/lib/product-package-completion'
jest.mock('next-auth', () => ({ getServerSession: jest.fn() }))
jest.mock('@/lib/auth', () => ({ authOptions: {} }))
jest.mock('@/lib/prisma', () => ({
prisma: {
material: {
findUnique: jest.fn(),
},
},
}))
jest.mock('@/lib/product-package-completion', () => {
const actual = jest.requireActual('@/lib/product-package-completion')
return {
...actual,
completeProductPackageUpload: jest.fn(),
}
})
describe('product-package complete public route security', () => {
beforeEach(() => {
jest.clearAllMocks()
;(getServerSession as jest.Mock).mockResolvedValue({ user: { id: 'user-1' } })
;(completeProductPackageUpload as jest.Mock).mockResolvedValue({
materialId: 'material-1',
taskNo: null,
isFullSet: false,
})
})
test.each([
['fileUrl', { fileUrl: 'http://127.0.0.1:3000/internal' }],
['localFilePath', { localFilePath: 'C:\\Windows\\win.ini' }],
])('公开 complete 路由拒绝 %s 来源', async (_source, sourceInput) => {
const response = await POST({
json: jest.fn().mockResolvedValue({
materialId: 'material-1',
assets: {
main: [{ ...sourceInput, fileName: 'main.png', fileSize: 10 }],
},
}),
} as any)
expect(response.status).toBe(400)
expect(prisma.material.findUnique).not.toHaveBeenCalled()
})
test('非任务详情素材不能使用专用完成接口', async () => {
;(prisma.material.findUnique as jest.Mock).mockResolvedValue({
id: 'material-1',
userId: 'user-1',
materialType: '3D场景',
taskDetailRequesterUserId: 'requester-1',
})
const response = await POST({
json: jest.fn().mockResolvedValue({
materialId: 'material-1',
assets: { main: [] },
}),
} as any)
const body = await response.json()
expect(response.status).toBe(400)
expect(body.error).toContain('不是任务-详情')
expect(completeProductPackageUpload).not.toHaveBeenCalled()
})
test('缺少需求运营的新流程素材不能完成上传', async () => {
;(prisma.material.findUnique as jest.Mock).mockResolvedValue({
id: 'material-1',
userId: 'user-1',
materialType: '任务-详情',
taskDetailRequesterUserId: null,
})
const response = await POST({
json: jest.fn().mockResolvedValue({
materialId: 'material-1',
assets: { main: [] },
}),
} as any)
const body = await response.json()
expect(response.status).toBe(400)
expect(body.error).toContain('缺少需求运营人员')
expect(completeProductPackageUpload).not.toHaveBeenCalled()
})
test('数据库连接繁忙时返回可理解的临时故障提示', async () => {
;(prisma.material.findUnique as jest.Mock).mockResolvedValue({
id: 'material-1',
userId: 'user-1',
materialType: '任务-详情',
taskDetailRequesterUserId: 'requester-1',
})
;(completeProductPackageUpload as jest.Mock).mockRejectedValue(
Object.assign(
new Error('Transaction API error: Unable to start a transaction in the given time.'),
{ code: 'P2028' },
),
)
const response = await POST({
json: jest.fn().mockResolvedValue({
materialId: 'material-1',
assets: { main: [] },
}),
} as any)
const body = await response.json()
expect(response.status).toBe(503)
expect(response.headers.get('Retry-After')).toBe('5')
expect(body).toEqual({
success: false,
code: 'DATABASE_BUSY',
error: '系统繁忙,本次上传未完成,请稍后重新上传',
})
})
})