test: extend eight-business acceptance flows and offline contracts
This commit is contained in:
@@ -4,8 +4,8 @@ import { readFileSync } from 'node:fs'
|
||||
import { mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, resolve, basename } from 'node:path'
|
||||
import { DISPATCH_BASE as BASE, dispatchEvidenceContext } from './grouped-dispatch-acceptance.mjs'
|
||||
import { run, selectDispatchRouteFixture, dispatchRouteMatches, dispatchBusinessSnapshot, verifyDispatchRouteLifecycle } from './phase4-overseas-dispatch.mjs'
|
||||
import { DISPATCH_BASE as BASE, dispatchEvidenceContext, dispatchIo } from './grouped-dispatch-acceptance.mjs'
|
||||
import { run, createClientRelabelDispatch, selectDispatchRouteFixture, dispatchRouteMatches, dispatchBusinessSnapshot, verifyDispatchRouteLifecycle } from './phase4-overseas-dispatch.mjs'
|
||||
|
||||
const clone = value => structuredClone(value)
|
||||
const rows = () => [1, 2, 3].map(n => ({ selection: { sourceType: 'INBOUND_PLAN', planPalletId: `2${n}`, returnReceiptId: null, relabelNo: null, warehousePalletId: `3${n}`, sourceVersion: `${n}`.repeat(64) },
|
||||
@@ -46,7 +46,8 @@ function server(fault = '', initial = false) {
|
||||
if (method === 'POST' && path === `${BASE}/drafts`) { const d = { draftToken: `SERVER-${++serial}`, recordVersion: 0, warehouseId: body.warehouseId, status: 'ACTIVE', sources: [], containers: [] }; drafts.set(d.draftToken, d); return remember(d) }
|
||||
if (method === 'GET' && path === `${BASE}/drafts`) return response([...drafts.values()].filter(d => d.status === 'ACTIVE'))
|
||||
if (path === `${BASE}/candidates`) {
|
||||
assert.equal(params.get('warehouseId'), '44'); assert.equal(params.get('sourceStage'), 'RECEIVED')
|
||||
assert.equal(params.get('warehouseId'), '44')
|
||||
assert.equal(params.get('sourceStage'), params.get('orderNo') === 'RL-CLIENT' ? 'RELABEL' : 'RECEIVED')
|
||||
const claimed = new Set([...orders.values()].flatMap(o => o.items.map(i => i.sourceKey)))
|
||||
for (const d of drafts.values()) if (d.status === 'ACTIVE' && params.get('draftToken') && d.draftToken !== params.get('draftToken')) for (const c of d.sources) claimed.add(c.sourceKey)
|
||||
return response({ candidates: candidates.filter(c => !claimed.has(c.sourceKey)), complete: true, snapshotAt: '2026-10-19' })
|
||||
@@ -97,6 +98,37 @@ function server(fault = '', initial = false) {
|
||||
return { context, candidates, calls, checks, orders, bundle: () => { const o = orders.get('DS-1'); return { data: view(o), warehouseId: '44', items: { items: clone(o.items) }, route: { stops: stops(o) } } } }
|
||||
}
|
||||
|
||||
function addClientRelabel(h) {
|
||||
const candidate = clone(rows()[0])
|
||||
Object.assign(candidate, { sourceKey: 'RELABEL:RL-CLIENT:91', availablePackages: 1 })
|
||||
candidate.selection = { sourceType: 'RELABEL', relabelNo: 'RL-CLIENT', warehousePalletId: '91', sourceVersion: candidate.sourceVersion }
|
||||
h.candidates.push(candidate)
|
||||
h.context.createdResources.phase4OverseasClientServices = { relabelFulfillment: { marker: 'CURRENT', relabelNo: 'RL-CLIENT',
|
||||
status: 'READY_FOR_OUTBOUND', execution: { status: 'HTTP_VERIFIED_READY_FOR_DISPATCH' }, customerId: '55', warehouseId: '44', targetPalletIds: ['91'] } }
|
||||
}
|
||||
|
||||
test('client original relabel gets its own exact dispatch and refuses repeat execution', async () => {
|
||||
const h = server(); addClientRelabel(h)
|
||||
const proof = {}, io = dispatchIo(h.context, 'test', proof, 'CLIENT-RELABEL')
|
||||
await createClientRelabelDispatch(h.context, io, proof)
|
||||
assert.equal(proof.clientRelabel.status, 'CREATED_BEFORE_LOADING')
|
||||
assert.equal(h.context.createdResources.phase4OverseasClientServices.relabelFulfillment.dispatchNo, 'DS-1')
|
||||
assert.deepEqual(proof.clientRelabel.sourceKeys, ['RELABEL:RL-CLIENT:91'])
|
||||
const writes = h.calls.filter(c => c.method === 'POST').length
|
||||
await assert.rejects(createClientRelabelDispatch(h.context, io, proof))
|
||||
assert.equal(h.calls.filter(c => c.method === 'POST').length, writes)
|
||||
})
|
||||
|
||||
for (const fault of ['customer', 'pallet', 'missing']) test(`client relabel excludes ${fault} candidates before writes`, async () => {
|
||||
const h = server(); addClientRelabel(h)
|
||||
if (fault === 'customer') h.candidates.at(-1).customerId = '99'
|
||||
if (fault === 'pallet') h.candidates.at(-1).selection.warehousePalletId = '99'
|
||||
if (fault === 'missing') h.candidates.pop()
|
||||
const proof = {}, io = dispatchIo(h.context, 'test', proof, 'CLIENT-RELABEL')
|
||||
await assert.rejects(createClientRelabelDispatch(h.context, io, proof))
|
||||
assert.equal(h.calls.some(c => c.method === 'POST'), false)
|
||||
})
|
||||
|
||||
test('native route fixture uses real physical pallets and requires two destinations plus a repeated destination', () => {
|
||||
const c = rows(), selected = selectDispatchRouteFixture(c)
|
||||
assert.deepEqual(selected.selected.map(c => c.sourceKey), ['PLAN:21', 'PLAN:22']); assert.equal(selected.duplicate.sourceKey, 'PLAN:23')
|
||||
@@ -139,7 +171,7 @@ test('phase4 missing current marker or real multi-destination fixture is explici
|
||||
test('complete offline phase4 hands off the actual native Detail and current source keys at BEFORE_LOADING', async () => {
|
||||
const directory = await mkdtemp(join(tmpdir(), 'phase4-dispatch-contract-'))
|
||||
try {
|
||||
const h = server(); delete h.context.checkpoint; h.context.options = { output: join(directory, 'report.json') }
|
||||
const h = server(); addClientRelabel(h); delete h.context.checkpoint; h.context.options = { output: join(directory, 'report.json') }
|
||||
h.context.createdResources.dispatchWriteBoundary = { marker: 'CURRENT', stopped: false, password: 'PRIVATE_PASSWORD', token: 'PRIVATE_LOGIN_TOKEN' }
|
||||
const originalRequest = h.context.request
|
||||
h.context.request = async (method, path, body) => {
|
||||
|
||||
Reference in New Issue
Block a user