test: correct container loading acceptance order
This commit is contained in:
@@ -374,7 +374,7 @@ export async function run(context) {
|
||||
|
||||
const transportResponse = await request(context, 'PATCH', `${CONTAINERS}/${containerId}/transport`, {
|
||||
containerNo: `TST-${suffix}`.slice(0, 64), voyageNo: `V-${suffix}`.slice(0, 64),
|
||||
version: Number(containerDetail?.version ?? 0), reason: context.marker,
|
||||
version: Number(linkedContainer.version), reason: context.marker,
|
||||
})
|
||||
const transported = context.apiSuccess(MODULE, '在放舱前绑定本轮真实箱号', transportResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '绑定后柜次保留本轮箱号', transportResponse,
|
||||
|
||||
@@ -75,6 +75,7 @@ test('existing container number replacement keeps container plan and PDA identit
|
||||
|
||||
test('PDA task opens only after preload push, real container number and ocean release converge', () => {
|
||||
assert.match(containerLoadingAcceptance, /无真实柜号时计划可排柜但 PDA 任务不开放/)
|
||||
assert.match(containerLoadingAcceptance, /version: Number\(linkedContainer\.version\), reason: context\.marker/)
|
||||
assert.match(containerLoadingAcceptance, /订舱 CONFIRMED 即使已有真实柜号也不开放 PDA 任务/)
|
||||
assert.match(containerLoadingAcceptance, /ocean-bookings\/\$\{bookingId\}\/release/)
|
||||
assert.match(containerLoadingAcceptance, /订舱放舱并开放现场 PDA 任务/)
|
||||
|
||||
@@ -170,6 +170,7 @@ export async function run(context) {
|
||||
id(linked?.id) === containerBookingId && linked?.cabinetNo === simulation.cabinetNo
|
||||
&& id(linked?.oceanBookingId) === bookingId,
|
||||
`containerBookingId=${containerBookingId}, cabinetNo=${linked?.cabinetNo || 'missing'}`, OPTIONS)
|
||||
if (id(linked?.id) !== containerBookingId) return
|
||||
|
||||
const candidateResponse = await context.request('GET', `${smartLoadingBase}/candidates?bookingId=${encodeURIComponent(containerBookingId)}&warehouseId=${encodeURIComponent(warehouseId)}`)
|
||||
const candidates = context.apiSuccess(MODULE, '查询本轮入库单柜次装柜候选', candidateResponse, '', OPTIONS)
|
||||
@@ -233,29 +234,55 @@ export async function run(context) {
|
||||
`linkId=${id(link?.id) || 'missing'}, preloadVersion=${link?.preloadVersion ?? 'missing'}, loadingVersion=${link?.loadingPlanVersion ?? 'missing'}`, OPTIONS)
|
||||
if (!link?.id) return
|
||||
|
||||
const missingContainerPushResponse = await request(context, 'POST',
|
||||
`${smartLoadingBase}/plans/${loadingPlanId}/push-preload`,
|
||||
{ expectedVersion: Number(link.loadingPlanVersion) },
|
||||
{ 'Idempotency-Key': `${context.marker}-bridge-push-without-container-no`.slice(0, 128) })
|
||||
const missingContainerPushRejected = failure(missingContainerPushResponse)
|
||||
context.addStep(MODULE, '无真实柜号拒绝推送柜次计划', missingContainerPushResponse,
|
||||
missingContainerPushRejected,
|
||||
`HTTP=${missingContainerPushResponse.http}, code=${codeOf(missingContainerPushResponse)}, requestId=${missingContainerPushResponse.requestId || 'missing'}, traceId=${missingContainerPushResponse.traceId || 'missing'}`, OPTIONS)
|
||||
if (!missingContainerPushRejected) return
|
||||
|
||||
const missingContainerTaskResponse = await context.request('GET',
|
||||
`${pdaBase}/tasks?loadingPlanId=${encodeURIComponent(loadingPlanId)}&warehouseId=${encodeURIComponent(warehouseId)}`)
|
||||
const missingContainerTaskAbsent = failure(missingContainerTaskResponse)
|
||||
|| !id(missingContainerTaskResponse?.body?.id || missingContainerTaskResponse?.body?.data?.id)
|
||||
context.addStep(MODULE, '无真实柜号推送失败后不生成 PDA 任务', missingContainerTaskResponse,
|
||||
missingContainerTaskAbsent,
|
||||
`HTTP=${missingContainerTaskResponse.http}, code=${codeOf(missingContainerTaskResponse)}, task=${id(missingContainerTaskResponse?.body?.id || missingContainerTaskResponse?.body?.data?.id) || 'none'}`, OPTIONS)
|
||||
if (!missingContainerTaskAbsent) return
|
||||
|
||||
const containerBeforeTransportResponse = await context.request('GET',
|
||||
`/api/platform/v1/containers/${containerBookingId}`)
|
||||
const containerBeforeTransport = context.apiSuccess(MODULE, '读取补真实柜号前柜次最新版本',
|
||||
containerBeforeTransportResponse, '', OPTIONS)
|
||||
const transportVersion = Number(containerBeforeTransport?.version)
|
||||
context.assert(MODULE, '补真实柜号使用订舱关联后的最新柜次版本', containerBeforeTransportResponse,
|
||||
id(containerBeforeTransport?.id) === containerBookingId && Number.isInteger(transportVersion),
|
||||
`containerBookingId=${id(containerBeforeTransport?.id) || 'missing'}, version=${containerBeforeTransport?.version ?? 'missing'}`, OPTIONS)
|
||||
if (!Number.isInteger(transportVersion)) return
|
||||
const transportResponse = await request(context, 'PATCH',
|
||||
`/api/platform/v1/containers/${containerBookingId}/transport`, {
|
||||
containerNo, voyageNo: `P3V-${suffix}`, version: transportVersion,
|
||||
reason: `${context.marker}-bind-container-no`,
|
||||
})
|
||||
const transported = context.apiSuccess(MODULE, '绑定真实柜号后开放三期 PDA 推送门禁', transportResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '真实柜号绑定到稳定柜次且计划身份不变', transportResponse,
|
||||
transported?.containerNo === containerNo && id(transported?.id) === containerBookingId
|
||||
&& transported?.cabinetNo === linked.cabinetNo,
|
||||
`containerNo=${transported?.containerNo || 'missing'}, containerBookingId=${id(transported?.id) || 'missing'}, loadingPlanId=${loadingPlanId}`, OPTIONS)
|
||||
if (transported?.containerNo !== containerNo) return
|
||||
|
||||
const pushResponse = await request(context, 'POST', `${smartLoadingBase}/plans/${loadingPlanId}/push-preload`,
|
||||
{ expectedVersion: Number(link.loadingPlanVersion) },
|
||||
{ 'Idempotency-Key': `${context.marker}-bridge-push`.slice(0, 128) })
|
||||
const pushedLoadingPlan = context.apiSuccess(MODULE, '桥接后推送柜次计划并生成真实 PDA 任务', pushResponse, '', OPTIONS)
|
||||
const pushedLoadingPlan = context.apiSuccess(MODULE, '绑定真实柜号后推送柜次计划并生成 PDA 任务', pushResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '推送后柜次计划保持 EDITING 且版本递增', pushResponse,
|
||||
pushedLoadingPlan?.status === 'EDITING'
|
||||
&& Number(pushedLoadingPlan?.version) === Number(link.loadingPlanVersion) + 1,
|
||||
`status=${pushedLoadingPlan?.status || 'missing'}, version=${pushedLoadingPlan?.version ?? 'missing'}`, OPTIONS)
|
||||
|
||||
const containerBeforeTransportResponse = await context.request('GET',
|
||||
`/api/platform/v1/containers/${containerBookingId}`)
|
||||
const containerBeforeTransport = context.apiSuccess(MODULE, '读取补真实柜号前柜次版本',
|
||||
containerBeforeTransportResponse, '', OPTIONS)
|
||||
const transportResponse = await request(context, 'PATCH',
|
||||
`/api/platform/v1/containers/${containerBookingId}/transport`, {
|
||||
containerNo, voyageNo: `P3V-${suffix}`, version: Number(containerBeforeTransport?.version),
|
||||
reason: `${context.marker}-bind-container-no`,
|
||||
})
|
||||
const transported = context.apiSuccess(MODULE, '补真实柜号并开放三期 PDA 任务', transportResponse, '', OPTIONS)
|
||||
context.assert(MODULE, '真实柜号绑定到稳定柜次且计划身份不变', transportResponse,
|
||||
transported?.containerNo === containerNo && id(transported?.id) === containerBookingId,
|
||||
`containerNo=${transported?.containerNo || 'missing'}, containerBookingId=${id(transported?.id) || 'missing'}`, OPTIONS)
|
||||
|
||||
const blockedBeforePreloadConfirm = await request(context, 'POST', `${base}/loading/complete?tenantId=${encodeURIComponent(tenantId)}`,
|
||||
{ expectedVersion: initialVersion, remark: 'must confirm preload first' },
|
||||
{ 'Idempotency-Key': `${context.marker}-p3-complete-before-preload-confirm`.slice(0, 128) })
|
||||
|
||||
@@ -86,6 +86,11 @@ test('container loading follows push, claim, real pick, completed sequence', ()
|
||||
test('bridge loading starts through a real PDA batch before preload confirmation', () => {
|
||||
const value = source('./phase3-loading.mjs')
|
||||
assertOrder(value, [
|
||||
'原子关联入库预装与柜次装柜计划',
|
||||
'无真实柜号拒绝推送柜次计划',
|
||||
'无真实柜号推送失败后不生成 PDA 任务',
|
||||
'绑定真实柜号后开放三期 PDA 推送门禁',
|
||||
'绑定真实柜号后推送柜次计划并生成 PDA 任务',
|
||||
"pushedLoadingPlan?.status === 'EDITING'",
|
||||
"claimedPlan?.status === 'LOADING'",
|
||||
"const canonicalPick = await request",
|
||||
@@ -98,6 +103,10 @@ test('bridge loading starts through a real PDA batch before preload confirmation
|
||||
assert.match(value, /confirmed\?\.mainStatus === 'LOADING'/)
|
||||
assert.match(value, /confirmed\?\.preloadPlan\?\.status === 'CONFIRMING'/)
|
||||
assert.match(value, /Number\(confirmed\?\.preloadPlan\?\.version\) === 2/)
|
||||
assert.match(value, /const missingContainerPushRejected = failure\(missingContainerPushResponse\)[\s\S]*if \(!missingContainerPushRejected\) return/)
|
||||
assert.match(value, /const missingContainerTaskAbsent =[\s\S]*if \(!missingContainerTaskAbsent\) return/)
|
||||
assert.match(value, /version: transportVersion/)
|
||||
assert.match(value, /Number\.isInteger\(transportVersion\)/)
|
||||
})
|
||||
|
||||
test('PDA inventory snapshot gates batch and records the only safe mutation gap', () => {
|
||||
@@ -149,6 +158,8 @@ test('container loading arranges a simulation before linking a confirmed real bo
|
||||
const draft = value.slice(value.indexOf('const bookingDraft = {'), value.indexOf('context.assert(MODULE, \'订舱渠道与仓库快照完整\''))
|
||||
assert.doesNotMatch(draft, /billOfLadingNo|vesselName|voyageNo|estimatedSailingAt|estimatedArrivalAt/)
|
||||
assert.match(value, /containerAllocations\.length === 0/)
|
||||
assert.match(value, /version: Number\(linkedContainer\.version\), reason: context\.marker/)
|
||||
assert.doesNotMatch(value, /version: Number\(containerDetail\?\.version \?\? 0\), reason: context\.marker/)
|
||||
assert.match(value, /function responseEvidence\(response\)[\s\S]*HTTP=\$\{response\?\.http \?\? 'missing'\}[\s\S]*code=\$\{codeOf\(response\)\}[\s\S]*requestId=\$\{response\?\.requestId \|\| 'missing'\}[\s\S]*traceId=\$\{response\?\.traceId \|\| 'missing'\}/)
|
||||
assert.match(value, /emptySimulation = context\.apiSuccess\(MODULE, '空模拟柜首次关联真实订舱', emptyLink,\s+responseEvidence\(emptyLink\), OPTIONS\)\s+if \(!emptySimulation\) return\s+const emptyUnlink = await request/)
|
||||
assert.match(value, /emptySimulation = context\.apiSuccess\(MODULE, '空模拟柜允许解除关联', emptyUnlink,\s+responseEvidence\(emptyUnlink\), OPTIONS\)\s+if \(!emptySimulation\) return\s+const emptyRelink = await request/)
|
||||
|
||||
@@ -61,3 +61,23 @@ test('test-environment full acceptance keeps assigned and unassigned customer bo
|
||||
assert.match(bookingModule, /指定客户订舱持久化本轮客户范围快照/)
|
||||
assert.match(bookingModule, /phase3Loading[\s\S]*phase3ContainerLoading/)
|
||||
})
|
||||
|
||||
test('test-environment loading chains bind the latest container version before opening PDA', () => {
|
||||
const containerLoading = readFileSync(
|
||||
resolve(scriptsDir, 'admin-test-env-modules', 'phase3-container-loading.mjs'), 'utf8')
|
||||
const bridgeLoading = readFileSync(
|
||||
resolve(scriptsDir, 'admin-test-env-modules', 'phase3-loading.mjs'), 'utf8')
|
||||
|
||||
assert.match(containerLoading, /version: Number\(linkedContainer\.version\), reason: context\.marker/)
|
||||
|
||||
const bridgeIndex = bridgeLoading.indexOf('原子关联入库预装与柜次装柜计划')
|
||||
const rejectedPushIndex = bridgeLoading.indexOf('无真实柜号拒绝推送柜次计划')
|
||||
const transportIndex = bridgeLoading.indexOf('绑定真实柜号后开放三期 PDA 推送门禁')
|
||||
const successfulPushIndex = bridgeLoading.indexOf('绑定真实柜号后推送柜次计划并生成 PDA 任务')
|
||||
const pdaIndex = bridgeLoading.indexOf('读取柜次级 PDA 唯一事实任务')
|
||||
assert.ok(bridgeIndex >= 0 && rejectedPushIndex > bridgeIndex && transportIndex > rejectedPushIndex
|
||||
&& successfulPushIndex > transportIndex && pdaIndex > successfulPushIndex)
|
||||
assert.match(bridgeLoading, /const missingContainerPushRejected = failure\(missingContainerPushResponse\)[\s\S]*if \(!missingContainerPushRejected\) return/)
|
||||
assert.match(bridgeLoading, /const missingContainerTaskAbsent =[\s\S]*if \(!missingContainerTaskAbsent\) return/)
|
||||
assert.match(bridgeLoading, /version: transportVersion/)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user