gongxiangp2/lib/__tests__/task-detail-zip-entry-order.test.ts

102 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** @jest-environment node */
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'fs/promises'
import os from 'os'
import path from 'path'
import { createZipFromFiles } from '@/lib/task-material-processor'
import { deleteStoredUploadFileFromRemote, syncStoredUploadFileToRemote } from '@/lib/remote-upload-sync'
import { extractZipEntriesToDirectory, listZipEntries } from '@/lib/zip-entry-reader'
import { groupPreviewRowsIntoRuns, matchRunsToZipEntries } from '@/lib/task-detail-asset-edit'
jest.mock('@/lib/prisma', () => ({ prisma: {} }))
jest.mock('@/lib/s3', () => ({ deleteFromS3: jest.fn() }))
jest.mock('@/lib/public-display', () => ({
buildMaterialPublicDisplayPath: jest.fn(),
uploadPublicDisplayToS3: jest.fn(),
}))
jest.mock('@/lib/image-processor', () => ({ compressImage: jest.fn() }))
jest.mock('@/lib/remote-upload-sync', () => ({
deleteStoredUploadFileFromRemote: jest.fn().mockResolvedValue(undefined),
syncStoredUploadFileToRemote: jest.fn(),
}))
jest.mock('@/lib/material-upload-merge', () => ({
acquireUploadMergeDiskReservation: jest.fn().mockResolvedValue(() => undefined),
}))
/**
* 生产链路(archiver)并发打包不保证中央目录顺序等于传入顺序。
* 单图编辑因此不依赖位置映射,而是按"来源文件名"锚定匹配。
* 这里用真实打包产物验证:条目顺序无论怎样,名字匹配都能对上。
*/
describe('生产打包链路的单图编辑前提', () => {
let uploadRoot: string
let previousUploadDir: string | undefined
beforeEach(async () => {
previousUploadDir = process.env.UPLOAD_DIR
uploadRoot = await mkdtemp(path.join(os.tmpdir(), 'entry-order-'))
process.env.UPLOAD_DIR = uploadRoot
})
afterEach(async () => {
if (previousUploadDir === undefined) {
delete process.env.UPLOAD_DIR
} else {
process.env.UPLOAD_DIR = previousUploadDir
}
await rm(uploadRoot, { recursive: true, force: true })
})
function row(id: string, fileName: string, sortOrder: number): any {
return { id, assetType: 'main-preview', fileUrl: `s3://${id}`, fileName, sortOrder, skuCode: null }
}
test('条目内容完整解压;预览行与条目按名字正确配对(与顺序无关)', async () => {
const names = ['z-last.jpg', 'a-first.jpg', '中图.png', 'm-middle.jpg']
const files: Array<{ path: string; name: string; size: number }> = []
const sourceDir = path.join(uploadRoot, 'src')
await mkdir(sourceDir, { recursive: true })
for (let index = 0; index < names.length; index += 1) {
const name = names[index]
const filePath = path.join(sourceDir, `file-${index}.bin`)
const bytes = Buffer.from(`${name}-${index}`)
await writeFile(filePath, bytes)
files.push({ path: filePath, name, size: bytes.length })
}
const result = await createZipFromFiles(
files,
'material-order-check',
'MODEL',
'任务-详情',
'主图',
'作者',
'店铺',
)
const zipPath = path.join(uploadRoot, 'materials', result.fileName)
// 解压:每个条目内容完整
const destination = path.join(uploadRoot, 'extracted')
const extracted = await extractZipEntriesToDirectory(zipPath, destination)
expect(extracted.map((item) => item.entryName).sort()).toEqual([...names].sort())
const byName = new Map(extracted.map((item) => [item.entryName, item]))
expect((await readFile(byName.get('中图.png')!.filePath)).toString()).toBe('中图.png-2')
// 匹配:即使中央目录顺序被打乱,预览行仍能按基础名找到自己的条目
const entries = await listZipEntries(zipPath)
const entryNames = entries.map((entry) => entry.name)
const runs = groupPreviewRowsIntoRuns([
row('p0', 'z-last.webp', 0),
row('p1', 'a-first.webp', 1),
row('p2', '中图.webp', 2),
row('p3', 'm-middle.webp', 3),
])!
const assigned = matchRunsToZipEntries(runs, entryNames)
expect(entryNames[assigned[0]]).toBe('z-last.jpg')
expect(entryNames[assigned[1]]).toBe('a-first.jpg')
expect(entryNames[assigned[2]]).toBe('中图.png')
expect(entryNames[assigned[3]]).toBe('m-middle.jpg')
})
})