127 lines
3.8 KiB
TypeScript
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: '系统繁忙,本次上传未完成,请稍后重新上传',
|
|
})
|
|
})
|
|
})
|