test(fleet): preserve PDF and direct fetch prerequisite evidence
This commit is contained in:
@@ -3,6 +3,8 @@ import assert from 'node:assert/strict'
|
||||
import { mkdtempSync, mkdirSync, readFileSync, existsSync, rmSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, dirname, resolve } from 'node:path'
|
||||
import { AcceptanceRun } from './admin-test-env-acceptance.mjs'
|
||||
import { run as runInboundMeasurement } from './admin-test-env-modules/inbound-measurement.mjs'
|
||||
import { parseFleetArguments, fleetExecutionPlan, runFleetAcceptance, reviewedFullCheckpoint, verifyFleetTerminalState, runFleetHandoverStage, fleetCostShares, prepareFleetCostActors, guardFleetWrites, redactFleetWrite } from './fleet-test-env-acceptance.mjs'
|
||||
|
||||
for (const response of [
|
||||
@@ -35,6 +37,93 @@ test('formal correlated rejection is recorded and remains available for expected
|
||||
await ctx.request('GET', '/readback'); assert.equal(saved.length, 2)
|
||||
})
|
||||
|
||||
test('response evidence preserves business messages and returned resource IDs with explicit session provenance and no tokens', async () => {
|
||||
const saved = [], ctx = { token: 'admin-session', pdaToken: 'pda-session', options: { username: 'test-admin' },
|
||||
async request() { return { http: 200, body: { code: '200', message: 'created', data: {
|
||||
id: '123', businessNo: 'FIXTURE-123', accessToken: 'new-secret', refreshToken: 'refresh-secret' } }, requestId: 'r', traceId: 't' } } }
|
||||
guardFleetWrites(ctx, entry => saved.push(redactFleetWrite(structuredClone(entry))))
|
||||
await ctx.request('POST', '/create', { expectedVersion: 3 })
|
||||
assert.deepEqual(saved[0].actor, { kind: 'PLATFORM', source: 'CONTEXT_ADMIN_SESSION' })
|
||||
assert.equal(saved[1].responseBody.message, 'created'); assert.equal(saved[1].responseBody.data.id, '123')
|
||||
await ctx.request('POST', '/pda', {}, 'pda-session'); assert.equal(saved[2].actor.kind, 'PDA')
|
||||
await ctx.request('POST', '/other', {}, 'unresolved-session'); assert.equal(saved[4].actor.kind, 'UNKNOWN')
|
||||
await ctx.request('POST', '/login', {}, ''); assert.equal(saved[6].actor.source, 'NO_SESSION')
|
||||
for (const secret of ['admin-session', 'pda-session', 'new-secret', 'refresh-secret', 'unresolved-session'])
|
||||
assert.ok(!JSON.stringify(saved).includes(secret))
|
||||
})
|
||||
|
||||
test('POST carton-label PDF is a definite binary response with durable hash and permits same-fixture readback', async () => {
|
||||
const saved = [], calls = [], response = { http: 200, body: null, bytes: Buffer.from('%PDF-1.7\nfixture'),
|
||||
contentType: 'application/pdf', requestId: 'r', traceId: 't' }
|
||||
const ctx = { async requestBinary(method, path) {
|
||||
assert.equal(saved.at(-1).status, 'SENDING'); calls.push(path); return response
|
||||
}, async request() { calls.push('readback') } }
|
||||
guardFleetWrites(ctx, entry => saved.push(structuredClone(entry)))
|
||||
const path = '/api/platform/inbounds/1/carton-label-pdf?tenantId=2&cartonIndex=1'
|
||||
assert.equal(await ctx.requestBinary('POST', path), response)
|
||||
assert.equal(saved[0].path, path); assert.equal(saved[1].status, 'RESPONDED')
|
||||
assert.equal(saved[1].binary.bytes, response.bytes.length); assert.match(saved[1].binary.sha256, /^[a-f0-9]{64}$/)
|
||||
assert.equal(saved[1].binary.validPdf, true)
|
||||
await ctx.request('GET', '/api/platform/inbounds/1'); assert.deepEqual(calls, [path, 'readback'])
|
||||
})
|
||||
|
||||
test('real inbound prerequisite and AcceptanceRun binary transport accept three PDF writes then stop raw multipart retries', async t => {
|
||||
const calls = [], saved = [], summary = { id: '1', tenantId: '2', processStatus: 'WAIT_MEASURE', inboundCode: 'FIXTURE-INBOUND', totalPackageCount: 2 }
|
||||
const native = t.mock.method(globalThis, 'fetch', async (input, init) => {
|
||||
const req = new Request(input, init), path = new URL(req.url).pathname
|
||||
calls.push({ method: req.method, path })
|
||||
const headers = { 'x-request-id': 'r', 'x-trace-id': 't' }
|
||||
if (req.method === 'GET') return new Response(JSON.stringify({ code: '200', data: { summary } }), { headers: { ...headers, 'content-type': 'application/json' } })
|
||||
assert.equal(saved.at(-1).status, 'SENDING')
|
||||
if (path.endsWith('/carton-label-pdf')) return new Response('%PDF-1.7\nfixture', { headers: { ...headers, 'content-type': 'application/pdf' } })
|
||||
assert.ok(path.endsWith('/measurement/carton-scans'))
|
||||
throw Error('transport failure Bearer sensitive-session https://example.invalid/private')
|
||||
})
|
||||
const context = new AcceptanceRun({ gateway: 'https://test.invalid' })
|
||||
context.createdResources.orders = { marker: context.marker, inboundId: '1', tenantId: '2' }
|
||||
const restore = guardFleetWrites(context, entry => saved.push(redactFleetWrite(structuredClone(entry))), { guardFetch: true })
|
||||
try {
|
||||
await runInboundMeasurement(context)
|
||||
assert.equal(calls.filter(row => row.path.endsWith('/carton-label-pdf')).length, 3)
|
||||
assert.equal(calls.filter(row => row.path.endsWith('/measurement/carton-scans')).length, 1, 'raw helper retries must not reach transport')
|
||||
const pdfs = saved.filter(row => row.binary?.validPdf)
|
||||
assert.equal(pdfs.length, 3); assert.ok(pdfs.every(row => row.status === 'RESPONDED'))
|
||||
assert.equal(context.steps.find(row => row.name === '生成箱唛 PDF 并冻结稳定箱码').passed, true)
|
||||
const last = saved.at(-1); assert.equal(last.status, 'UNKNOWN_RESULT')
|
||||
assert.ok(last.body.multipart.some(row => row.field === 'data' && row.base64))
|
||||
assert.ok(last.raw.includes('transport failure')); assert.ok(!last.raw.includes('sensitive-session')); assert.ok(!last.raw.includes('example.invalid'))
|
||||
await assert.rejects(context.request('POST', '/subsequent'), /transport failure/)
|
||||
} finally { restore(); assert.equal(globalThis.fetch, native); t.mock.restoreAll() }
|
||||
})
|
||||
|
||||
test('raw JSON prerequisite writes retain headers, original version and bounded diagnostics without duplicate journaling', async t => {
|
||||
let calls = 0
|
||||
t.mock.method(globalThis, 'fetch', async () => { calls++; return new Response('x'.repeat(600), { status: 502 }) })
|
||||
const saved = [], context = new AcceptanceRun({ gateway: 'https://test.invalid' })
|
||||
const restore = guardFleetWrites(context, entry => saved.push(redactFleetWrite(structuredClone(entry))), { guardFetch: true })
|
||||
try {
|
||||
await assert.rejects(fetch('https://test.invalid/approve', { method: 'PUT', headers: { 'content-type': 'application/json',
|
||||
Authorization: 'Bearer secret', 'Idempotency-Key': 'same-key' }, body: JSON.stringify({ expectedVersion: 6 }) }), /Unknown write outcome/)
|
||||
assert.equal(saved.length, 2); assert.equal(saved[0].body.expectedVersion, 6)
|
||||
assert.equal(saved[0].headers['idempotency-key'], 'same-key'); assert.equal(saved[0].headers.authorization, '[REDACTED]')
|
||||
assert.equal(saved[1].raw.length, 500)
|
||||
await assert.rejects(fetch('https://test.invalid/retry', { method: 'POST' }), /Unknown write outcome/)
|
||||
assert.equal(calls, 1)
|
||||
} finally { restore(); t.mock.restoreAll() }
|
||||
})
|
||||
|
||||
for (const override of [{ http: 0 }, { http: 503 }, { bytes: Buffer.from('<html>proxy</html>') },
|
||||
{ bytes: new Uint8Array() }, { contentType: 'text/html' }, { traceId: null },
|
||||
{ contentType: 'application/json', bytes: Buffer.from('{"code":"200"}'), body: { code: '200' } }])
|
||||
test(`binary write still stops on unknown or invalid response: ${JSON.stringify(override)}`, async () => {
|
||||
const saved = [], ctx = { async requestBinary() { return { http: 200, body: null, bytes: Buffer.from('%PDF-1.7'),
|
||||
contentType: 'application/pdf', requestId: 'r', traceId: 't', ...override } }, async request() { assert.fail('must stop') } }
|
||||
guardFleetWrites(ctx, entry => saved.push(structuredClone(entry)))
|
||||
await assert.rejects(ctx.requestBinary('POST', '/carton-label-pdf'), /Unknown write outcome/)
|
||||
assert.equal(saved[1].status, 'UNKNOWN_RESULT')
|
||||
await assert.rejects(ctx.request('GET', '/readback'), /Unknown write outcome/)
|
||||
})
|
||||
|
||||
test('checkpoint failure stops before dispatch; transport throw stops even after caller catches', async () => {
|
||||
let calls = 0
|
||||
const ctx = { async request() { calls++; throw Error('transport failure') } }
|
||||
|
||||
Reference in New Issue
Block a user