66 lines
3.6 KiB
JavaScript
66 lines
3.6 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import { finishOrderLifecycle } from './orders.mjs'
|
|
|
|
const input = { tenantId: '2105193679178272769', orderId: '2105193704449441794', firstLegDetailPath: '/original-order-detail' }
|
|
function fixture(option, status, transitions) {
|
|
const calls = []; const checks = []
|
|
const resources = { orders: { tenantId: input.tenantId, orderId: input.orderId,
|
|
inboundId: '2105193705748041729', inboundCode: 'TEST-APPROVED', skuLines: [{ skuNo: 'SKU-A' }] },
|
|
ocrInbound: { tenantId: input.tenantId, inboundId: '2105193705748041729' } }
|
|
const context = { context: { orderStopAtApproved: option }, createdResources: resources,
|
|
async request(method, path) { calls.push({ method, path });
|
|
return { http: 200, body: { code: '200', data: method === 'GET'
|
|
? { order: { status }, statusHistory: transitions.map(toStatus => ({ toStatus })) } : {} } } },
|
|
apiSuccess(_module, _step, response) { assert.equal(response.body.code, '200'); return response.body.data },
|
|
assert(_module, step, _response, valid) { checks.push({ step, valid: Boolean(valid) }) },
|
|
}
|
|
return { context, calls, checks, resources }
|
|
}
|
|
|
|
test('explicit approved stop checks raw Order and never sends accept or start; measurement resources survive', async () => {
|
|
const f = fixture(true, 'APPROVED', ['DRAFT', 'PENDING_REVIEW', 'APPROVED'])
|
|
const before = JSON.stringify(f.resources)
|
|
await finishOrderLifecycle(f.context, input)
|
|
assert.deepEqual(f.calls, [{ method: 'GET', path: input.firstLegDetailPath }])
|
|
assert.ok(f.checks.every(check => check.valid))
|
|
assert.equal(f.resources.orders.rawOrderStatus, 'APPROVED')
|
|
assert.equal(f.resources.orders.stoppedAtApproved, true)
|
|
delete f.resources.orders.rawOrderStatus; delete f.resources.orders.stoppedAtApproved
|
|
assert.equal(JSON.stringify(f.resources), before)
|
|
})
|
|
|
|
for (const option of [undefined, false, 'true']) {
|
|
test(`only boolean true opts out of default accept/start: ${String(option)}`, async () => {
|
|
const f = fixture(option, 'PROCESSING', ['APPROVED', 'ACCEPTED', 'PROCESSING'])
|
|
await finishOrderLifecycle(f.context, input)
|
|
assert.equal(f.calls.length, 3)
|
|
assert.ok(f.calls[0].path.includes('/accept?'))
|
|
assert.ok(f.calls[1].path.includes('/start?'))
|
|
assert.deepEqual(f.calls.map(call => call.method), ['POST', 'POST', 'GET'])
|
|
assert.ok(f.checks.every(check => check.valid))
|
|
assert.equal(f.resources.orders.rawOrderStatus, 'PROCESSING')
|
|
})
|
|
}
|
|
|
|
for (const [status, history] of [['PROCESSING', ['APPROVED', 'ACCEPTED', 'PROCESSING']],
|
|
['APPROVED', ['APPROVED', 'ACCEPTED']], ['APPROVED', ['PENDING_REVIEW']]]) {
|
|
test(`approved stop rejects invalid raw status/history: ${status}/${history.join(',')}`, async () => {
|
|
const f = fixture(true, status, history)
|
|
await assert.rejects(finishOrderLifecycle(f.context, input), /原始APPROVED/)
|
|
assert.equal(f.calls.length, 1)
|
|
assert.ok(f.checks.some(check => !check.valid))
|
|
})
|
|
}
|
|
|
|
test('approved stop runs after full approval replay, inbound readiness and shared measurement handoff', () => {
|
|
const source = readFileSync(new URL('./orders.mjs', import.meta.url), 'utf8')
|
|
const run = source.slice(source.indexOf('export async function run('))
|
|
const finish = run.indexOf('await finishOrderLifecycle(')
|
|
for (const fragment of ['await verifyOrderApprovalReplay(', '隔离入库单 marker 来源与可测量状态正确',
|
|
'context.createdResources.orders.inboundId = inboundId', 'context.context.createdInbound =', '履约任务与本轮订单和客户一致']) {
|
|
assert.ok(run.indexOf(fragment) >= 0 && run.indexOf(fragment) < finish, fragment)
|
|
}
|
|
})
|