完成单机TEST统一入口与可靠投递回归收尾
This commit is contained in:
@@ -32,16 +32,12 @@ function sameIds(left, right) {
|
||||
}
|
||||
|
||||
export async function run(context) {
|
||||
if (!context.token || !context.overseasToken) {
|
||||
blockingGap(context, '双Token前置', '国内或海外访问令牌缺失,不能执行跨区安全门禁', 'missing-dual-token')
|
||||
}
|
||||
|
||||
const domesticTokenAgainstOverseas = await context.requestOverseas('GET',
|
||||
'/api/platform/v1/last-leg/orders?page=1&size=1', undefined, {}, context.token)
|
||||
context.assert(MODULE, '国内Token不能访问海外Audience', domesticTokenAgainstOverseas,
|
||||
denied(domesticTokenAgainstOverseas) && traced(domesticTokenAgainstOverseas),
|
||||
evidence(domesticTokenAgainstOverseas), OPTIONS)
|
||||
|
||||
if (!context.token) blockingGap(context, '统一会话前置', '平台访问令牌缺失', 'missing-session')
|
||||
const authorized = await context.request('GET', '/api/platform/v1/last-leg/orders?page=1&size=1')
|
||||
context.assert(MODULE, '统一平台会话可访问Lastleg授权列表', authorized,
|
||||
success(authorized) && traced(authorized), evidence(authorized), OPTIONS)
|
||||
const anonymous = await context.request('GET', '/api/platform/v1/last-leg/orders?page=1&size=1', undefined, '')
|
||||
context.assert(MODULE, '无会话不得访问Lastleg', anonymous, denied(anonymous) && traced(anonymous), evidence(anonymous), OPTIONS)
|
||||
const phase3 = context.createdResources?.phase3Loading
|
||||
if (phase3?.marker !== context.marker || !phase3?.finalizeSucceeded
|
||||
|| !phase3?.containerBookingId || !phase3?.containerNo || !phase3?.loadingPlanId) {
|
||||
@@ -50,31 +46,29 @@ export async function run(context) {
|
||||
'missing-marker-loading-completed')
|
||||
}
|
||||
|
||||
const overseasTokenAgainstDomestic = await context.request('GET',
|
||||
`/api/platform/v1/containers/domestic/${encodeURIComponent(id(phase3.containerBookingId))}`,
|
||||
undefined, context.overseasToken)
|
||||
context.assert(MODULE, '海外Token不能访问国内Audience', overseasTokenAgainstDomestic,
|
||||
denied(overseasTokenAgainstDomestic) && traced(overseasTokenAgainstDomestic),
|
||||
evidence(overseasTokenAgainstDomestic), OPTIONS)
|
||||
const domestic = await context.request('GET',
|
||||
`/api/platform/v1/containers/domestic/${encodeURIComponent(id(phase3.containerBookingId))}`)
|
||||
context.assert(MODULE, '同一平台会话可访问国内授权详情', domestic,
|
||||
success(domestic) && traced(domestic), evidence(domestic), OPTIONS)
|
||||
|
||||
const taillegPath = `/api/platform/v1/last-leg/orders?page=1&size=200&containerNo=${encodeURIComponent(phase3.containerNo)}`
|
||||
const delivered = await poll(
|
||||
() => context.requestOverseas('GET', taillegPath),
|
||||
() => context.request('GET', taillegPath),
|
||||
(response) => success(response) && records(response.body?.data)
|
||||
.some((item) => item?.containerNo === phase3.containerNo))
|
||||
const deliveredRows = records(delivered.response?.body?.data)
|
||||
.filter((item) => item?.containerNo === phase3.containerNo)
|
||||
context.assert(MODULE, '国内装柜完成经Bridge生成Lastleg订单', delivered.response,
|
||||
context.assert(MODULE, '国内装柜完成经Outbox直接投递生成Lastleg订单', delivered.response,
|
||||
deliveredRows.length > 0 && traced(delivered.response),
|
||||
evidence(delivered.response, `pollRequests=${delivered.attempts}, containerNo=${phase3.containerNo}, orders=${deliveredRows.length}`), OPTIONS)
|
||||
if (deliveredRows.length === 0) {
|
||||
blockingGap(context, '国内到海外事件闭环',
|
||||
`本轮柜号${phase3.containerNo}在轮询后仍无Lastleg订单;需结合requestId/traceId检查Bridge重试与下游诊断`,
|
||||
`本轮柜号${phase3.containerNo}在轮询后仍无Lastleg订单;需结合requestId/traceId检查生产服务Outbox重试与下游诊断`,
|
||||
'cn-overseas-event-not-delivered', delivered.response)
|
||||
}
|
||||
|
||||
const deliveredAgain = await context.requestOverseas('GET', taillegPath)
|
||||
context.assert(MODULE, '装柜完成事件重试保持建单幂等', deliveredAgain,
|
||||
const deliveredAgain = await context.request('GET', taillegPath)
|
||||
context.assert(MODULE, '装柜结果重复读取稳定(非事件重放证据)', deliveredAgain,
|
||||
success(deliveredAgain) && sameIds(delivered.response.body?.data, deliveredAgain.body?.data)
|
||||
&& traced(deliveredAgain),
|
||||
evidence(deliveredAgain, `firstOrders=${deliveredRows.map((item) => id(item.id)).join(',')}`), OPTIONS)
|
||||
@@ -88,7 +82,7 @@ export async function run(context) {
|
||||
}
|
||||
|
||||
const orderDetailPath = `/api/platform/v1/last-leg/orders/${encodeURIComponent(overseasPda.orderId)}?customerId=${encodeURIComponent(overseasPda.customerId)}`
|
||||
const orderDetail = await context.requestOverseas('GET', orderDetailPath)
|
||||
const orderDetail = await context.request('GET', orderDetailPath)
|
||||
const order = orderDetail.body?.data?.order || orderDetail.body?.data
|
||||
const completedContainerNo = order?.containerNo || ''
|
||||
context.assert(MODULE, '读取海外完成柜来源尾程单', orderDetail,
|
||||
@@ -127,13 +121,13 @@ export async function run(context) {
|
||||
const validTimeline = Number.isFinite(completedAt) && Number.isFinite(uploadedAt)
|
||||
&& Number.isFinite(estimatedAt) && completedAt >= uploadedAt
|
||||
&& estimatedAt - completedAt === 72 * 60 * 60 * 1_000
|
||||
context.assert(MODULE, '海外柜完成经Bridge回写Firstleg时间事实', writtenBack.response,
|
||||
context.assert(MODULE, '海外柜完成经Outbox直接投递回写Firstleg时间事实', writtenBack.response,
|
||||
validTimeline && traced(writtenBack.response),
|
||||
evidence(writtenBack.response,
|
||||
`pollRequests=${writtenBack.attempts}, completed=${snapshot.unloadingCompletedAt || 'missing'}, photo=${snapshot.unloadingPhotoUploadedAt || 'missing'}, estimatedEmpty=${snapshot.estimatedEmptyReportedAt || 'missing'}`), OPTIONS)
|
||||
if (!validTimeline) {
|
||||
blockingGap(context, '海外到国内事件闭环',
|
||||
`柜号${completedContainerNo}轮询后仍无有效卸柜完成/拍照/预计报空回写;需检查Bridge重试与诊断证据`,
|
||||
`柜号${completedContainerNo}轮询后仍无有效卸柜完成/拍照/预计报空回写;需检查生产服务Outbox重试与诊断证据`,
|
||||
'overseas-cn-event-not-delivered', writtenBack.response)
|
||||
}
|
||||
|
||||
@@ -141,8 +135,11 @@ export async function run(context) {
|
||||
const replaySnapshot = writtenBackAgain.body?.data?.container || {}
|
||||
const frozen = ['unloadingCompletedAt', 'unloadingPhotoUploadedAt', 'estimatedEmptyReportedAt',
|
||||
'actualEmptyReportedAt', 'emptyReportSourceVersion']
|
||||
context.assert(MODULE, '海外柜完成事件重试保持回写幂等', writtenBackAgain,
|
||||
context.assert(MODULE, '卸柜时间事实重复读取稳定(非事件重放证据)', writtenBackAgain,
|
||||
success(writtenBackAgain) && frozen.every((field) => (replaySnapshot[field] ?? null) === (snapshot[field] ?? null))
|
||||
&& traced(writtenBackAgain),
|
||||
evidence(writtenBackAgain, `containerId=${id(domesticMatches[0].id)}, sourceVersion=${snapshot.emptyReportSourceVersion ?? 'missing'}`), OPTIONS)
|
||||
blockingGap(context, '真实投递重试与同事件幂等证据',
|
||||
'两次GET仅证明读取稳定;需按event-outbox-operations.mjs的受保护内部运维流程,在本轮事件上验证故障后有限重试/DEAD及同eventId+payloadHash重复消费,保存逐次尝试与业务计数证据。不得重放SENT、改表或伪造新事件。',
|
||||
'missing-direct-delivery-replay-evidence')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user