test(firstleg): cover simulated container booking links

This commit is contained in:
2026-08-14 21:00:20 +08:00
parent 7184f5083a
commit a4a4374c67
14 changed files with 318 additions and 81 deletions
@@ -25,6 +25,7 @@ async function createConfirmedUnassignedBooking(context, suffix, master) {
const createdResponse = await request(context, 'POST', BASE, { expectedVersion: 0, data: draft }, { 'Idempotency-Key': key })
const created = data(createdResponse)
const bookingId = id(created?.oceanBooking?.id)
const createdBookingNo = id(created?.oceanBooking?.bookingNo)
context.apiSuccess(MODULE, '创建未指定客户订舱', createdResponse,
`marker=${context.marker}, customerFields=${'customerId' in draft || 'customerName' in draft ? 'present' : 'omitted'}`, OPTIONS)
context.assert(MODULE, '未指定客户订舱不伪造客户快照', createdResponse,
@@ -32,6 +33,12 @@ async function createConfirmedUnassignedBooking(context, suffix, master) {
`bookingId=${bookingId || 'missing'}, customerId=${id(created?.oceanBooking?.customerId) || 'null'}, customerName=${created?.oceanBooking?.customerName || 'null'}`, OPTIONS)
if (!bookingId) return null
const hiddenResponse = await request(context, 'GET', `${BASE}?page=1&size=20&keyword=${encodeURIComponent(createdBookingNo)}`)
const hiddenPage = context.apiSuccess(MODULE, '未确认订舱不进入正式订舱列表', hiddenResponse, '', OPTIONS)
context.assert(MODULE, 'DRAFT 未指定客户订舱仅可按详情维护', hiddenResponse,
!hiddenPage?.records?.some?.((item) => id(item.id) === bookingId),
`bookingId=${bookingId}, records=${hiddenPage?.records?.length ?? 'n/a'}`, OPTIONS)
const path = `${BASE}/${bookingId}`
const submitResponse = await request(context, 'POST', `${path}/submit`, {
expectedVersion: Number(created.oceanBooking.version), reason: context.marker,
@@ -40,16 +47,15 @@ async function createConfirmedUnassignedBooking(context, suffix, master) {
const confirmResponse = await request(context, 'POST', `${path}/confirm`, {
expectedVersion: Number(submitted?.oceanBooking?.version), reason: context.marker,
}, { 'Idempotency-Key': `${context.marker}-ocean-unassigned-confirm`.slice(0, 128) })
context.apiSuccess(MODULE, '确认未指定客户订舱并生成柜次', confirmResponse, '', OPTIONS)
context.apiSuccess(MODULE, '确认未指定客户真实订舱', confirmResponse, '', OPTIONS)
const detailResponse = await request(context, 'GET', path)
const detail = context.apiSuccess(MODULE, '回查未指定客户订舱持久化结果', detailResponse, '', OPTIONS)
const containerBookingId = id(detail?.containerAllocations?.[0]?.containerBookingId)
const bookingNo = id(detail?.oceanBooking?.bookingNo || created?.oceanBooking?.bookingNo)
context.assert(MODULE, '未指定客户订舱保持空客户并在 CONFIRMED 生成稳定柜次', detailResponse,
context.assert(MODULE, '未指定客户订舱保持空客户且 CONFIRMED 不自动生成内部柜', detailResponse,
detail?.oceanBooking?.status === 'CONFIRMED' && detail?.oceanBooking?.customerId == null
&& detail?.oceanBooking?.customerName == null && Boolean(containerBookingId) && Boolean(bookingNo),
`status=${detail?.oceanBooking?.status || 'missing'}, customerId=${id(detail?.oceanBooking?.customerId) || 'null'}, bookingNo=${bookingNo || 'missing'}, containerBookingId=${containerBookingId || 'missing'}`, OPTIONS)
&& detail?.oceanBooking?.customerName == null && detail?.containerAllocations?.length === 0 && Boolean(bookingNo),
`status=${detail?.oceanBooking?.status || 'missing'}, customerId=${id(detail?.oceanBooking?.customerId) || 'null'}, bookingNo=${bookingNo || 'missing'}, allocations=${detail?.containerAllocations?.length ?? 'n/a'}`, OPTIONS)
const listResponse = await request(context, 'GET', `${BASE}?page=1&size=20&keyword=${encodeURIComponent(bookingNo)}`)
const page = context.apiSuccess(MODULE, '按真实订舱号查询未指定客户订舱', listResponse, `bookingNo=${bookingNo}`, OPTIONS)
@@ -57,7 +63,7 @@ async function createConfirmedUnassignedBooking(context, suffix, master) {
page?.records?.some?.((item) => id(item.id) === bookingId && item.customerId == null && item.customerName == null),
`bookingId=${bookingId}, bookingNo=${bookingNo}, records=${page?.records?.length ?? 'n/a'}`, OPTIONS)
const resource = { id: bookingId, marker: context.marker, customerId: null, customerName: null,
bookingNo, channelCode: draft.channelCode, warehouseId: draft.warehouseId, containerBookingId,
bookingNo, channelCode: draft.channelCode, warehouseId: draft.warehouseId, containerBookingId: null,
finalStatus: detail?.oceanBooking?.status || null }
context.createdResources.oceanBookingUnassignedCustomer = resource
return resource
@@ -119,8 +125,9 @@ export async function run(context) {
}
const page = context.apiSuccess(MODULE, '订舱列表分页查询', list, '', OPTIONS)
context.assert(MODULE, '订舱列表分页结构正确', list, Array.isArray(page?.records) && Number.isFinite(Number(page?.total)), `records=${page?.records?.length ?? 'n/a'}`, OPTIONS)
const statuses = ['DRAFT', 'SUBMITTED', 'CONFIRMED', 'RELEASED', 'FAILED', 'CANCELLED']
const statuses = ['CONFIRMED', 'CHANGE_PENDING', 'RELEASED', 'CANCELLED']
for (const status of statuses) { const r = await request(context, 'GET', `${BASE}?page=1&size=5&status=${status}`); const d = data(r); step(context, `订舱 ${status} 状态筛选`, r, r.http === 200 && codeOf(r) === '200' && Array.isArray(d?.records) && d.records.every((x) => x.status === status), `HTTP=${r.http}, code=${codeOf(r)}, records=${d?.records?.length ?? 'n/a'}`) }
for (const status of ['DRAFT', 'SUBMITTED']) { const r = await request(context, 'GET', `${BASE}?page=1&size=5&status=${status}`); step(context, `正式订舱列表拒绝 ${status} 状态`, r, failure(r), `HTTP=${r.http}, code=${codeOf(r)}`) }
await createConfirmedUnassignedBooking(context, suffix, master)
@@ -154,7 +161,7 @@ export async function run(context) {
const changed = await request(context, 'POST', `${path}/change-request`, { expectedVersion: version, vesselName: `改船-${suffix}`, voyageNo: `CV${suffix}`, reason: context.marker }, { 'Idempotency-Key': `${context.marker}-change` })
context.apiSuccess(MODULE, '申请改船', changed, '', OPTIONS); version = Number(data(changed)?.oceanBooking?.version)
const changeConfirmed = await action('确认改船', 'change-confirm', version, 'confirm change')
let allocatedContainerBookingId = id(changeConfirmed?.containerAllocations?.[0]?.containerBookingId)
const allocatedContainerBookingId = ''
if (containerFlow?.finalStatus === 'COMPLETED' || containerFlow?.finalStatus === 'LOADED') {
context.assert(MODULE, '订舱与本轮柜次客户归属一致', changed,
id(containerFlow?.tenantId) === id(changeConfirmed?.oceanBooking?.customerId),
@@ -163,9 +170,9 @@ export async function run(context) {
context.assert(MODULE, '订舱存在匹配本轮柜型的箱型需求', changed,
Boolean(requirement?.id), `requirementId=${id(requirement?.id) || 'missing'}`, OPTIONS)
if (!requirement?.id || id(containerFlow?.tenantId) !== id(changeConfirmed?.oceanBooking?.customerId)) return
context.assert(MODULE, '确认订舱自动生成并关联柜次', changed,
Boolean(allocatedContainerBookingId) && Number.isInteger(version),
`containerBookingId=${allocatedContainerBookingId || 'missing'}, version=${version}`, OPTIONS)
context.assert(MODULE, '独立订舱生命周期不占用本轮已装货柜次', changed,
changeConfirmed?.containerAllocations?.length === 0 && Number.isInteger(version),
`allocations=${changeConfirmed?.containerAllocations?.length ?? 'n/a'}, version=${version}`, OPTIONS)
}
const released = await request(context, 'POST', `${path}/release`, {
expectedVersion: version, billOfLadingNo: `BL-${suffix}`, vesselName: `放舱船-${suffix}`,
@@ -177,9 +184,9 @@ export async function run(context) {
version = Number(releasedDetail?.oceanBooking?.version)
const final = await request(context, 'GET', path); const fd = data(final)
context.apiSuccess(MODULE, '回查 RELEASED 订舱详情', final, '', OPTIONS)
context.assert(MODULE, '订舱状态历史、审计操作者、柜次关联和终态完整', final,
context.assert(MODULE, '独立订舱状态历史、审计操作者和终态完整', final,
fd?.oceanBooking?.status === 'RELEASED' && fd.histories?.length >= 5 && fd.histories.every((h) => h.operatorName)
&& fd?.containerAllocations?.some?.((item) => id(item?.containerBookingId) === allocatedContainerBookingId),
&& fd?.containerAllocations?.length === 0,
`status=${fd?.oceanBooking?.status || 'missing'}, histories=${fd?.histories?.length ?? 'n/a'}, allocations=${fd?.containerAllocations?.length ?? 'n/a'}`, OPTIONS)
let downstreamBooking = {
id: bookingId, customerId: id(fd?.oceanBooking?.customerId), marker: context.marker,