fix(import): 媒体工厂守卫与加固——manifest 形态/侧车门控/冲突约束名/窗口边界与冲突测试

评审加固五项:
- manifest 形态守卫:bundle 在场而 metadata.manifest 非数组 → throw(与
  storageRoot 守卫同风格),不与"bundle 合法缺席=空清单"共用静默空 coercion
- 侧车门控:空清单下级②不可达,跳过侧车加载——破损侧车副本不因无用输入
  让整 job 失败
- public_path 冲突 catch 按约束名精确认领(postgres.js 的 constraint_name,
  drizzle 包一层在 cause)——未来加第三唯一索引不误标,其余 23505 原样上抛
- 集成用例:不同 source_url 同旧 uploads 路径 → 后到不覆盖、issue 报告、
  只留一行(mediaRegistered=1)
- 单测边界对拍:'<svg' 恰好收在 1024 字节窗口内(起点 1020)检出;起点
  1021 被截断为 '<sv' → null

310 测试(308+2);typecheck/lint 三绿。

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-10-21 18:56:16 +08:00
parent ed081ec477
commit 7e6ce91001
3 changed files with 85 additions and 4 deletions
+56
View File
@@ -564,6 +564,62 @@ describe('媒体登记写入器(三级对拍 + 魔数/隔离)', () => {
})
})
it('public_path 唯一键冲突:不同 source_url 同旧 uploads 路径 → 后到不覆盖、issue 报告、只留一行', async () => {
await withTestDb(async (db) => {
const work = await mkdtemp(join(tmpdir(), 'sf-media-pconf-'))
try {
const dup = pngBytes(`dup-${rand()}`)
await mkdir(join(work, '2026/10'), { recursive: true })
await writeFile(join(work, '2026/10/dup.png'), dup)
const manifest = [manifestEntryOf('2026/10/dup.png', dup)]
// 两 attachment:不同源 URL(不同 host 的 uploads 域)、同一旧 uploads 相对路径
// → 级①命中同一清单实体 → public_path 相同 → 第二行撞 media_assets_public_path_uq
const events: AdapterEvent[] = [
attachmentEvent({ sourceId: '971', url: 'https://fixture.example/wp-content/uploads/2026/10/dup.png' }),
attachmentEvent({ sourceId: '972', url: 'https://mirror.example/wp-content/uploads/2026/10/dup.png' }),
]
const { key, reg, wxr } = await registerMini(db, work)
try {
const result = await createImportJob(db, {
siteKey: key,
snapshotId: wxr.id,
reportRoot: join(work, 'artifacts', 'import'),
events: eventsOf(events),
pipeline: directMediaPipeline({ storageRoot: work, manifest, sidecar: [] }),
})
expect(result.status).toBe('succeeded_with_issues')
// 先到行保留、后到不静默覆盖——同公开路径是真实冲突(同站命名空间不重不解)
const assets = (await db.execute(`select source_id, source_url, public_path, status
from media_assets where site_id = '${reg.siteId}' order by source_id`)) as any
expect(plain(assets)).toStrictEqual([
{
source_id: '971',
source_url: 'https://fixture.example/wp-content/uploads/2026/10/dup.png',
public_path: '/wp-content/uploads/2026/10/dup.png',
status: 'ready',
},
])
const report = await readReport(work, result.jobId)
expect(report.counts).toStrictEqual({
mediaRegistered: 1,
mediaFromSidecar: 0,
mediaMissing: 0,
mediaQuarantined: 0,
mediaDanglingRefs: 0,
mediaAmbiguous: 0,
})
const conflict = report.issues.filter((i) => i.includes('public_path 冲突'))
expect(conflict).toHaveLength(1)
expect(conflict[0]).toContain('972')
} finally {
await cleanupSiteRows(db, reg.siteId)
}
} finally {
await rm(work, { recursive: true, force: true })
}
})
})
it('幂等重跑:media_assets/media_usages 零增行、mediaRegistered=0;悬空引用按流如实再现', async () => {
await withTestDb(async (db) => {
const work = await mkdtemp(join(tmpdir(), 'sf-media-idem-'))