gongxiangp2/scripts/audit-legacy-material-previews.test.mjs
WuYongJie a890660853 fix(materials): audit historical previews and gate production releases
Preserve known legacy failures by reviewed identity, probe material delivery before and after releases, and retain originals and downloads.
2026-10-23 19:47:58 +08:00

92 lines
5.5 KiB
JavaScript

import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import test from 'node:test'
import { AUDIT_LIMITS, auditPreviewBatch, auditUniquePreviews, main, metadataCandidates, validateAuditSnapshot } from './audit-legacy-material-previews.mjs'
import { LEGACY_DOMAINS } from './check-legacy-storage-boundaries.mjs'
const host = LEGACY_DOMAINS[0]
const source = n => `https://${host}/jsxiangche/preview-${n}.webp`
const row = (id, index = id) => ({ id, materialType: '详情模板', previewUrl: source(index) })
const html = '<!doctype html><html><body>Not Found. The server is powered by frp. Faithfully yours, frp.</body></html>'
const response = () => new Response(html, { status: 404, headers: { 'content-type': 'text/html' } })
test('snapshot must match exact count, identity, and registered legacy host before any GET', () => {
const value = { readOnly: true, count: 2, materials: [row('a'), row('b')] }
assert.equal(validateAuditSnapshot(value, { host, expectedCount: 2 }).length, 2)
assert.throws(() => validateAuditSnapshot(value, { host }), /exact_inventory_count_required/)
assert.throws(() => validateAuditSnapshot({ ...value, materials: [row('a'), row('a')] }, { host, expectedCount: 2 }), /unique_material_identity_required/)
assert.throws(() => validateAuditSnapshot({ ...value, materials: [row('a'), { ...row('b'), previewUrl: 'https://evil.example/a.webp' }] }, { host, expectedCount: 2 }), /inventory_host_changed/)
})
test('candidate mapping keeps same-material display/preview evidence and excludes ZIP-like main role', () => {
const material = { ...row('a'), displayImages: [source('display'), 'https://s3.jiansun.vip/gongxiangpan/p.png'],
assets: [{ id: 'main', assetType: 'main', fileUrl: source('looks-like-image'), fileSize: '10' },
{ id: 'good', assetType: 'main-preview', fileUrl: source('real-preview'), fileSize: '0' },
{ id: 'bad', assetType: 'preview', fileUrl: 'https://s3.jiansun.vip/gongxiangpan/download.zip', fileSize: '1000000000' }],
skus: [{ id: 'sku', previewUrl: source('sku'), filePath: 'https://s3.jiansun.vip/gongxiangpan/big.zip' }] }
const result = metadataCandidates(material)
assert.equal(result.materialId, 'a')
assert.equal(result.candidates.length, 4)
assert.equal(result.candidates.find(item => item.identity === 'good').declaredBytes, '0')
assert.ok(result.candidates.every(item => item.metadataOnly && item.bytesRead === 0 && item.originalIdentityVerified === false))
assert.deepEqual(result.excludedAssets.map(item => item.assetId), ['main', 'bad'])
assert.ok(!JSON.stringify(result.candidates).includes('big.zip'))
})
test('FRP failure still retains hash of the complete actual response body', async () => {
let calls = 0
const report = await auditPreviewBatch({ version: 1, reviewed: true, materials: [{ materialId: 'a', registeredSource: source('a') }] },
{ fetchImpl: async () => { calls++; return response() } })
assert.equal(calls, 1)
assert.equal(report.results[0].code, 'frp_default_404')
assert.equal(report.results[0].responseBodyComplete, true)
assert.equal(report.results[0].responseBodySha256, createHash('sha256').update(html).digest('hex'))
assert.equal(report.results[0].responseBytes, Buffer.byteLength(html))
})
test('batches stay at fifty, duplicate URLs map every ID, and at most two GETs run concurrently', async () => {
const materials = Array.from({ length: 103 }, (_, n) => row(`m-${n}`, n % 53))
let active = 0, maxActive = 0
const gets = new Map(), events = []
const result = await auditUniquePreviews(materials, { fetchImpl: async url => {
gets.set(url, (gets.get(url) || 0) + 1)
active++; maxActive = Math.max(maxActive, active)
await new Promise(resolve => setTimeout(resolve, 1))
active--
return response()
}, onBatch: async event => events.push(event) })
assert.equal(maxActive, 2)
assert.equal(result.actualGetAttempts, 53)
assert.equal(gets.size, 53)
assert.ok([...gets.values()].every(value => value === 1))
assert.deepEqual(result.batches.map(item => item.materials.length), [50, 3])
assert.equal(events.length, 2)
assert.equal(result.results.length, 103)
assert.equal(new Set(result.results.map(item => item.materialId)).size, 103)
assert.equal(result.allRowsAccounted, true)
assert.ok(result.results.every(item => item.code === 'frp_default_404' && item.responseBodyComplete))
assert.equal(result.results.find(item => item.materialId === 'm-53').checkedRepresentativeId, 'm-0')
assert.equal(AUDIT_LIMITS.concurrency, 2)
})
test('a non-preview source is recorded without GET and a timeout is never automatically retried', async () => {
const materials = [row('a'), { ...row('b'), previewUrl: 'http://127.0.0.1/private' }]
let calls = 0
const result = await auditUniquePreviews(materials, { timeoutMs: 5, fetchImpl: () => { calls++; return new Promise(() => {}) } })
assert.equal(calls, 1)
assert.equal(result.results[0].classification, 'timeout')
assert.equal(result.results[0].responseBodySha256, null)
assert.equal(result.results[1].attempts, 0)
assert.equal(result.results[1].classification, 'source_rejected')
assert.equal(result.allRowsAccounted, true)
assert.equal(result.retries, 0)
})
test('default invocation is plan-only without SSH, database, images or file writes', async () => {
const result = await main([])
assert.equal(result.planOnly, true)
assert.equal(result.expectedCount, 472)
assert.equal(result.productionWrites, 0)
await assert.rejects(main(['--run']), /run_requires_new_absolute_output_directory/)
})