258 lines
7.9 KiB
TypeScript
258 lines
7.9 KiB
TypeScript
/** @jest-environment node */
|
||
|
||
import { NextRequest } from 'next/server'
|
||
import { getServerSession } from 'next-auth'
|
||
|
||
import { POST } from '@/app/api/materials/upload/product-package/init/route'
|
||
import { prisma } from '@/lib/prisma'
|
||
import { isTaskNoExists, purgeDeletedMaterialsByTaskNo, validateTaskNo } from '@/lib/product-package'
|
||
|
||
jest.mock('next-auth', () => ({
|
||
getServerSession: jest.fn(),
|
||
}))
|
||
|
||
jest.mock('@/lib/auth', () => ({
|
||
authOptions: {},
|
||
}))
|
||
|
||
jest.mock('@/lib/prisma', () => ({
|
||
prisma: {
|
||
material: {
|
||
create: jest.fn(),
|
||
},
|
||
user: {
|
||
findFirst: jest.fn(),
|
||
findUnique: jest.fn(),
|
||
},
|
||
},
|
||
}))
|
||
|
||
jest.mock('@/lib/product-package', () => ({
|
||
validateTaskNo: jest.fn(() => true),
|
||
isTaskNoExists: jest.fn(() => false),
|
||
purgeDeletedMaterialsByTaskNo: jest.fn(() => Promise.resolve({ deletedMaterials: 0, deletedFiles: 0, failedFiles: 0 })),
|
||
}))
|
||
|
||
jest.mock('@/lib/material-query', () => ({
|
||
INCOMPLETE_SHELL_CREATE_DATA: {
|
||
isIncompleteShell: true,
|
||
},
|
||
}))
|
||
|
||
describe('product-package init route', () => {
|
||
beforeEach(() => {
|
||
jest.clearAllMocks()
|
||
;(getServerSession as jest.Mock).mockResolvedValue({
|
||
// 贴近真实登录态:lib/auth.ts 的 session 回调只写入 id/email/role 等字段,
|
||
// 从不写入 username 或 realName。此前实现误读这两个字段,一路兜底把邮箱
|
||
// 存成了作者名,而旧测试恰好给了一个含 username 的理想化 session,把问题掩盖了。
|
||
user: {
|
||
id: 'user-1',
|
||
email: 'tester@example.com',
|
||
},
|
||
})
|
||
;(prisma.material.create as jest.Mock).mockResolvedValue({
|
||
id: 'material-1',
|
||
})
|
||
;(prisma.user.findFirst as jest.Mock).mockResolvedValue({
|
||
id: 'requester-1',
|
||
username: 'operator',
|
||
realName: '运营甲',
|
||
})
|
||
;(prisma.user.findUnique as jest.Mock).mockResolvedValue({
|
||
username: 'tester',
|
||
})
|
||
})
|
||
|
||
test('未填写出图方式时应拒绝初始化任务详情上传', async () => {
|
||
const request = new NextRequest('http://localhost/api/materials/upload/product-package/init', {
|
||
method: 'POST',
|
||
body: JSON.stringify({
|
||
productModel: 'SPU-001',
|
||
categoryName: '旗舰店',
|
||
sceneName: '客厅',
|
||
taskDetailRequesterUserId: 'requester-1',
|
||
}),
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
const response = await POST(request)
|
||
const body = await response.json()
|
||
|
||
expect(response.status).toBe(400)
|
||
expect(body.success).toBe(false)
|
||
expect(body.error).toContain('出图方式')
|
||
expect(prisma.material.create).not.toHaveBeenCalled()
|
||
})
|
||
|
||
test('非法出图方式应返回 400', async () => {
|
||
const request = new NextRequest('http://localhost/api/materials/upload/product-package/init', {
|
||
method: 'POST',
|
||
body: JSON.stringify({
|
||
productModel: 'SPU-001',
|
||
categoryName: '旗舰店',
|
||
taskDetailSourceType: '照片',
|
||
}),
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
const response = await POST(request)
|
||
const body = await response.json()
|
||
|
||
expect(response.status).toBe(400)
|
||
expect(body.error).toContain('出图方式')
|
||
expect(prisma.material.create).not.toHaveBeenCalled()
|
||
})
|
||
|
||
test('新品私密组合会在初始化阶段被拒绝', async () => {
|
||
const request = new NextRequest('http://localhost/api/materials/upload/product-package/init', {
|
||
method: 'POST',
|
||
body: JSON.stringify({
|
||
productModel: 'SPU-001',
|
||
categoryName: '旗舰店',
|
||
taskDetailSourceType: '渲染',
|
||
taskDetailDemandType: '新品',
|
||
taskDetailPrivate: true,
|
||
taskDetailRequesterUserId: 'requester-1',
|
||
}),
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
const response = await POST(request)
|
||
const body = await response.json()
|
||
|
||
expect(response.status).toBe(400)
|
||
expect(body.error).toBe('仅优化需求支持私密上传')
|
||
expect(prisma.material.create).not.toHaveBeenCalled()
|
||
})
|
||
|
||
test('优化私密会原样持久化', async () => {
|
||
const request = new NextRequest('http://localhost/api/materials/upload/product-package/init', {
|
||
method: 'POST',
|
||
body: JSON.stringify({
|
||
productModel: 'SPU-001',
|
||
categoryName: '旗舰店',
|
||
taskNo: 'TASK202604230001',
|
||
taskDetailSourceType: '渲染',
|
||
taskDetailDemandType: '优化',
|
||
taskDetailPrivate: true,
|
||
taskDetailRequesterUserId: 'requester-1',
|
||
taskDetailRequesterName: '伪造姓名',
|
||
}),
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
const response = await POST(request)
|
||
|
||
expect(response.status).toBe(200)
|
||
expect(validateTaskNo).toHaveBeenCalledWith('TASK202604230001')
|
||
expect(isTaskNoExists).toHaveBeenCalledWith('TASK202604230001')
|
||
expect(purgeDeletedMaterialsByTaskNo).not.toHaveBeenCalled()
|
||
expect(prisma.material.create).toHaveBeenCalledWith({
|
||
data: expect.objectContaining({
|
||
taskDetailSourceType: '渲染',
|
||
taskDetailDemandType: '优化',
|
||
taskDetailPrivate: true,
|
||
taskDetailRequesterUserId: 'requester-1',
|
||
taskDetailRequesterName: '运营甲',
|
||
// 作者名必须取自数据库中的用户名,而不是登录态里并不存在的字段,
|
||
// 更不能兜底成邮箱
|
||
authorName: 'tester',
|
||
isIncompleteShell: true,
|
||
}),
|
||
})
|
||
expect(prisma.user.findUnique).toHaveBeenCalledWith({
|
||
where: { id: 'user-1' },
|
||
select: { username: true },
|
||
})
|
||
})
|
||
|
||
test('缺省需求类型和私密时回填新品公开', async () => {
|
||
const request = new NextRequest('http://localhost/api/materials/upload/product-package/init', {
|
||
method: 'POST',
|
||
body: JSON.stringify({
|
||
productModel: 'SPU-001',
|
||
categoryName: '旗舰店',
|
||
taskDetailSourceType: '渲染',
|
||
taskDetailRequesterUserId: 'requester-1',
|
||
}),
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
const response = await POST(request)
|
||
|
||
expect(response.status).toBe(200)
|
||
expect(prisma.material.create).toHaveBeenCalledWith({
|
||
data: expect.objectContaining({
|
||
taskDetailDemandType: '新品',
|
||
taskDetailPrivate: false,
|
||
}),
|
||
})
|
||
})
|
||
|
||
test('未选择需求运营人员时应返回 400', async () => {
|
||
const request = new NextRequest('http://localhost/api/materials/upload/product-package/init', {
|
||
method: 'POST',
|
||
body: JSON.stringify({
|
||
productModel: 'SPU-001',
|
||
categoryName: '旗舰店',
|
||
}),
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
const response = await POST(request)
|
||
const body = await response.json()
|
||
|
||
expect(response.status).toBe(400)
|
||
expect(body.error).toBe('请选择需求运营人员')
|
||
expect(prisma.user.findFirst).not.toHaveBeenCalled()
|
||
expect(prisma.material.create).not.toHaveBeenCalled()
|
||
})
|
||
|
||
test('需求运营人员不存在或已停用时应返回 400', async () => {
|
||
;(prisma.user.findFirst as jest.Mock).mockResolvedValue(null)
|
||
const request = new NextRequest('http://localhost/api/materials/upload/product-package/init', {
|
||
method: 'POST',
|
||
body: JSON.stringify({
|
||
productModel: 'SPU-001',
|
||
categoryName: '旗舰店',
|
||
taskDetailRequesterUserId: 'inactive-user',
|
||
}),
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
})
|
||
|
||
const response = await POST(request)
|
||
const body = await response.json()
|
||
|
||
expect(response.status).toBe(400)
|
||
expect(body.error).toContain('不存在或已停用')
|
||
expect(prisma.user.findFirst).toHaveBeenCalledWith({
|
||
where: {
|
||
id: 'inactive-user',
|
||
isActive: true,
|
||
status: 'active',
|
||
},
|
||
select: {
|
||
id: true,
|
||
username: true,
|
||
realName: true,
|
||
},
|
||
})
|
||
expect(prisma.material.create).not.toHaveBeenCalled()
|
||
})
|
||
})
|