feat(dispatch): unify whole vehicle scheduling, source ownership and staged execution
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { parsePoolArguments, poolPlan, runPoolAcceptance, assertPoolRemaining, assertSingleCommercial,
|
||||
shrinkingVehicleRequest, expiryWaitMs, poolLocalDateTime, poolPickScope, pickPoolDispatch, POOL_COVERAGE, POOL_REMAINING,
|
||||
poolInquiryWrite, poolReadIsDenied, assertPoolBoundCapacity, verifyPoolConfirmedCancellation, verifyPoolBindingRevalidation,
|
||||
@@ -122,7 +123,7 @@ test('PDA namespace belongs to the exact run, customer and dispatch while parent
|
||||
assert.throws(() => poolPickScope(context, { dispatchNo: 'DS-A' }), /PICK_SCOPE_REQUIRED/)
|
||||
})
|
||||
|
||||
test('real pickAll helper handles two same-customer dispatches with separate pick/complete keys and exact replays', async () => {
|
||||
test('real native pickAll preserves separate command namespaces, version zero, exact replays and stale rejection', async () => {
|
||||
const operations = new Map(), requests = [], steps = [], picked = new Map()
|
||||
const dispatches = ['DS-A', 'DS-B']
|
||||
const context = {
|
||||
@@ -134,26 +135,34 @@ test('real pickAll helper handles two same-customer dispatches with separate pic
|
||||
const dispatchNo = path.split('/dispatches/')[1].split('/')[0], index = dispatches.indexOf(dispatchNo)
|
||||
assert.ok(index >= 0)
|
||||
const response = data => ({ http: 200, body: { code: '200', data }, requestId: 'test', traceId: 'test' })
|
||||
if (method === 'GET') return response({ items: [{ dispatchItemId: `${index + 1}`, scheduledPackages: 1,
|
||||
pickedPackages: picked.get(dispatchNo) || 0, positions: [{ id: `${index + 100}`, availablePackages: 1 }] }] })
|
||||
const version = picked.get(dispatchNo) || 0
|
||||
if (method === 'GET') return response({ dispatchNo, status: 'ACTIVE', stage: 'LOADING', recordVersion: index === 0 ? version : String(version),
|
||||
pickingReconciled: version === 1, counts: { packageCount: 1, pickedPackageCount: version },
|
||||
items: [{ dispatchItemId: `${index + 1}`, scheduledPackages: 1, pickedPackages: version,
|
||||
positions: [{ positionId: `${index + 100}`, availablePackages: 1 }] }] })
|
||||
requests.push({ method, path, body: structuredClone(body) })
|
||||
assert.ok(path.endsWith('/pick'))
|
||||
assert.equal(body.recordVersion, index === 0 ? 0 : '0')
|
||||
const prior = operations.get(body.idempotencyKey), payload = JSON.stringify([path, body])
|
||||
if (prior) {
|
||||
if (prior.payload !== payload) return { http: 200, body: { code: 'A0409', message: 'Idempotency key was reused with a different request' } }
|
||||
assert.equal(prior.payload, payload)
|
||||
return response({ ...prior.result, replayed: true })
|
||||
}
|
||||
let result
|
||||
if (path.endsWith('/pick')) { picked.set(dispatchNo, 1); result = { pickedPackages: 1 } }
|
||||
else result = { status: 'READY_TO_DEPART', plannedPackages: 1, pickedPackages: 1, remainingPackages: 0, replayed: false }
|
||||
if (version !== Number(body.recordVersion)) return { http: 409, body: { code: 'A0409' } }
|
||||
picked.set(dispatchNo, 1)
|
||||
const result = { recordVersion: '1', result: { pickedPackages: 1 }, counts: { packageCount: 1, pickedPackageCount: 1 }, replayed: false }
|
||||
operations.set(body.idempotencyKey, { payload, result })
|
||||
return response(result)
|
||||
},
|
||||
}
|
||||
for (const dispatchNo of dispatches) await pickPoolDispatch(context, 'test-pda', { customerId: '9', dispatchNo })
|
||||
assert.equal(operations.size, 4)
|
||||
assert.equal(requests.length, 8)
|
||||
assert.equal(operations.size, 2)
|
||||
assert.equal(requests.length, 6)
|
||||
assert.ok(steps.every(Boolean))
|
||||
for (let index = 0; index < requests.length; index += 2) assert.deepEqual(requests[index], requests[index + 1])
|
||||
for (let index = 0; index < requests.length; index += 3) {
|
||||
assert.deepEqual(requests[index], requests[index + 1])
|
||||
assert.notEqual(requests[index].body.idempotencyKey, requests[index + 2].body.idempotencyKey)
|
||||
}
|
||||
})
|
||||
|
||||
test('failed helper result stops the pool stage immediately instead of appending a misleading generic failure', async () => {
|
||||
@@ -277,3 +286,31 @@ test('confirmed cancellation verifies both commercial types and reuses exact suc
|
||||
assert.equal(result.remainingPalletSpaces, 12)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
test('pool orchestration keeps native one-main boundaries, explicit reviews and real loading before whole arrangement', () => {
|
||||
const source = readFileSync(new URL('./fleet-capacity-pool-test-env.mjs', import.meta.url), 'utf8')
|
||||
for (const old of ['controlledPalletMeasurements(', '/external-freight', '/loading-review`', "departed.status === 'IN_TRANSIT'", 'sourceStage:', "'/dispatches/pre-arrange'"]) assert.equal(source.includes(old), false, old)
|
||||
assert.match(source, /testPlannedCapacity\(plan\.cargo, detail\.trip\.stops\)/)
|
||||
assert.match(source, /actual: false/)
|
||||
assert.match(source, /arranged\.booking\.dispatchNo === fixture\.dispatchNo/)
|
||||
assert.match(source, /\['submit-review', 'dispatch-review', 'loading-review'\]/)
|
||||
const pick = source.indexOf('const namespace = await pickPoolDispatch('), load = source.indexOf('await confirmFleetLoading('), arrange = source.indexOf('await arrangeDispatchDelivery('), depart = source.indexOf("const departed = await request('POST', departPath")
|
||||
assert.ok(pick > 0 && pick < load && load < arrange && arrange < depart)
|
||||
assert.match(source, /fixture\.loadingMeasurements/)
|
||||
assert.match(source, /recordVersion: current\.recordVersion/)
|
||||
assert.match(source, /readyTask\.actions\?\.DEPART\?\.allowed === true/)
|
||||
assert.match(source, /JSON\.stringify\(actual\.fees\) === JSON\.stringify\(signed\.fees\)/)
|
||||
assert.match(source, /sameCustomerOrders\.every\(source => actual\.operations\.filter/)
|
||||
})
|
||||
|
||||
test('pool execution explicitly selects capacity verification before handing off the shared runner', async () => {
|
||||
const config = parsePoolArguments(['--execute', '--environment', 'TEST', '--run-id', 'pool-scope', '--warehouse-id', '10', '--destination-warehouse-id', '11'])
|
||||
let selected
|
||||
await assert.rejects(runPoolAcceptance(config, { env: {}, runner: async (actual, options) => {
|
||||
assert.equal(actual, config)
|
||||
selected = options.verificationScope
|
||||
throw Error('STOP_BEFORE_ENVIRONMENT_ACCESS')
|
||||
} }), /STOP_BEFORE_ENVIRONMENT_ACCESS/)
|
||||
assert.equal(selected, 'CAPACITY_POOL')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user