102 lines
4.1 KiB
TypeScript
102 lines
4.1 KiB
TypeScript
/** @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')
|
||
})
|
||
})
|