/** @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 })), })) 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: '旗舰店', taskNo: 'TASK202604230001', taskDetailSourceType: '渲染', 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: '渲染', taskDetailRequesterUserId: 'requester-1', taskDetailRequesterName: '运营甲', // 作者名必须取自数据库中的用户名,而不是登录态里并不存在的字段, // 更不能兜底成邮箱 authorName: 'tester', isIncompleteShell: true, }), }) expect(prisma.user.findUnique).toHaveBeenCalledWith({ where: { id: 'user-1' }, select: { username: true }, }) }) 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() }) })