test: extend eight-business acceptance flows and offline contracts

This commit is contained in:
2026-10-22 15:41:50 +08:00
parent 86889d7e0e
commit 080ca600e6
33 changed files with 1285 additions and 101 deletions
@@ -48,3 +48,95 @@ test('failure to persist the request prevents network dispatch', async () => {
await assert.rejects(verifyProcurementSample(context, inputs), /disk write failed/)
assert.equal(events.length, 0)
})
function completeFixture(fault = '') {
const { context, inputs } = fixture()
const calls = [], seen = new Map()
let spent = 0, frozen = 0, creations = 0
const order = { orderNo: 'CG-TEST-1', customerId: '101', status: 'PENDING_REVIEW', version: 0, updatedAt: '2026-10-22T12:00:00' }
const fees = [], physical = { receipts: [], cartons: [], packingVersion: 0 }
const transport = { overseasLabels: { version: 0 }, domesticLabels: { version: 0 }, shipments: [] }
const detail = () => structuredClone({ customerView: { order, fees }, physical, transport, fees })
inputs.upload = async () => ({ fileId: '501' })
inputs.readWallet = async () => ({ actualBalance: ((1000 - spent) / 100).toFixed(2), availableBalance: ((1000 - spent - frozen) / 100).toFixed(2),
frozenBalance: (frozen / 100).toFixed(2), creditUsed: '0.00' })
context.request = async (method, path, body) => {
calls.push({ method, path, body: structuredClone(body) })
const reply = data => ({ http: 200, body: { code: '200', data: structuredClone(data) }, requestId: 'req-test', traceId: 'trace-test' })
if (method === 'GET') {
if (path.startsWith('/api/merchant/')) return reply({ order: { ...order, ...(fault === 'customer final order' ? { orderNo: 'OTHER' } : {}) } })
const result = detail()
if (fault === 'aggregate customer') result.customerView.order.customerId = '999'
if (fault === 'aggregate order') result.customerView.order.orderNo = 'OTHER'
if (fault === 'aggregate version') result.customerView.order.version = null
return reply(result)
}
if (path === '/api/merchant/v1/last-leg/overseas-procurements') {
creations++
return reply({ order: { ...order, ...(creations === 2 && fault === 'create replay customer' ? { customerId: '999' } : {}) } })
}
assert.equal(method, 'POST')
const action = path.split('/CG-TEST-1/')[1]?.split('?')[0]
assert.ok(action, path)
if (seen.has(body.idempotencyKey)) {
assert.deepEqual(body, seen.get(body.idempotencyKey))
if (fault === 'replay deducts' && action === 'purchase') spent += 100
return reply(detail())
}
assert.equal(body.version, order.version)
seen.set(body.idempotencyKey, structuredClone(body))
switch (action) {
case 'review': order.status = 'APPROVED'; break
case 'purchase':
spent += 100
fees.push({ active: true, purpose: 'SAMPLE_PAYMENT', walletStatus: 'SETTLED', sourceLinked: true, walletBusinessNo: 'PAY-1' })
break
case 'overseas-receipts': case 'domestic-receipts':
physical.receipts.push({ id: String(601 + physical.receipts.length), leg: body.leg, active: true, goodQuantity: body.goodQuantity }); break
case 'overseas-receipts/complete': case 'domestic-receipts/complete': break
case 'packing':
physical.cartons = body.cartons.map((row, index) => ({ ...row, id: String(701 + index) })); physical.packingVersion++; break
case 'packing/confirm': assert.equal(body.packingVersion, physical.packingVersion); break
case 'labels': transport[body.leg === 'OVERSEAS' ? 'overseasLabels' : 'domesticLabels'].version++; break
case 'international-estimate':
frozen = 200; fees.push({ active: true, purpose: 'INTERNATIONAL_ESTIMATE', walletStatus: 'FROZEN' }); break
case 'labels/apply': break
case 'overseas-shipment': case 'customer-shipment':
transport.shipments.push({ leg: body.leg })
if (action === 'customer-shipment') order.status = 'SHIPPED_TO_CUSTOMER'
break
case 'charges/confirm':
spent += 150; frozen = 0
fees.find(row => row.purpose === 'INTERNATIONAL_ESTIMATE').walletStatus = 'RELEASED'
fees.push({ active: true, purpose: 'INTERNATIONAL_FREIGHT', walletStatus: 'SETTLED', sourceLinked: true, walletBusinessNo: 'PAY-2' })
break
default: assert.fail(`Unexpected operation ${action}`)
}
order.version++
return reply(detail())
}
return { context, inputs, calls, seen, physical, transport }
}
test('sample procurement traverses receipt, packing, both shipments and exact wallet settlement with same-key replays', async () => {
const h = completeFixture()
const proof = await verifyProcurementSample(h.context, h.inputs)
assert.equal(proof.status, 'PASSED')
assert.equal(proof.settledAmountCny, '2.50')
assert.deepEqual(proof.walletBusinessNos, ['PAY-1', 'PAY-2'])
assert.equal(h.physical.receipts.length, 4)
assert.equal(h.physical.cartons.length, 2)
assert.deepEqual(h.transport.shipments.map(row => row.leg), ['OVERSEAS', 'DOMESTIC'])
assert.equal(proof.walletChecks.length, 4)
assert.ok(proof.checks.every(check => check.passed))
})
for (const fault of ['aggregate customer', 'aggregate order', 'aggregate version', 'create replay customer', 'customer final order', 'replay deducts']) {
test(`procurement stops rather than passing ${fault}`, async () => {
const h = completeFixture(fault)
await assert.rejects(verifyProcurementSample(h.context, h.inputs))
assert.equal(h.context.createdResources.procurementSample.status, 'FAILED')
if (fault.startsWith('aggregate') || fault === 'create replay customer') assert.equal(h.seen.size, 0)
if (fault === 'replay deducts') assert.equal(h.physical.receipts.length, 0)
})
}