Preserve known legacy failures by reviewed identity, probe material delivery before and after releases, and retain originals and downloads.
237 lines
16 KiB
JavaScript
237 lines
16 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { createHash } from 'node:crypto'
|
|
import { createRequire } from 'node:module'
|
|
import test from 'node:test'
|
|
import { bundleReleaseGate, buildPlan, canonicalDeliverySource, compareChecks, CRITICAL_MATERIAL_IDS, DELIVERY_FILES, KNOWN_LEGACY_MEMBERS_SHA256, normalizeDeliveryConfig, POLICY, runProbes, runReleaseStage, validateAccepted } from './material-preview-release-gate.mjs'
|
|
import { LEGACY_DOMAINS } from './check-legacy-storage-boundaries.mjs'
|
|
|
|
const sharp = createRequire(import.meta.url)('sharp')
|
|
const PNG = await sharp({ create: { width: 3, height: 2, channels: 3, background: '#4d5e6f' } }).png().toBuffer()
|
|
const hash = value => createHash('sha256').update(value).digest('hex')
|
|
const digest = value => hash(JSON.stringify(value))
|
|
const current = id => `https://s3.jiansun.vip/gongxiangpan/${id}.png`
|
|
const independent = id => `https://${LEGACY_DOMAINS[0]}/jsxiangche/${id}.png`
|
|
const compatible = id => `https://${LEGACY_DOMAINS[1]}/gongxiangpan/${id}.png`
|
|
const FRP = '<html><p>The server is powered by <a href="https://github.com/fatedier/frp">frp</a>.</p><p>Faithfully yours, frp.</p></html>'
|
|
const row = (materialId, registeredSource = current(materialId)) => ({ materialId, registeredSource, sourceSha256: hash(registeredSource) })
|
|
const code = Object.fromEntries(DELIVERY_FILES.map(file => [file, 'c'.repeat(64)]))
|
|
const config = { S3_ENDPOINT: 'http://192.168.58.20:9000', S3_BUCKET: 'gongxiangpan', S3_FILE_PATH: '由壹点AI/',
|
|
S3_PUBLIC_DOMAIN: 'https://s3.jiansun.vip/gongxiangpan', S3_REGION: 'us-east-1', SERVER_IP: '192.168.58.20', MINIO_API_PORT: '9000', APP_PUBLIC_URL: 'https://sc.jiansun.vip' }
|
|
const initial = () => [...CRITICAL_MATERIAL_IDS.map((id, i) => row(id, i ? current(id) : compatible(id))), row('legacy', independent('legacy')), row('untested-current'),
|
|
{ materialId: 'private-source', sourceSha256: hash('uploads/materials/private'), uncheckable: { classification: 'source_rejected', code: 'invalid_url' } }]
|
|
|
|
function database(rows) {
|
|
const events = []
|
|
const normalized = rows.map(r => ({ id: r.materialId, previewUrl: r.registeredSource || 'uploads/materials/private', materialType: '材质' })).sort((a, b) => a.id.localeCompare(b.id, 'en'))
|
|
return { events, prisma: { async $transaction(fn, options) {
|
|
events.push(options)
|
|
return fn({ async $executeRawUnsafe(sql) { events.push(sql) }, material: {
|
|
async count(query) { events.push(query); return normalized.length }, async findMany(query) {
|
|
events.push(query)
|
|
const start = query.cursor ? normalized.findIndex(r => r.id === query.cursor.id) + 1 : 0
|
|
return normalized.slice(start, start + query.take)
|
|
},
|
|
} })
|
|
} } }
|
|
}
|
|
|
|
const fixtureFetch = async url => {
|
|
const parsed = new URL(url)
|
|
if (parsed.hostname === LEGACY_DOMAINS[0]) return new Response(FRP, { status: 404, headers: { 'Content-Type': 'text/html' } })
|
|
if (parsed.hostname === LEGACY_DOMAINS[1]) throw new Error('old same-bucket route unavailable')
|
|
return new Response(PNG, { status: 200, headers: { 'Content-Type': 'image/png' } })
|
|
}
|
|
|
|
async function stage(stageName, rows, { accepted, capture, candidate, probeFetch = fixtureFetch, actualCode = code, targetCode = code, ...input } = {}) {
|
|
const db = database(rows)
|
|
const report = await runReleaseStage({ version: 1, stage: stageName, commit: 'b'.repeat(40), accepted, capture, candidate,
|
|
targetDeliveryConfig: config, ...input }, {
|
|
prismaClient: db.prisma, readDeliveryCode: async () => actualCode, targetDeliveryCode: targetCode,
|
|
environment: config, probe: plan => runProbes(plan, { fetchImpl: probeFetch }),
|
|
// Synthetic offline fixture proofs; the production stdin runner cannot supply
|
|
// these dependency injections and uses the fixed incident/original SHA values.
|
|
legacyFailureProof: { bytes: Buffer.byteLength(FRP), bodySha256: hash(FRP) },
|
|
compatibilityProof: { materialId: CRITICAL_MATERIAL_IDS[0], bodySha256: hash(PNG), width: 3, height: 2 },
|
|
legacyMembers: [row('legacy', independent('legacy'))],
|
|
})
|
|
assert.equal(db.events[1], 'SET TRANSACTION READ ONLY')
|
|
assert.deepEqual(db.events[0], { isolationLevel: 'RepeatableRead', timeout: 60_000, maxWait: 5000 })
|
|
return report
|
|
}
|
|
|
|
test('bootstrap records all metadata, exact 12 materials and all independent legacy rows without inventing health for omitted sources', async () => {
|
|
const report = await stage('bootstrap', initial())
|
|
assert.equal(report.passed, true)
|
|
assert.equal(report.allSelectedHealthy, false)
|
|
assert.equal(report.bootstrapRequiresExplicitShaReview, true)
|
|
assert.equal(report.inventory.length, 15)
|
|
assert.equal(report.plan.selected.length, 13)
|
|
assert.equal(report.checks.length, 14)
|
|
assert.equal(report.knownFailures.length, 2)
|
|
assert.equal(report.plan.unverified.length, 2)
|
|
assert.equal(report.plan.unverified.find(r => r.materialId === 'private-source').reason, 'unsupported_or_missing_registered_source')
|
|
assert.equal(report.budget.retries, 0)
|
|
validateAccepted(report)
|
|
assert.equal(report.checks.find(r => r.materialId === CRITICAL_MATERIAL_IDS[0] && r.role === 'registered').result.ok, false)
|
|
assert.equal(report.checks.find(r => r.materialId === CRITICAL_MATERIAL_IDS[0] && r.role === 'canonical').result.bodySha256, hash(PNG))
|
|
})
|
|
|
|
test('fixed same plan is checked before and after candidate with no metadata or content writes', async () => {
|
|
const accepted = await stage('bootstrap', initial())
|
|
const rows = [...initial(), row('new-current')]
|
|
const capture = await stage('capture', rows, { accepted })
|
|
const candidate = await stage('candidate', rows, { accepted, capture })
|
|
const post = await stage('post', rows, { accepted, capture, candidate })
|
|
assert.equal(capture.plan.selected.some(r => r.materialId === 'new-current' && r.reasons.includes('new_or_changed_source')), true)
|
|
assert.equal(post.passed, true)
|
|
assert.equal(post.planSha256, capture.planSha256)
|
|
assert.equal(candidate.planSha256, capture.planSha256)
|
|
assert.equal(post.inventorySha256, capture.inventorySha256)
|
|
validateAccepted(post)
|
|
})
|
|
|
|
test('missing or forged first baseline stops capture before image GET', async () => {
|
|
let calls = 0
|
|
await assert.rejects(stage('capture', initial(), { probeFetch: async () => { calls++; return fixtureFetch(current('x')) } }), /reviewed_baseline_required/)
|
|
const accepted = await stage('bootstrap', initial())
|
|
accepted.inventory.pop()
|
|
await assert.rejects(stage('capture', initial(), { accepted }), /baseline_digest_mismatch/)
|
|
assert.equal(calls, 0)
|
|
})
|
|
|
|
test('new failure or changed failure type blocks candidate; existing FRP incident remains explicit', async () => {
|
|
const accepted = await stage('bootstrap', initial()), capture = await stage('capture', initial(), { accepted })
|
|
const bad = await stage('candidate', initial(), { accepted, capture, probeFetch: async url => url === current(CRITICAL_MATERIAL_IDS[1])
|
|
? new Response('<html>failed</html>', { status: 200, headers: { 'Content-Type': 'text/html' } }) : fixtureFetch(url) })
|
|
assert.equal(bad.passed, false)
|
|
assert.ok(bad.problems.some(r => r.code === 'new_or_changed_failure'))
|
|
assert.ok(bad.knownFailures.some(r => r.materialId === 'legacy' && r.code === 'frp_default_404'))
|
|
const legacyChange = await stage('candidate', initial(), { accepted, capture, probeFetch: async url => url === independent('legacy')
|
|
? new Response('<Error><Code>NoSuchKey</Code></Error>', { status: 404, headers: { 'Content-Type': 'application/xml' } }) : fixtureFetch(url) })
|
|
assert.equal(legacyChange.passed, false)
|
|
})
|
|
|
|
test('a newly registered legacy failure cannot join the known baseline automatically', async () => {
|
|
const accepted = await stage('bootstrap', initial())
|
|
const capture = await stage('capture', [...initial(), row('new-legacy', independent('new-legacy'))], { accepted })
|
|
assert.equal(capture.passed, false)
|
|
assert.ok(capture.problems.some(r => r.materialId === 'new-legacy' && r.code === 'new_or_changed_failure'))
|
|
})
|
|
|
|
test('bootstrap only permits the exact documented FRP body and original canonical compatibility proof', async () => {
|
|
const wrongLegacy = await stage('bootstrap', initial(), { probeFetch: async url => url === independent('legacy')
|
|
? new Response(FRP + 'changed', { status: 404, headers: { 'Content-Type': 'text/html' } }) : fixtureFetch(url) })
|
|
assert.equal(wrongLegacy.passed, false)
|
|
const wrongCanonical = await stage('bootstrap', initial(), { probeFetch: async url => {
|
|
if (url !== current(CRITICAL_MATERIAL_IDS[0])) return fixtureFetch(url)
|
|
const different = await sharp({ create: { width: 3, height: 2, channels: 3, background: '#112233' } }).png().toBuffer()
|
|
return new Response(different, { headers: { 'Content-Type': 'image/png' } })
|
|
} })
|
|
assert.equal(wrongCanonical.passed, false)
|
|
})
|
|
|
|
test('bootstrap rejects an extra historical row, a replacement ID at equal count, and a changed historical source', async () => {
|
|
for (const rows of [
|
|
[...initial(), row('unreviewed-old', independent('unreviewed-old'))],
|
|
initial().map(r => r.materialId === 'legacy' ? row('replacement-old', independent('legacy')) : r),
|
|
initial().map(r => r.materialId === 'legacy' ? row('legacy', independent('different-source')) : r),
|
|
]) {
|
|
const bootstrap = await stage('bootstrap', rows)
|
|
assert.equal(bootstrap.passed, false)
|
|
assert.ok(bootstrap.problems.some(r => r.code === 'new_or_changed_failure'))
|
|
}
|
|
})
|
|
|
|
test('healthy same URL with different bytes requires review rather than silently rebaselining', async () => {
|
|
const accepted = await stage('bootstrap', initial()), capture = await stage('capture', initial(), { accepted })
|
|
const variant = await sharp({ create: { width: 3, height: 2, channels: 3, background: '#ffffff' } }).png().toBuffer()
|
|
const candidate = await stage('candidate', initial(), { accepted, capture, probeFetch: async url => url === current(CRITICAL_MATERIAL_IDS[1])
|
|
? new Response(variant, { headers: { 'Content-Type': 'image/png' } }) : fixtureFetch(url) })
|
|
assert.equal(candidate.passed, false)
|
|
assert.ok(candidate.problems.some(r => r.code === 'unchanged_url_content_changed'))
|
|
})
|
|
|
|
test('inventory drift, missing coverage, missing candidate, and detector failure all fail closed', async () => {
|
|
const accepted = await stage('bootstrap', initial()), capture = await stage('capture', initial(), { accepted })
|
|
await assert.rejects(stage('candidate', [...initial(), row('concurrent-upload')], { accepted, capture }), /inventory_or_capture_drift/)
|
|
await assert.rejects(stage('post', initial(), { accepted, capture }), /candidate_receipt_required/)
|
|
const broken = structuredClone(capture); broken.checks.pop(); broken.checksSha256 = digest(broken.checks)
|
|
await assert.rejects(stage('candidate', initial(), { accepted, capture: broken }), /probe_coverage_incomplete/)
|
|
const faulty = structuredClone(capture.checks)
|
|
faulty[0].result.ok = false; faulty[0].result.classification = 'decoder_unavailable'; faulty[0].result.code = 'image_decoder_unavailable'
|
|
assert.equal(compareChecks(capture.plan, faulty, capture).passed, false)
|
|
})
|
|
|
|
test('new unsupported source, absent key material and changes beyond budget cannot be dropped', async () => {
|
|
const accepted = await stage('bootstrap', initial())
|
|
await assert.rejects(stage('capture', [...initial(), { materialId: 'unsupported-new', sourceSha256: hash('x'), uncheckable: { code: 'invalid_url' } }], { accepted }), /new_or_changed_uncheckable_preview/)
|
|
assert.throws(() => buildPlan(initial().slice(1), accepted, { publicDomain: 'https://s3.jiansun.vip/gongxiangpan' }), /critical_material_coverage_missing/)
|
|
assert.throws(() => buildPlan([...initial(), ...Array.from({ length: POLICY.probes }, (_, i) => row(`new-${i}`))], accepted,
|
|
{ publicDomain: 'https://s3.jiansun.vip/gongxiangpan' }), /probe_budget_requires_independent_full_acceptance/)
|
|
})
|
|
|
|
test('delivery source or storage code changes expand scope and cannot skip large inventory', async () => {
|
|
const accepted = await stage('bootstrap', initial())
|
|
const changed = { ...code, [DELIVERY_FILES[0]]: 'd'.repeat(64) }
|
|
const capture = await stage('capture', initial(), { accepted, targetCode: changed })
|
|
assert.equal(capture.plan.expandedScope, true)
|
|
assert.ok(capture.plan.selected.some(r => r.materialId === 'untested-current'))
|
|
await assert.rejects(stage('candidate', initial(), { accepted, capture, targetCode: changed }), /candidate_delivery_identity_mismatch/)
|
|
const many = [...initial(), ...Array.from({ length: POLICY.probes }, (_, i) => row(`unchanged-${i}`))]
|
|
const largeBase = await stage('bootstrap', many)
|
|
await assert.rejects(stage('capture', many, { accepted: largeBase, targetCode: changed }), /probe_budget_requires_independent_full_acceptance/)
|
|
const outOfBand = await stage('capture', initial(), { accepted, actualCode: changed, targetCode: changed })
|
|
assert.equal(outOfBand.plan.expandedScope, true, 'a change since the accepted code baseline cannot disappear when target already equals current')
|
|
})
|
|
|
|
test('nonsecret delivery configuration changes expand scope and inconsistent candidate settings stop before GET', async () => {
|
|
const accepted = await stage('bootstrap', initial())
|
|
for (const [key, value] of [['S3_ENDPOINT', 'http://192.168.58.21:9000'], ['S3_BUCKET', 'other-bucket'], ['S3_FILE_PATH', 'other/'],
|
|
['SERVER_IP', '192.168.58.21'], ['MINIO_API_PORT', '9001'], ['S3_REGION', 'other-region']]) {
|
|
const targetDeliveryConfig = { ...config, [key]: value }
|
|
const capture = await stage('capture', initial(), { accepted, targetDeliveryConfig })
|
|
assert.equal(capture.plan.expandedScope, true, key)
|
|
await assert.rejects(stage('candidate', initial(), { accepted, capture, targetDeliveryConfig }), /candidate_delivery_identity_mismatch/)
|
|
}
|
|
await assert.rejects(stage('capture', initial(), { accepted, targetDeliveryConfig: undefined }), /complete_delivery_configuration_required/)
|
|
assert.throws(() => normalizeDeliveryConfig({ ...config, S3_ENDPOINT: 'http://user:secret@192.168.58.20:9000' }), /credential_or_query_in_delivery_configuration/)
|
|
assert.throws(() => normalizeDeliveryConfig({ ...config, S3_ACCESS_KEY_ID: 'must-not-be-logged' }), /complete_delivery_configuration_required/)
|
|
})
|
|
|
|
test('versioned historical membership proof exactly matches the independently reviewed 472 IDs and normalized source digests', async () => {
|
|
const { readFile } = await import('node:fs/promises')
|
|
const policy = JSON.parse(await readFile(new URL('./fixtures/material-preview-known-legacy-20261023.json', import.meta.url), 'utf8'))
|
|
assert.equal(policy.members.length, 472)
|
|
assert.equal(digest(policy.members), KNOWN_LEGACY_MEMBERS_SHA256)
|
|
assert.deepEqual(Object.keys(policy.members[0]).sort(), ['materialId', 'sourceSha256'])
|
|
assert.equal(JSON.stringify(policy).includes('https://'), false)
|
|
})
|
|
|
|
test('canonical mapping preserves bucket and key and never rewrites independent old storage', () => {
|
|
assert.equal(canonicalDeliverySource(independent('original'), 'https://s3.jiansun.vip/gongxiangpan'), independent('original'))
|
|
assert.equal(canonicalDeliverySource(compatible('%E4%B8%AD%E6%96%87'), 'https://s3.jiansun.vip/gongxiangpan'), current('%E4%B8%AD%E6%96%87'))
|
|
assert.throws(() => canonicalDeliverySource(compatible('x'), 'https://elsewhere.invalid/gongxiangpan'), /untrusted_public_bucket_configuration/)
|
|
})
|
|
|
|
test('read-only probe queue is bounded to four requests and one attempt with a fixed wall budget', async () => {
|
|
const accepted = await stage('bootstrap', initial()), plan = buildPlan(initial(), accepted, { publicDomain: 'https://s3.jiansun.vip/gongxiangpan' })
|
|
let active = 0, peak = 0, calls = 0
|
|
await runProbes(plan, { fetchImpl: async (url, options) => {
|
|
calls++; peak = Math.max(peak, ++active)
|
|
assert.equal(options.method, 'GET'); assert.equal(options.redirect, 'manual'); assert.equal(options.cache, 'no-store')
|
|
await new Promise(resolve => setTimeout(resolve, 2)); active--
|
|
return fixtureFetch(url)
|
|
} })
|
|
assert.equal(calls, plan.probes.length); assert.equal(peak, POLICY.concurrency)
|
|
let time = 0
|
|
await assert.rejects(runProbes(plan, { now: () => (time += POLICY.wallMs), fetchImpl: () => { throw Error('must not fetch') } }), /release_probe_wall_budget/)
|
|
})
|
|
|
|
test('exact-checkout bundle loads without an old image containing new scripts', async () => {
|
|
const bundle = await bundleReleaseGate(undefined, { runtimeBase: new URL('../package.json', import.meta.url).href })
|
|
assert.ok(bundle.length < 524288)
|
|
const gate = await import(bundle)
|
|
assert.equal(typeof gate.runReleaseStage, 'function')
|
|
assert.deepEqual(gate.POLICY, POLICY)
|
|
})
|