Files
shanxia2.0/web-admin/src/utils/pl-vehicle-cert-zip.test.ts
T
wuzilongandCursor 55abe614f8 feat(pl): add personnel cert zip packing and shared pack core.
Extract packAndSaveCertZip for reuse; keep vehicle API; name folders with _2 on collisions.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 20:38:43 +08:00

354 lines
9.8 KiB
TypeScript

import assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import {
collectVehicleCertZipEntries,
buildVehicleCertZipFilename,
buildAndSaveVehicleCertZip,
collectPersonnelCertZipEntries,
buildPersonnelCertZipFilename,
buildAndSavePersonnelCertZip,
uniqueZipPath,
sanitizeZipPathSegment
} from './pl-vehicle-cert-zip'
describe('sanitizeZipPathSegment', () => {
it('replaces unsafe path chars', () => {
assert.equal(sanitizeZipPathSegment('皖A/D12\\345'), '皖A_D12_345')
assert.equal(sanitizeZipPathSegment(' 皖A '), '皖A')
})
})
describe('collectVehicleCertZipEntries', () => {
it('skips vehicles and certs without urls; prefixes 主·/挂· when trailer certs exist', () => {
const entries = collectVehicleCertZipEntries([
{
id: 'v1',
licensePlate: '皖AD12345',
trailerNo: '挂A8888',
certList: [
{
id: 'c1',
ownerType: 'vehicle',
ownerId: 'v1',
status: '1',
certName: '行驶证',
vehiclePart: 'head',
attachmentUrls: 'https://cdn.example/a.jpg'
},
{
id: 'c2',
ownerType: 'vehicle',
ownerId: 'v1',
status: '1',
certName: '道路运输证',
vehiclePart: 'head',
attachmentUrls: ''
},
{
id: 'c3',
ownerType: 'vehicle',
ownerId: 'v1',
status: '1',
certName: '行驶证',
vehiclePart: 'trailer',
attachmentUrls: 'https://cdn.example/b.pdf'
}
]
},
{
id: 'v2',
licensePlate: '皖BF67890',
certList: []
}
])
assert.equal(entries.length, 1)
assert.equal(entries[0].vehicleId, 'v1')
assert.equal(entries[0].plateFolder, '皖AD12345挂A8888')
assert.equal(entries[0].files.length, 2)
assert.equal(entries[0].files[0].baseName, '主·行驶证')
assert.equal(entries[0].files[0].ext, 'jpg')
assert.equal(entries[0].files[1].baseName, '挂·行驶证')
assert.equal(entries[0].files[1].ext, 'pdf')
})
it('omits 主·/挂· prefix when no trailer certs on that vehicle', () => {
const entries = collectVehicleCertZipEntries([
{
id: 'v1',
licensePlate: '皖BF67890',
certList: [
{
id: 'c1',
ownerType: 'vehicle',
ownerId: 'v1',
status: '1',
certName: '行驶证',
vehiclePart: 'head',
attachmentUrls: 'https://cdn.example/a.jpg,https://cdn.example/a2.png'
}
]
}
])
assert.equal(entries[0].files.length, 2)
assert.equal(entries[0].files[0].baseName, '行驶证')
assert.equal(entries[0].files[1].baseName, '行驶证')
})
})
describe('buildVehicleCertZipFilename', () => {
it('names single-vehicle zip with plate', () => {
assert.equal(
buildVehicleCertZipFilename([{ plateFolder: '皖AD12345挂A8888' }], '20260908'),
'车辆证书_皖AD12345挂A8888.zip'
)
})
it('names multi-vehicle zip with count and date', () => {
assert.equal(
buildVehicleCertZipFilename(
[{ plateFolder: 'A' }, { plateFolder: 'B' }],
'20260908'
),
'车辆证书批量_2辆_20260908.zip'
)
})
})
describe('uniqueZipPath', () => {
it('appends _2 _3 on collisions', () => {
const used = new Set<string>()
assert.equal(uniqueZipPath(used, '行驶证.jpg'), '行驶证.jpg')
assert.equal(uniqueZipPath(used, '行驶证.jpg'), '行驶证_2.jpg')
assert.equal(uniqueZipPath(used, '行驶证.jpg'), '行驶证_3.jpg')
})
})
describe('buildAndSaveVehicleCertZip', () => {
it('packs single vehicle flat; skips failed blobs; returns counts', async () => {
const saved: { name: string; size: number }[] = []
const result = await buildAndSaveVehicleCertZip(
[
{
id: 'v1',
licensePlate: '皖AD12345',
certList: [
{
id: 'c1',
ownerType: 'vehicle',
ownerId: 'v1',
status: '1',
certName: '行驶证',
vehiclePart: 'head',
attachmentUrls: 'https://cdn.example/ok.jpg,https://cdn.example/bad.jpg'
}
]
}
],
{
yyyymmdd: '20260908',
fetchBlob: async (fileUrl) => {
if (fileUrl.includes('bad')) throw new Error('fail')
return new Blob([fileUrl], { type: 'application/octet-stream' })
},
saveAs: async (blob, name) => {
saved.push({ name, size: blob.size })
}
}
)
assert.equal(result.okCount, 1)
assert.equal(result.failCount, 1)
assert.equal(result.zipName, '车辆证书_皖AD12345.zip')
assert.equal(saved.length, 1)
assert.equal(saved[0].name, '车辆证书_皖AD12345.zip')
})
it('uses subfolders for multiple vehicles', async () => {
const paths: string[] = []
await buildAndSaveVehicleCertZip(
[
{
id: 'v1',
licensePlate: '皖A1',
certList: [
{
id: 'c1',
ownerType: 'vehicle',
ownerId: 'v1',
status: '1',
certName: '行驶证',
vehiclePart: 'head',
attachmentUrls: 'https://cdn.example/a.jpg'
}
]
},
{
id: 'v2',
licensePlate: '皖B2',
certList: [
{
id: 'c2',
ownerType: 'vehicle',
ownerId: 'v2',
status: '1',
certName: '营运证',
vehiclePart: 'head',
attachmentUrls: 'https://cdn.example/b.pdf'
}
]
}
],
{
yyyymmdd: '20260908',
fetchBlob: async () => new Blob(['x']),
saveAs: async () => {},
onFileAdded: (zipPath) => paths.push(zipPath)
}
)
assert.deepEqual(paths.sort(), ['皖A1/行驶证.jpg', '皖B2/营运证.pdf'].sort())
})
it('returns empty when no attachments', async () => {
const result = await buildAndSaveVehicleCertZip(
[{ id: 'v1', licensePlate: '皖A1', certList: [] }],
{
yyyymmdd: '20260908',
fetchBlob: async () => new Blob(['x']),
saveAs: async () => {}
}
)
assert.equal(result.okCount, 0)
assert.equal(result.failCount, 0)
assert.equal(result.zipName, '')
})
})
describe('collectPersonnelCertZipEntries', () => {
it('uses personnel name folders; dedupes same names with _2; skips empty', () => {
const entries = collectPersonnelCertZipEntries([
{
id: 'p1',
personnelName: '张三',
certList: [
{
id: 'c1',
ownerType: 'personnel',
ownerId: 'p1',
status: '1',
certName: '驾驶证',
attachmentUrls: 'https://cdn.example/d.jpg'
}
]
},
{
id: 'p2',
personnelName: '张三',
certList: [
{
id: 'c2',
ownerType: 'personnel',
ownerId: 'p2',
status: '1',
certName: '从业资格证',
attachmentUrls: 'https://cdn.example/q.pdf'
}
]
},
{
id: 'p3',
personnelName: '李四',
certList: []
}
])
assert.equal(entries.length, 2)
assert.equal(entries[0].folderLabel, '张三')
assert.equal(entries[1].folderLabel, '张三_2')
assert.equal(entries[0].files[0].baseName, '驾驶证')
assert.equal(entries[1].files[0].baseName, '从业资格证')
})
it('does not add 主·/挂· for personnel certs', () => {
const entries = collectPersonnelCertZipEntries([
{
id: 'p1',
personnelName: '王五',
certList: [
{
id: 'c1',
ownerType: 'personnel',
ownerId: 'p1',
status: '1',
certName: '驾驶证',
vehiclePart: 'trailer',
attachmentUrls: 'https://cdn.example/a.jpg'
}
]
}
])
assert.equal(entries[0].files[0].baseName, '驾驶证')
})
})
describe('buildPersonnelCertZipFilename', () => {
it('names single and multi personnel zips', () => {
assert.equal(
buildPersonnelCertZipFilename([{ folderLabel: '张三' }], '20260908'),
'人员证书_张三.zip'
)
assert.equal(
buildPersonnelCertZipFilename(
[{ folderLabel: '张三' }, { folderLabel: '张三_2' }],
'20260908'
),
'人员证书批量_2人_20260908.zip'
)
})
})
describe('buildAndSavePersonnelCertZip', () => {
it('packs multi personnel into name subfolders', async () => {
const paths: string[] = []
const result = await buildAndSavePersonnelCertZip(
[
{
id: 'p1',
personnelName: '张三',
certList: [
{
id: 'c1',
ownerType: 'personnel',
ownerId: 'p1',
status: '1',
certName: '驾驶证',
attachmentUrls: 'https://cdn.example/a.jpg'
}
]
},
{
id: 'p2',
personnelName: '李四',
certList: [
{
id: 'c2',
ownerType: 'personnel',
ownerId: 'p2',
status: '1',
certName: '身份证',
attachmentUrls: 'https://cdn.example/b.png'
}
]
}
],
{
yyyymmdd: '20260908',
fetchBlob: async () => new Blob(['x']),
saveAs: async () => {},
onFileAdded: (p) => paths.push(p)
}
)
assert.equal(result.zipName, '人员证书批量_2人_20260908.zip')
assert.deepEqual(paths.sort(), ['张三/驾驶证.jpg', '李四/身份证.png'].sort())
})
})