test: extend eight-business acceptance flows and offline contracts
This commit is contained in:
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user