import assert from 'node:assert/strict'; import { createHash } from 'node:crypto'; import { test } from 'node:test'; import { buildAssetMappings } from '../../../apps/server/scripts/asset-mapping.mjs'; const hash = (value) => createHash('sha256').update(value).digest('hex'); function fixture() { const sourceRef = 'cloud://legacy.env/assets/previews/math/a.jpg'; const asset = { id: 'keep-original-id', sourceRef, sourceRefHash: hash(sourceRef), purpose: 'preview', visibility: 'public', ownerUserId: null, status: 'pending', }; const file = { relativePath: 'assets/previews/math/a.jpg', key: 'doodle-assets/assets/previews/math/a.jpg', bucket: 'test-public', visibility: 'public', mimeType: 'image/jpeg', size: 10, sha256: hash('local'), etag: 'etag', status: 'uploaded', verifiedBy: 'qiniu-etag-and-size', }; return { asset, file, report: { schemaVersion: 1, mode: 'apply', ok: true, files: [file] }, config: { publicBucket: 'test-public', prefix: 'doodle-assets/' }, }; } test('资产回填:保留 ID、完整 key 与原始来源;重复映射幂等', () => { const { asset, file, report, config } = fixture(); const [mapped] = buildAssetMappings(report, [asset], config); assert.equal(mapped.asset.id, asset.id); assert.equal(mapped.data.objectKey, file.key); assert.equal( mapped.data.objectRefHash, hash(JSON.stringify(['qiniu', file.bucket, file.key])), ); assert.equal(mapped.data.status, 'ready'); assert.equal(mapped.unchanged, false); assert.equal( buildAssetMappings(report, [{ ...asset, ...mapped.data }], config)[0] .unchanged, true, ); assert.throws( () => buildAssetMappings( report, [{ ...asset, ...mapped.data, objectKey: 'edited-key' }], config, ), /READY_ASSET_CONFLICT/, ); }); test('资产回填:预演报告、未核对对象、缺失映射和来源不一致整批拒绝', () => { const { asset, file, report, config } = fixture(); assert.throws( () => buildAssetMappings({ ...report, mode: 'dry-run' }, [asset], config), /INVALID_SYNC_REPORT/, ); for (const changed of [ { status: 'planned' }, { bucket: 'another-bucket' }, { key: 'wrong-prefix/a.jpg' }, { verifiedBy: null }, { sha256: 'bad' }, ]) assert.throws( () => buildAssetMappings( { ...report, files: [{ ...file, ...changed }] }, [asset], config, ), /INVALID_REPORT_FILE/, ); assert.throws( () => buildAssetMappings( report, [{ ...asset, sourceRefHash: 'bad' }], config, ), /INVALID_SOURCE_REFERENCE/, ); assert.throws( () => buildAssetMappings({ ...report, files: [] }, [asset], config), /ASSET_MAPPING_MISSING_OR_INELIGIBLE/, ); });