/** @jest-environment node */ import { generatePreviewImages, resolveUniqueZipEntryName } from '@/lib/task-material-processor' import { uploadPublicDisplayToS3 } from '@/lib/public-display' import { uploadToS3 } from '@/lib/s3' import { compressImage } from '@/lib/image-processor' jest.mock('fs/promises', () => ({ readFile: jest.fn(), mkdir: jest.fn(), unlink: jest.fn(), rm: jest.fn(), })) jest.mock('@/lib/upload-sessions', () => ({ getUploadedFile: jest.fn(), })) jest.mock('@/lib/upload-sessions-redis', () => ({ deleteUploadSession: jest.fn(), })) jest.mock('@/lib/image-processor', () => ({ compressImage: jest.fn(), })) jest.mock('@/lib/public-display', () => ({ ...jest.requireActual('@/lib/public-display'), uploadPublicDisplayToS3: jest.fn(), })) jest.mock('@/lib/s3', () => ({ uploadToS3: jest.fn(), deleteFromS3: jest.fn(), })) describe('task-material-processor', () => { const fsPromises = jest.requireMock('fs/promises') as { readFile: jest.Mock } const { getUploadedFile } = jest.requireMock('@/lib/upload-sessions') as { getUploadedFile: jest.Mock } let dateNowSpy: jest.SpyInstance let mathRandomSpy: jest.SpyInstance beforeEach(() => { jest.clearAllMocks() fsPromises.readFile.mockResolvedValue(Buffer.from('source-image')) getUploadedFile.mockResolvedValue('/tmp/uploaded/image.png') ;(compressImage as jest.Mock).mockResolvedValue(Buffer.from('compressed-image')) ;(uploadPublicDisplayToS3 as jest.Mock).mockResolvedValue( 'https://s3.jiansun.vip/gongxiangpan/public-display/materials/preview-123.webp', ) ;(uploadToS3 as jest.Mock).mockResolvedValue( 'https://s3.jiansun.vip/gongxiangpan/product-package/preview-123.webp', ) dateNowSpy = jest.spyOn(Date, 'now').mockReturnValue(1711111111111) mathRandomSpy = jest.spyOn(Math, 'random').mockReturnValue(0.123456789) }) afterEach(() => { dateNowSpy.mockRestore() mathRandomSpy.mockRestore() }) test('generatePreviewImages 应将任务类预览图写入 public-display materials 前缀', async () => { const result = await generatePreviewImages( [{ uploadId: 'upload-1', fileName: 'origin.png' }], '任务-3D原图', ) expect(result).toEqual([ 'https://s3.jiansun.vip/gongxiangpan/public-display/materials/preview-123.webp', ]) expect(uploadPublicDisplayToS3).toHaveBeenCalledWith( Buffer.from('compressed-image'), expect.stringMatching(/^materials\/preview-1711111111111-0-[a-z0-9]+\.webp$/), 'image/webp', ) expect(uploadToS3).not.toHaveBeenCalled() }) test('resolveUniqueZipEntryName 应避免同名文件在解压时互相覆盖', () => { const usedNames = new Set() expect(resolveUniqueZipEntryName('01.jpg', 0, usedNames)).toBe('01.jpg') expect(resolveUniqueZipEntryName('01.jpg', 1, usedNames)).toBe('01-2.jpg') expect(resolveUniqueZipEntryName('01.JPG', 2, usedNames)).toBe('01-3.JPG') expect(resolveUniqueZipEntryName('主图/01.jpg', 3, usedNames)).toBe('主图_01.jpg') expect(resolveUniqueZipEntryName('', 4, usedNames)).toBe('file-005') }) })