Files
baiyue-firstleg/scripts/partial-loading-actual-test-env.test.mjs
T

350 lines
26 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { plan, execute, executionOptions, validateManifest, fixtureSource, expectedRows, scale, verifyActual, runCase, redact,
multipartAdapter, requestInit, validateResumeProof, validateResumeLive, resumeMeasurementDraft, validateZeroWorkTask, validatePreparedResume,
originalCompletionCommand, verifyPhysicalFacts, verifyOldOwnersUnchanged, verifyDownstream } from './partial-loading-actual-test-env.mjs'
import { fileURLToPath } from 'node:url'
import { resolve } from 'node:path'
import { createServer } from 'node:http'
const runId = 'PARTIAL-ACTUAL-offline'
const evidence = Object.fromEntries(['PHOTO_STEP_1','PHOTO_STEP_2','PHOTO_STEP_3','PHOTO_STEP_4','SEAL'].map(type => [type, 'sample.png']))
function fixture(kind = 'B') {
const definitions = kind === 'B' ? [[1, 11, 2, 'BUY_DECLARATION'], [2, 22, 1, 'TAX_REFUND']]
: [[1, 11, 3, 'TAX_REFUND'], [2, 11, 1, 'TAX_REFUND'], [3, 22, 1, 'BUY_DECLARATION'], [4, 33, 1, 'TAX_REFUND']]
const task = { id: '91', loadingPlanId: '90', containerBookingId: '92', version: 0,
items: definitions.map(([n, tenant, count]) => ({ id: String(n), loadingPlanItemId: String(n + 100), tenantId: String(tenant),
inboundId: String(kind === 'A' && n === 2 ? 1 : n), inboundDetailId: String(n + 200), plannedPackageCount: count,
pickedPackageCount: 0, itemStatus: 'PENDING', unitType: 'PACKAGE', palletId: null, version: 0 })) }
const preload = { items: definitions.map(([n, tenant, count, tax]) => ({ itemId: String(n + 100), accountId: String(tenant),
inboundId: String(kind === 'A' && n === 2 ? 1 : n), inboundDetailId: String(n + 200), customsTaxMode: tax, orderId: String(n + 300),
packageCount: count, productQuantity: count * 7, unitsPerPackage: 7, grossWeightKg: '10.001', netWeightKg: '8.002', dimensionalWeightKg: '6.004', volumeCbm: '0.1001' })) }
const spec = { kind, sourceReport: `${kind}.json`, evidence, sealNo: 'SEAL-TEST', actions: definitions.map(([n]) => ({ itemId: String(n),
action: n === 1 ? 'PARTIAL' : kind === 'A' && n === 3 ? 'PICK' : 'SKIP', ...(n === 1 ? { finalPickedPackageCount: kind === 'A' ? 2 : 1 } : {}) })) }
const expected = expectedRows(spec, task, preload)
const actualItems = expected.filter(row => row.actual > 0).map(row => ({ ...row.frozen, packageCount: row.actual,
productQuantity: row.actual * 7, ...Object.fromEntries(['grossWeightKg','netWeightKg','dimensionalWeightKg','volumeCbm'].map(field =>
[field, scale(row.frozen[field], row.actual, row.planned, field === 'volumeCbm' ? 4 : 3)])) }))
const sum = (field, digits) => Number(actualItems.reduce((value, item) => value + BigInt(scale(item[field], 1, 1, digits).replace('.', '')), 0n)) / 10 ** digits
const detail = { items: actualItems, summary: { totalPackageCount: actualItems.reduce((sum, row) => sum + row.packageCount, 0),
totalQuantity: actualItems.reduce((sum, row) => sum + row.productQuantity, 0), totalGrossWeightKg: sum('grossWeightKg', 3),
totalNetWeightKg: sum('netWeightKg', 3), totalVolumeCbm: sum('volumeCbm', 4) } }
const customs = { actualBoxCount: detail.summary.totalPackageCount, inbounds: expected.filter(row => row.actual > 0),
taxRefundCount: kind === 'B' ? 0 : 1, version: 0, status: kind === 'B' ? 'READY_TO_START' : 'WAITING_CUSTOMER_DOCUMENTS' }
const source = { pdaTaskId: '91', loadingPlanId: '90', containerBookingId: '92', warehouseId: '1', sourceMarker: `${runId}-${kind}` }
return { task, preload, spec, expected, detail, customs, source }
}
test('default plan and --plan make zero network calls', async () => {
const original = globalThis.fetch
globalThis.fetch = () => { throw Error('No network in plan') }
try { assert.deepEqual(await execute([], [], {}), plan()); assert.equal((await execute(['--plan'], [], {})).networkRequests, 0) }
finally { globalThis.fetch = original }
})
test('execution refuses absent explicit TEST/profile/cwd', () => {
assert.throws(() => executionOptions(['--execute'], [], {}))
assert.throws(() => executionOptions(['--execute','--environment=TEST',`--run-id=${runId}`,'--fixture=x.json'], [], {}, 'C:/other'))
})
test('NEW mode uses the configured /api base and does not accept a bare origin', () => {
const cwd = fileURLToPath(new URL('..', import.meta.url))
const env = { CHONGCHONG_ENV: 'test', CHONGCHONG_ADMIN_API_BASE_URL: 'https://test.invalid/api',
CHONGCHONG_ADMIN_USERNAME: 'admin', CHONGCHONG_ADMIN_PASSWORD: 'test-only', ADMIN_PDA_USERNAME: 'pda', ADMIN_PDA_PASSWORD: 'test-only' }
const args = ['--execute','--environment=TEST',`--run-id=${runId}`,'--fixture=NEW']
const execArgs = [`--env-file=${resolve('/.codex/env/chongchong-global/test-acceptance.env')}`]
assert.equal(executionOptions(args, execArgs, env, cwd).fixture, 'NEW')
assert.equal(executionOptions(args, execArgs, env, cwd).gateway, 'https://test.invalid')
assert.throws(() => executionOptions(args, execArgs, { ...env, CHONGCHONG_ADMIN_API_BASE_URL: 'https://test.invalid' }, cwd), /end in/)
})
test('nested generated customer passwords and login/refresh tokens never reach saved evidence', () => {
const value = redact({ requests: [{ response: { data: { temporaryPassword: 'SECRET', nested: { accessToken: 'TOKEN', refreshToken: 'REFRESH' }, tenantId: '11' } } }] })
const json = JSON.stringify(value)
assert.ok(!json.includes('SECRET') && !json.includes('TOKEN') && !json.includes('REFRESH'))
assert.ok(json.includes('"tenantId":"11"'))
})
test('real HTTP multipart adapter preserves descriptor bytes, existing form fields and generated boundary', async () => {
const received = [], original = Uint8Array.from([137,80,78,71,13,10,26,10,0,255,10,13,0,200])
const server = createServer(async (request, response) => {
const chunks = []; for await (const chunk of request) chunks.push(chunk)
received.push({ headers: request.headers, body: Buffer.concat(chunks), method: request.method })
response.writeHead(200, { 'Content-Type': 'application/json' }); response.end('{"code":"200"}')
})
await new Promise(done => server.listen(0, '127.0.0.1', done))
try {
const base = `http://127.0.0.1:${server.address().port}`
const adapter = multipartAdapter((method, path, body, role, headers) => {
assert.equal(role, 'admin'); return fetch(base + path, requestInit(method, body, 'test-token', headers))
}, () => 'admin')
await adapter('POST', '/upload', { content: original, type: 'image/png', name: 'weight.png' }, 'token')
const form = new FormData(); form.append('expectedVersion', '4'); form.append('file', new Blob([original], { type: 'image/png' }), 'original.png')
await adapter('POST', '/upload', form, 'token', { 'Content-Type': 'application/json', 'Idempotency-Key': 'same-key' })
assert.equal(received.length, 2)
for (const request of received) {
assert.match(request.headers['content-type'], /^multipart\/form-data; boundary=/)
const boundary = request.headers['content-type'].split('boundary=')[1]
assert.ok(request.body.subarray(0, boundary.length + 2).equals(Buffer.from(`--${boundary}`)))
assert.ok(request.body.includes(Buffer.from(original)), 'The original byte array reaches HTTP without JSON conversion')
assert.ok(request.body.includes(Buffer.from('name="file"')))
assert.ok(!request.body.includes(Buffer.from('name="content"')))
assert.equal(request.method, 'POST')
}
assert.ok(received[1].body.includes(Buffer.from('name="expectedVersion"\r\n\r\n4')))
assert.equal(received[1].headers['idempotency-key'], 'same-key')
} finally { server.closeAllConnections(); await new Promise(done => server.close(done)) }
})
test('explicit resume proves the original failed sample and refuses state/identity drift before writes', () => {
const summary = { id: '2106706513799155714', tenantId: '2106706477225971713', orderId: '2106706495895306242',
orderNo: 'FL2106706495895306242', inboundCode: 'NYEXX2610040025', processStatus: 'WAIT_MEASURE', version: 4 }
const measurement = { measurementId: '2106706548955811842', measurementStatus: 'DRAFT', version: 4, weightResults: [] }
const readback = { verified: true, sessionClosed: true, inbound: { summary }, measurement }
const report = { runId: 'PARTIAL-ACTUAL-20261004-r1', success: false, cases: [], requests: [{ path: '/api/platform/v1/files/measurements/2106706477225971713/2106706513799155714', http: 200, code: 'S0500', requestId: 'original-id' }] }
const owner = validateResumeProof(report, readback)
validateResumeLive(owner, readback, readback.inbound, measurement)
assert.throws(() => validateResumeProof({ ...report, runId: 'unrelated' }, readback), /explicitly authorized/)
assert.throws(() => validateResumeLive(owner, readback, { summary: { ...summary, version: 5 } }, measurement), /changed/)
assert.throws(() => validateResumeLive(owner, readback, readback.inbound, { ...measurement, weightResults: [{ id: '1' }] }), /changed/)
assert.throws(() => validateResumeProof(report, { ...readback, measurement: { ...measurement, measurementStatus: 'APPROVED' } }), /Frozen/)
})
test('original draft resumes by upload/measurement only, with independent 3+1 SKU rows', async () => {
const owner = { tenantId: '11', inboundId: '12', orderId: '13', marker: 'ORIGINAL' }
const initial = { measurementId: '14', version: 5, latestDeclaration: { items: [
{ inboundDetailId: '21', itemNo: 1, cargoName: 'SKU1', packageCount: 3, lengthCm: 30, widthCm: 20, heightCm: 20 },
{ inboundDetailId: '22', itemNo: 2, cargoName: 'SKU2', packageCount: 1, lengthCm: 40, widthCm: 30, heightCm: 30 }] } }
const calls = []; let approved = false
const context = { token: 'own-token', createdResources: {}, request: async (method, path, body) => {
calls.push({ method, path, body }); let data
if (path.includes('/files/measurements/')) data = { fileId: '41' }
else if (path.includes('/measurement/draft?')) data = { measurementId: '14' }
else if (path.includes('/weight-pallet-options?')) data = { records: [{ palletId: '51', palletCode: 'YW-TP-002' }] }
else if (path.includes('/weight-results/61/confirm?')) data = { recognitionStatus: 'CONFIRMED' }
else if (path.includes('/weight-results?')) data = { id: '61', version: 0 }
else if (path.includes('/measurement/submit?')) data = { processStatus: 'MEASURE_PENDING' }
else if (path.includes('/measurements/approve?')) { approved = true; data = {} }
else if (path.includes('/measurement?')) data = { measurementId: '14', version: 6, measurementStatus: approved ? 'APPROVED' : 'DRAFT',
processStatus: approved ? 'WAIT_SPLIT' : 'WAIT_MEASURE', items: initial.latestDeclaration.items }
else if (path.startsWith('/api/platform/inbounds/')) data = { summary: { version: 7 } }
else throw Error('Unexpected original-recovery endpoint')
return { path, http: 200, body: { code: '200', data } }
} }
context.requestMultipart = multipartAdapter(context.request, () => 'admin')
await resumeMeasurementDraft(context, owner, initial)
assert.equal(context.createdResources.inboundMeasurement.inboundId, '12')
assert.equal(context.createdResources.inboundMeasurement.processStatus, 'WAIT_SPLIT')
assert.ok(calls[0].body instanceof FormData)
assert.deepEqual(calls.find(call => call.path.includes('/measurement/draft?')).body.items.map(item => [item.inboundDetailId, item.packageCount]), [['21',3],['22',1]])
assert.ok(!calls.some(call => /carton-scans|\/iam\/tenants|\/orders/.test(call.path)), 'No qualification, creation, order submission or re-scan')
})
test('zero-work checkpoint accepts own preassignment but rejects another operator or any physical pick', () => {
const { source, task } = fixture()
task.taskStatus = 'LOADING'; task.workflowStatus = 'IN_PROGRESS'; task.assignment = { assigneeId: '99' }
source.beforeTaskVersion = 0
validateZeroWorkTask(source, task, '99')
assert.throws(() => validateZeroWorkTask(source, task, '98'), /own PDA/)
task.items[0].pickedPackageCount = 1
assert.throws(() => validateZeroWorkTask(source, task, '99'), /physical work/)
})
test('prepared continuation binds the two original-source checkpoints and refuses to redo PDA writes', () => {
const report = { runId: 'PARTIAL-ACTUAL-20261004-recovery-r3', success: false, cases: [], requests: [],
recovery: { originalOwner: { inboundId: '2106706513799155714' }, originalSha256: 'frozen-original-hash' },
preparation: ['A','B'].map(kind => ({ kind, stage: 'PDA_UNASSIGNED', source: { sourceMarker: `PARTIAL-ACTUAL-20261004-recovery-r3-${kind}` }, spec: { kind } })) }
assert.equal(validatePreparedResume(report).length, 2)
assert.throws(() => validatePreparedResume({ ...report, requests: [{ method: 'POST', path: '/api/platform/v1/first-leg/pda-loading/tasks/91/batch' }] }), /repeat any PDA/)
assert.throws(() => validatePreparedResume({ ...report, cases: [{ completed: true }] }), /Only this/)
})
test('A recovery takes only the original command id and immutable version/operation/remark', () => {
const body = { expectedTaskVersion: 2, operationId: 'PARTIAL-ACTUAL-20261004-recovery-r3-A-complete', remark: 'PARTIAL-ACTUAL-20261004-recovery-r3-A' }
const report = { runId: 'PARTIAL-ACTUAL-20261004-recovery-r7', success: false, requests: [{ method: 'POST',
path: '/api/platform/v1/first-leg/pda-loading/tasks/2106713892758491138/complete', state: 'RECEIVED', http: 200, code: '200', request: body,
response: { data: { completionCommand: { id: '2106716718935052290', operationId: body.operationId, expectedTaskVersion: 2 } } } }] }
assert.deepEqual(originalCompletionCommand(report), { commandId: '2106716718935052290', body })
const changed = structuredClone(report); changed.requests[0].request.expectedTaskVersion = 4
assert.throws(() => originalCompletionCommand(changed), /immutable/)
const duplicated = structuredClone(report); duplicated.requests.push(duplicated.requests[0])
assert.throws(() => originalCompletionCommand(duplicated), /Exactly one/)
})
test('physical A facts exclude skipped rows and unchanged owners/stock detect a repeated completion or deduction', () => {
const sample = fixture('A')
sample.task.items = sample.task.items.map(row => ({ ...row, itemStatus: { PARTIAL: 'PARTIAL', PICK: 'PICKED', SKIP: 'SKIPPED' }[sample.expected.find(item => item.pickId === row.id).action],
pickedPackageCount: sample.expected.find(item => item.pickId === row.id).actual }))
verifyPhysicalFacts(sample.expected, sample.task)
const drift = structuredClone(sample.task); drift.items[0].pickedPackageCount++
assert.throws(() => verifyPhysicalFacts(sample.expected, drift), /drift/)
const before = { sources: [{ owner: { inboundId: '1' }, view: { summary: { id: '1', processStatus: 'LOADED', version: 12 }, statusHistory: [{ action: 'COMPLETE_LOADING' }], palletLocations: [] } }], inventory: { inbounds: [] } }
verifyOldOwnersUnchanged(before, structuredClone(before), sample.expected)
const repeated = structuredClone(before); repeated.sources[0].view.statusHistory.push({ action: 'COMPLETE_LOADING' })
assert.throws(() => verifyOldOwnersUnchanged(before, repeated, sample.expected), /must not repeat/)
const deducted = structuredClone(before); deducted.inventory.inbounds.push({ availablePackageCount: 1 })
assert.throws(() => verifyOldOwnersUnchanged(before, deducted, sample.expected), /deducted again/)
})
test('transit evidence uses raw balance identity and excludes transit from physical total, with no skipped source or duplicate ledger', () => {
const sample = fixture('A'); sample.source.containerNo = 'TEST-CONTAINER'
for (const row of sample.expected) { row.frozen.inboundCode = `IN-${row.inboundId}`; row.frozen.skuCode = `SKU-${row.itemId}` }
const snapshot = ['1','3','4'].map(inboundId => {
const active = sample.expected.filter(row => row.inboundId === inboundId && row.actual > 0), ownerRow = sample.expected.find(row => row.inboundId === inboundId)
const owner = { inboundId, tenantId: ownerRow.accountId }, count = active.reduce((sum,row) => sum + row.actual, 0)
if (!count) return { owner, orders: [], balances: [], ledger: [], details: [] }
const order = { id: `100${inboundId}`, orderNo: `LL-${inboundId}`, customerId: owner.tenantId, sourceInboundId: inboundId,
sourceContainerBookingId: sample.source.containerBookingId, containerNo: sample.source.containerNo, packageCount: count }
return { owner, orders: [order], details: [{ cargo: active.map(row => ({ skuCode: row.frozen.skuCode, packageCount: row.actual })),
statusHistory: [{ toStatus: 'PENDING_ASSIGNMENT', remark: '头程装柜完成自动生成' }] }],
balances: [{ customerId: owner.tenantId, inboundCode: `IN-${inboundId}`, inboundOrderId: order.id, inTransitPackages: count,
totalPackages: 0, availablePackages: 0, holdPackages: 0, allocatedPackages: 0, pickedPackages: 0, receivingPackages: 0, procurementPackages: 0 }],
ledger: [{ customerId: owner.tenantId, inboundCode: `IN-${inboundId}`, inboundOrderId: order.id, businessOrderNo: order.orderNo,
beforePackages: 0, deltaPackages: count, afterPackages: count }] }
})
assert.equal(verifyDownstream(sample, snapshot).actualPackages, 3)
const doubled = structuredClone(snapshot); doubled[0].ledger.push(doubled[0].ledger[0])
assert.throws(() => verifyDownstream(sample, doubled), /inventory movement/)
const grouped = structuredClone(snapshot); grouped[0].balances[0].inboundOrderId = null
assert.throws(() => verifyDownstream(sample, grouped), /transit inventory/)
const skipped = structuredClone(snapshot); skipped[2].orders.push(snapshot[0].orders[0])
assert.throws(() => verifyDownstream(sample, skipped), /Skipped owner/)
})
test('manifest needs two ordered cases, unique actions and all five photos', () => {
const manifest = { schemaVersion: 1, runId, environment: 'TEST', cases: [fixture('A').spec, fixture('B').spec] }
assert.equal(validateManifest(manifest, runId), manifest)
const duplicate = structuredClone(manifest); duplicate.cases[1].actions[1].itemId = '1'
assert.throws(() => validateManifest(duplicate, runId), /unique/)
const missing = structuredClone(manifest); delete missing.cases[0].evidence.SEAL
assert.throws(() => validateManifest(missing, runId), /Five/)
})
test('official source must be fresh, passing, unclaimed and run-bound', () => {
const now = Date.parse('2026-10-04T12:00:00Z'), report = { success: true, fatal: null, summary: { failed: 0 }, gateway: 'https://test.invalid',
createdResources: { manualPdaBatchFixture: { ...fixture().source, schemaVersion: 1, stage: 'MANUAL_PDA_CLAIM_AND_BATCH_READY',
preparedAt: new Date(now).toISOString(), taskItems: fixture().task.items } } }
assert.ok(fixtureSource(report, runId, report.gateway, now))
assert.throws(() => fixtureSource(report, runId, report.gateway, now + 25 * 3600_000), /within/)
const old = structuredClone(report); old.createdResources.manualPdaBatchFixture.sourceMarker = 'old'
assert.throws(() => fixtureSource(old, runId, report.gateway, now), /run-bound/)
const failed = structuredClone(report); failed.summary.failed = 1
assert.throws(() => fixtureSource(failed, runId, report.gateway, now), /did not pass/)
})
test('exact HALF_UP handles non-half ratios, ties, nullable metrics and invalid ratios', () => {
assert.equal(scale('10.001', 2, 3, 3), '6.667')
assert.equal(scale('8.002', 2, 3, 3), '5.335')
assert.equal(scale('0.1001', 2, 3, 4), '0.0667')
assert.equal(scale('1', 1, 2, 0), '1')
assert.equal(scale(null, 1, 2, 3), null)
assert.throws(() => scale('10', 4, 3, 3))
})
test('A and B require the exact actual/skip shapes before any mutation', () => {
assert.equal(fixture('A').expected.filter(row => row.actual > 0).length, 2)
assert.ok(fixture('B').expected.filter(row => row.actual > 0).every(row => row.action === 'PARTIAL'))
const { spec, task, preload } = fixture('B')
spec.actions[0] = { itemId: '1', action: 'PICK' }
assert.throws(() => expectedRows(spec, task, preload), /exactly one/)
spec.actions[0] = { itemId: '1', action: 'PARTIAL', finalPickedPackageCount: 2 }
assert.throws(() => expectedRows(spec, task, preload), /less than/)
})
test('duplicate, missing, already picked and wrong-customer source rows reject preflight', () => {
const a = fixture('A'); a.spec.actions.pop()
assert.throws(() => expectedRows(a.spec, a.task, a.preload), /cover every/)
const b = fixture(); b.task.items[0].pickedPackageCount = 1
assert.throws(() => expectedRows(b.spec, b.task, b.preload), /zero-picked/)
const c = fixture(); c.preload.items[0].accountId = '999'
assert.throws(() => expectedRows(c.spec, c.task, c.preload), /identity/)
})
test('actual scope, metrics and refund customer/coverage are independently checked', () => {
for (const kind of ['A', 'B']) {
const sample = fixture(kind)
assert.equal(verifyActual(sample.expected, sample.detail, sample.customs).actualRows, kind === 'A' ? 2 : 1)
const contaminated = structuredClone(sample.detail); contaminated.items.push(sample.preload.items.at(-1))
assert.throws(() => verifyActual(sample.expected, contaminated, sample.customs), /Actual loading row set/)
const stale = structuredClone(sample.customs); stale.actualBoxCount++
assert.throws(() => verifyActual(sample.expected, sample.detail, stale), /Customs actual boxes/)
const wrong = structuredClone(sample.customs); wrong.inbounds[0].accountId = '999'
assert.throws(() => verifyActual(sample.expected, sample.detail, wrong), /Customs source/)
}
})
function transport(sample, { drift = false, failCompletion = false, incomplete = false } = {}) {
const calls = [], task = structuredClone(sample.task), customs = structuredClone(sample.customs)
const history = [], pda = `/api/platform/v1/first-leg/pda-loading/tasks/${sample.source.pdaTaskId}`
let completes = 0
return { calls, request: async (method, path, body, role, headers) => {
calls.push({ method, path, body: body instanceof FormData ? 'multipart' : structuredClone(body), role, headers })
let data
if (method === 'POST' && path === `${pda}/claim`) { task.workflowStatus = 'IN_PROGRESS'; data = task }
else if (method === 'POST' && /\/items\/\d+\/skip$/.test(path)) {
const itemId = path.split('/').at(-2)
task.items = task.items.map(row => row.id === itemId ? { ...row, itemStatus: 'SKIPPED', pickedPackageCount: 0 } : row)
data = task
}
else if (method === 'POST' && path === `${pda}/batch`) {
task.workflowStatus = 'AWAITING_REVIEW'; task.items = task.items.map(row => {
const expected = sample.expected.find(item => item.pickId === row.id)
return { ...row, itemStatus: { PICK: 'PICKED', PARTIAL: 'PARTIAL', SKIP: 'SKIPPED' }[expected.action], pickedPackageCount: expected.actual }
}); data = task
} else if (method === 'POST' && path === `${pda}/evidence`) data = {}
else if (path.startsWith(`${pda}/evidence?`)) data = { evidences: Object.keys(evidence).map(type => ({ evidenceType: type, status: 'ACTIVE', sealNo: sample.spec.sealNo })) }
else if (method === 'POST' && path === `${pda}/complete`) {
completes++
if (failCompletion) throw Error('Response lost')
task.taskStatus = incomplete ? 'COMPLETING' : 'COMPLETED'; task.workflowStatus = incomplete ? 'COMPLETING' : 'COMPLETED'
if (completes === 1) history.push({ actionType: 'COMPLETE_LOADING' })
if (drift && completes > 1) task.version++
data = task
} else if (path.startsWith(`${pda}/inventory-snapshot?`)) data = { snapshotToken: 'fresh-snapshot' }
else if (path.startsWith(`${pda}?`)) data = task
else if (path === `${pda}/operations`) data = history
else if (path.includes('/loading-lists/')) data = sample.detail
else if (path.endsWith('/send-to-broker')) return { path, http: 200, body: { code: 'A0409' } }
else if (path.endsWith('/start')) { customs.status = 'DECLARING'; customs.version++; data = customs }
else if (path.endsWith('/complete-all-buy')) { customs.status = 'COMPLETED'; customs.version++; data = customs }
else if (path.includes('/customs/loading-plans/')) data = customs
else throw Error(`Unexpected ${method} ${path}`)
return { path, http: 200, body: { code: '200', data: structuredClone(data) } }
} }
}
test('both cases execute actual completion; B is all PARTIAL and follows all-buy gates', async () => {
for (const kind of ['A', 'B']) {
const sample = fixture(kind), http = transport(sample), images = Object.fromEntries(Object.keys(evidence).map(type => [type, { bytes: new Uint8Array([1,2,3]), type: 'image/png' }]))
const result = await runCase(sample.spec, sample.source, { ...sample, images }, http.request, async () => {})
assert.equal(result.completed, true); assert.equal(result.allActualRowsPartial, kind === 'B')
const completes = http.calls.filter(call => call.path.endsWith('/complete'))
assert.equal(completes.length, 2); assert.deepEqual(completes[0].body, completes[1].body)
const batches = http.calls.filter(call => call.path.endsWith('/batch'))
assert.equal(batches.length, 1)
if (kind === 'A') {
const skipIndex = http.calls.findIndex(call => /\/items\/\d+\/skip$/.test(call.path))
assert.ok(skipIndex >= 0 && skipIndex < http.calls.findIndex(call => call.path.endsWith('/batch')))
assert.ok(!batches[0].body.items.some(item => item.itemId === '2'), 'Resolved same-owner SKIP does not repeat in full pending batch')
}
if (kind === 'B') assert.ok(http.calls.some(call => call.path.endsWith('/complete-all-buy')))
}
})
test('replay version/history drift is a hard failure', async () => {
const sample = fixture(), http = transport(sample, { drift: true }), images = Object.fromEntries(Object.keys(evidence).map(type => [type, { bytes: new Uint8Array([1]), type: 'image/png' }]))
await assert.rejects(runCase(sample.spec, sample.source, { ...sample, images }, http.request), /zero projection/)
})
test('unknown completion result is never retried and no later customs write is attempted', async () => {
const sample = fixture(), http = transport(sample, { failCompletion: true }), images = Object.fromEntries(Object.keys(evidence).map(type => [type, { bytes: new Uint8Array([1]), type: 'image/png' }]))
await assert.rejects(runCase(sample.spec, sample.source, { ...sample, images }, http.request), /Response lost/)
assert.equal(http.calls.filter(call => call.path.endsWith('/complete')).length, 1)
assert.equal(http.calls.filter(call => call.method === 'POST' && call.path.includes('/customs/')).length, 0)
})
test('accepted completion without terminal state fails rather than claiming success', async () => {
const sample = fixture(), http = transport(sample, { incomplete: true }), images = Object.fromEntries(Object.keys(evidence).map(type => [type, { bytes: new Uint8Array([1]), type: 'image/png' }]))
await assert.rejects(runCase(sample.spec, sample.source, { ...sample, images }, http.request, async () => {}), /polling exhausted/)
})