完成单机TEST统一入口与可靠投递回归收尾
This commit is contained in:
@@ -59,20 +59,19 @@ function blockerGap(context, name, reason, category = 'fixture-unavailable', res
|
||||
}
|
||||
|
||||
async function pda(context, token, method, name, path, body = undefined) {
|
||||
const response = await context.requestOverseas(method, path, body, {}, token)
|
||||
const response = await context.request(method, path, body, token, {})
|
||||
context.addStep(MODULE, name, response, success(response), trace(response), OPTIONS)
|
||||
return { response, data: success(response) ? response.body?.data : null }
|
||||
}
|
||||
|
||||
async function expected(context, token, method, name, path, body, predicate) {
|
||||
const response = await context.requestOverseas(method, path, body, {}, token)
|
||||
const response = await context.request(method, path, body, token, {})
|
||||
context.addStep(MODULE, name, response, predicate(response), trace(response), OPTIONS)
|
||||
return response
|
||||
}
|
||||
|
||||
async function captureCompletedOrder(context, containerNo, expectedCustomerId) {
|
||||
const response = await context.requestOverseas('GET',
|
||||
`/api/platform/v1/last-leg/orders?page=1&size=200&containerNo=${encodeURIComponent(containerNo)}`)
|
||||
const response = await context.request('GET', `/api/platform/v1/last-leg/orders?page=1&size=200&containerNo=${encodeURIComponent(containerNo)}`)
|
||||
const candidates = records(response?.body?.data)
|
||||
.filter((item) => id(item?.containerNo) === id(containerNo))
|
||||
const exactCustomer = expectedCustomerId == null ? candidates
|
||||
@@ -85,7 +84,7 @@ async function captureCompletedOrder(context, containerNo, expectedCustomerId) {
|
||||
context.createdResources.phase4OverseasPda.orderId = id(order.id)
|
||||
context.createdResources.phase4OverseasPda.customerId = id(order.customerId)
|
||||
const detailPath = `/api/platform/v1/last-leg/orders/${encodeURIComponent(id(order.id))}?customerId=${encodeURIComponent(id(order.customerId))}`
|
||||
const detail = await context.requestOverseas('GET', detailPath)
|
||||
const detail = await context.request('GET', detailPath)
|
||||
context.assert(MODULE, '完成柜回查尾程订单货物明细供后续PDA作业', detail,
|
||||
success(detail) && id(detail.body?.data?.order?.id) === id(order.id)
|
||||
&& Array.isArray(detail.body?.data?.cargo) && detail.body.data.cargo.length > 0,
|
||||
@@ -102,10 +101,10 @@ async function loginMerchantForNegativeProbe(context) {
|
||||
if (!username || !password) return null
|
||||
const response = await context.request('POST', '/api/auth/login', { loginId: username, password }, '')
|
||||
const data = success(response) ? response.body?.data : null
|
||||
context.addStep(MODULE, '本轮客户登录取得海外令牌用于PDA越权反证', response,
|
||||
success(response) && nonBlank(data?.overseasAccessToken),
|
||||
`overseasTokenPresent=${nonBlank(data?.overseasAccessToken)}; ${trace(response)}`, OPTIONS)
|
||||
return nonBlank(data?.overseasAccessToken) ? data.overseasAccessToken : null
|
||||
context.addStep(MODULE, '本轮客户登录取得访问令牌用于PDA越权反证', response,
|
||||
success(response) && nonBlank(data?.accessToken),
|
||||
`tokenPresent=${nonBlank(data?.accessToken)}; ${trace(response)}`, OPTIONS)
|
||||
return nonBlank(data?.accessToken) ? data.accessToken : null
|
||||
}
|
||||
|
||||
export async function resolvePickerAccess(context) {
|
||||
@@ -144,17 +143,16 @@ export async function createPicker(context, index, overseasWarehouseId, access)
|
||||
`membershipId=${membershipId || 'missing'}, tenantId=${id(created?.tenantId) || 'missing'}, overseasWarehouseId=${id(created?.overseasWarehouseId) || 'missing'}`, OPTIONS)
|
||||
if (!longId(membershipId)) return null
|
||||
const login = await context.request('POST', '/api/auth/login', { loginId: username, password }, '')
|
||||
const token = success(login) ? login.body?.data?.overseasAccessToken : null
|
||||
context.addStep(MODULE, `海外PDA操作员${index}登录取得双区域海外令牌`, login, nonBlank(token),
|
||||
`overseasTokenPresent=${nonBlank(token)}; ${trace(login)}`, OPTIONS)
|
||||
const token = success(login) ? login.body?.data?.accessToken : null
|
||||
context.addStep(MODULE, `海外PDA操作员${index}登录取得统一访问令牌`, login, nonBlank(token),
|
||||
`tokenPresent=${nonBlank(token)}; ${trace(login)}`, OPTIONS)
|
||||
if (!nonBlank(token)) return { membershipId, username, token: null }
|
||||
|
||||
let projectionResponse = null
|
||||
let projectionReady = false
|
||||
let projectionAttempt = 0
|
||||
for (; projectionAttempt < 30; projectionAttempt += 1) {
|
||||
projectionResponse = await context.requestOverseas('GET',
|
||||
'/api/pda/v1/last-leg/receiving-containers/current', undefined, {}, token)
|
||||
projectionResponse = await context.request('GET', '/api/pda/v1/last-leg/receiving-containers/current', undefined, token, {})
|
||||
projectionReady = success(projectionResponse) || notFound(projectionResponse)
|
||||
if (projectionReady || !forbidden(projectionResponse)) break
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
@@ -193,12 +191,10 @@ async function resolveGeneratedPallet(context, inbound) {
|
||||
const pallet = candidates[0]
|
||||
if (!pallet || !longId(pallet.palletId) || !nonBlank(pallet.palletCode)) return null
|
||||
const warehouseId = id(inbound?.overseasWarehouseId || pallet.warehouseId)
|
||||
const listed = await context.requestOverseas('GET',
|
||||
`/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}/pallets?page=1&size=10&keyword=${encodeURIComponent(pallet.palletCode)}`)
|
||||
const listed = await context.request('GET', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/pallets?page=1&size=10&keyword=${encodeURIComponent(pallet.palletCode)}`)
|
||||
const stored = records(listed?.body?.data).find((row) => id(row?.id) === pallet.palletId && row?.palletCode === pallet.palletCode)
|
||||
const locations = stored && longId(stored?.locationId)
|
||||
? await context.requestOverseas('GET',
|
||||
`/api/platform/v1/warehouse-admin/${encodeURIComponent(warehouseId)}/locations?page=1&size=200`)
|
||||
? await context.request('GET', `/api/platform/v1/overseas-warehouse-resources/${encodeURIComponent(warehouseId)}/locations?page=1&size=200`)
|
||||
: null
|
||||
const location = records(locations?.body?.data).find((row) => id(row?.id) === id(stored?.locationId))
|
||||
const resolved = success(listed) && success(locations) && stored && longId(stored?.locationId) && nonBlank(location?.locationCode)
|
||||
@@ -263,14 +259,14 @@ async function verifyWarehouseQueries(context, token, inboundCode, pallet, desti
|
||||
}
|
||||
|
||||
export async function uploadPhotos(context, token) {
|
||||
const malicious = await context.requestOverseasMultipart('POST', '/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files', {
|
||||
const malicious = await context.requestMultipart('POST', '/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files', {
|
||||
name: `${context.marker}-forged.png`, type: 'image/png', content: Uint8Array.from(Buffer.from('<script>alert(1)</script>')),
|
||||
}, token)
|
||||
context.addStep(MODULE, '伪造图片内容安全扫描返回A0400', malicious,
|
||||
malicious?.http === 200 && code(malicious) === 'A0400' && traced(malicious), trace(malicious), OPTIONS)
|
||||
const photos = []
|
||||
for (const role of PHOTO_ROLES) {
|
||||
const response = await context.requestOverseasMultipart('POST', '/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files', {
|
||||
const response = await context.requestMultipart('POST', '/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files', {
|
||||
name: `${context.marker}-${role.toLowerCase()}.png`, type: 'image/png', content: PNG,
|
||||
}, token)
|
||||
const fileId = id(response?.body?.data?.fileId)
|
||||
@@ -290,8 +286,7 @@ async function awaitProducerConsumption(context, producers) {
|
||||
for (let attempt = 1; attempt <= 90; attempt += 1) {
|
||||
const missing = expected.filter((containerNo) => !consumed.has(containerNo))
|
||||
const results = await Promise.all(missing.map(async (containerNo) => {
|
||||
const response = await context.requestOverseas('GET',
|
||||
`/api/platform/v1/last-leg/orders?page=1&size=200&containerNo=${encodeURIComponent(containerNo)}`)
|
||||
const response = await context.request('GET', `/api/platform/v1/last-leg/orders?page=1&size=200&containerNo=${encodeURIComponent(containerNo)}`)
|
||||
return { containerNo, response }
|
||||
}))
|
||||
for (const { containerNo, response } of results) {
|
||||
@@ -316,9 +311,7 @@ async function awaitProducerConsumption(context, producers) {
|
||||
async function convertReceivingContainers(context, producers) {
|
||||
for (const producer of producers) {
|
||||
const containerNo = id(producer?.containerNo)
|
||||
const summaryResponse = await context.requestOverseas('GET',
|
||||
`/api/platform/v1/last-leg/orders/container-summaries?page=1&size=20&containerNo=${encodeURIComponent(containerNo)}`,
|
||||
undefined, {}, context.overseasToken)
|
||||
const summaryResponse = await context.request('GET', `/api/platform/v1/last-leg/orders/container-summaries?page=1&size=20&containerNo=${encodeURIComponent(containerNo)}`, undefined, context.token, {})
|
||||
const summaries = success(summaryResponse) && Array.isArray(summaryResponse.body?.data?.records)
|
||||
? summaryResponse.body.data.records : []
|
||||
const summary = summaries.find((item) => id(item?.containerNo) === containerNo)
|
||||
@@ -336,9 +329,7 @@ async function convertReceivingContainers(context, producers) {
|
||||
`sourceContainerBookingId=${id(summary?.sourceContainerBookingId) || 'missing'}, allPlansReady=${summary?.allPlansReady === true}, orders=${totalOrders}, inboundCodes=${inboundCodes}, plans=${existingPlans}, pending=${pendingPlans}; ${trace(summaryResponse)}`, OPTIONS)
|
||||
if (!convertible) return false
|
||||
const bookingId = id(summary.sourceContainerBookingId)
|
||||
const arrivalQueryResponse = await context.requestOverseas('POST',
|
||||
'/api/platform/v1/last-leg/orders/container-arrival-times/query',
|
||||
{ sourceContainerBookingIds: [bookingId] }, {}, context.overseasToken)
|
||||
const arrivalQueryResponse = await context.request('POST', '/api/platform/v1/last-leg/orders/container-arrival-times/query', { sourceContainerBookingIds: [bookingId] }, context.token, {})
|
||||
const arrivalTimes = success(arrivalQueryResponse) && Array.isArray(arrivalQueryResponse.body?.data)
|
||||
? arrivalQueryResponse.body.data : []
|
||||
let arrivalTime = arrivalTimes.find((item) => id(item?.sourceContainerBookingId) === bookingId)
|
||||
@@ -349,12 +340,8 @@ async function convertReceivingContainers(context, producers) {
|
||||
if (!arrivalTime) return false
|
||||
if (!nonBlank(arrivalTime.estimatedWarehouseArrivalAt)) {
|
||||
const estimatedWarehouseArrivalAt = new Date(Date.now() + 2 * 86400000).toISOString().slice(0, 19)
|
||||
const arrivalUpdateResponse = await context.requestOverseas('PUT',
|
||||
`/api/platform/v1/last-leg/orders/container-summaries/${encodeURIComponent(bookingId)}/expected-arrival`,
|
||||
{ estimatedWarehouseArrivalAt, expectedVersion: Number(arrivalTime.version),
|
||||
remark: `${context.marker}-overseas-inbound-plan-prerequisite` },
|
||||
{ 'Idempotency-Key': `${context.marker}-expected-arrival-${containerNo}`.slice(0, 128) },
|
||||
context.overseasToken)
|
||||
const arrivalUpdateResponse = await context.request('PUT', `/api/platform/v1/last-leg/orders/container-summaries/${encodeURIComponent(bookingId)}/expected-arrival`, { estimatedWarehouseArrivalAt, expectedVersion: Number(arrivalTime.version),
|
||||
remark: `${context.marker}-overseas-inbound-plan-prerequisite` }, context.token, { 'Idempotency-Key': `${context.marker}-expected-arrival-${containerNo}`.slice(0, 128) })
|
||||
arrivalTime = success(arrivalUpdateResponse) ? arrivalUpdateResponse.body?.data : null
|
||||
context.addStep(MODULE, `维护整柜预计到仓时间 ${containerNo}`, arrivalUpdateResponse,
|
||||
id(arrivalTime?.sourceContainerBookingId) === bookingId
|
||||
@@ -364,8 +351,7 @@ async function convertReceivingContainers(context, producers) {
|
||||
if (!arrivalTime || !nonBlank(arrivalTime.estimatedWarehouseArrivalAt)) return false
|
||||
}
|
||||
const previewPath = `/api/platform/v1/last-leg/inbound-plans/container-preview?sourceContainerBookingId=${encodeURIComponent(bookingId)}`
|
||||
const previewResponse = await context.requestOverseas('GET', previewPath,
|
||||
undefined, {}, context.overseasToken)
|
||||
const previewResponse = await context.request('GET', previewPath, undefined, context.token, {})
|
||||
const preview = success(previewResponse) ? previewResponse.body?.data : null
|
||||
context.addStep(MODULE, `读取PDA前柜级计划与自动托盘预览 ${containerNo}`, previewResponse,
|
||||
preview?.ready === true && Number(preview?.totalOrderCount) === totalOrders
|
||||
@@ -377,9 +363,7 @@ async function convertReceivingContainers(context, producers) {
|
||||
const body = { sourceContainerBookingId: bookingId,
|
||||
snapshotVersion: Number(preview.snapshotVersion), snapshotHash: String(preview.snapshotHash) }
|
||||
const key = `${context.marker}-pda-convert-${containerNo}`.slice(0, 128)
|
||||
const convertedResponse = await context.requestOverseas('POST',
|
||||
'/api/platform/v1/last-leg/inbound-plans/convert-container', body,
|
||||
{ 'Idempotency-Key': key }, context.overseasToken)
|
||||
const convertedResponse = await context.request('POST', '/api/platform/v1/last-leg/inbound-plans/convert-container', body, context.token, { 'Idempotency-Key': key })
|
||||
const plans = success(convertedResponse) && Array.isArray(convertedResponse.body?.data)
|
||||
? convertedResponse.body.data : []
|
||||
const convertedInboundCodes = new Set(plans
|
||||
@@ -412,9 +396,7 @@ async function convertReceivingContainers(context, producers) {
|
||||
OPTIONS)
|
||||
}
|
||||
rememberGeneratedPlans(context, plans)
|
||||
const replayResponse = await context.requestOverseas('POST',
|
||||
'/api/platform/v1/last-leg/inbound-plans/convert-container', body,
|
||||
{ 'Idempotency-Key': key }, context.overseasToken)
|
||||
const replayResponse = await context.request('POST', '/api/platform/v1/last-leg/inbound-plans/convert-container', body, context.token, { 'Idempotency-Key': key })
|
||||
context.addStep(MODULE, `整柜转换幂等重放零新增 ${containerNo}`, replayResponse,
|
||||
success(replayResponse) && JSON.stringify(replayResponse.body?.data) === JSON.stringify(plans),
|
||||
trace(replayResponse), OPTIONS)
|
||||
@@ -428,9 +410,7 @@ async function convertReceivingContainers(context, producers) {
|
||||
`plans=${existingPlans}, plannedPallets=${preview?.existingPlannedPalletCount ?? 'missing'}; ${trace(previewResponse)}`,
|
||||
OPTIONS)
|
||||
}
|
||||
const progressResponse = await context.requestOverseas('GET',
|
||||
`/api/platform/v1/last-leg/orders/container-summaries/${encodeURIComponent(summary.sourceContainerBookingId)}`,
|
||||
undefined, {}, context.overseasToken)
|
||||
const progressResponse = await context.request('GET', `/api/platform/v1/last-leg/orders/container-summaries/${encodeURIComponent(summary.sourceContainerBookingId)}`, undefined, context.token, {})
|
||||
const progress = success(progressResponse) ? progressResponse.body?.data?.summary : null
|
||||
context.addStep(MODULE, `PDA前整柜计划完整度闭环 ${containerNo}`, progressResponse,
|
||||
progress?.allPlansReady === true && Number(progress?.planCount) === totalOrders
|
||||
@@ -452,8 +432,7 @@ async function awaitPickupReadyCandidates(context, token, producers, allProducer
|
||||
let response = null
|
||||
let candidatesByContainer = new Map()
|
||||
for (let attempt = 1; attempt <= 30; attempt += 1) {
|
||||
response = await context.requestOverseas('GET',
|
||||
'/api/pda/v1/last-leg/receiving-containers/candidates', undefined, {}, token)
|
||||
response = await context.request('GET', '/api/pda/v1/last-leg/receiving-containers/candidates', undefined, token, {})
|
||||
const candidateRows = success(response) && Array.isArray(response.body?.data) ? response.body.data : []
|
||||
candidatesByContainer = new Map(candidateRows
|
||||
.map((item) => [id(item?.containerNo), item]).filter(([containerNo]) => nonBlank(containerNo)))
|
||||
@@ -499,8 +478,7 @@ export async function resolveCartonFixtures(context, token, inbounds, producerCa
|
||||
}
|
||||
const cartonFixtures = []
|
||||
for (const [index, cartonMark] of cartonCandidates.entries()) {
|
||||
const response = await context.requestOverseas('POST',
|
||||
'/api/pda/v1/last-leg/receiving-containers/current/cartons/resolve', { cartonMark }, {}, token)
|
||||
const response = await context.request('POST', '/api/pda/v1/last-leg/receiving-containers/current/cartons/resolve', { cartonMark }, token, {})
|
||||
if (!strictDropship && overseasCartons.includes(cartonMark)
|
||||
&& a0404Message(response, /^Carton mark was not found$/)) {
|
||||
context.addStep(MODULE, `排除未实装箱码候选${index + 1}`, response, true,
|
||||
@@ -669,15 +647,15 @@ async function receiveDropship(context, token, producer, inbounds) {
|
||||
}
|
||||
|
||||
async function runReceiving(context, token, secondaryToken, merchantToken, preferredProducer = null, runAuthorizationProbes = true) {
|
||||
if (runAuthorizationProbes && context.overseasToken) {
|
||||
const denied = await context.requestOverseas('GET', '/api/pda/v1/last-leg/receiving-containers/candidates', undefined, {}, context.overseasToken)
|
||||
context.addStep(MODULE, '平台管理员海外令牌不能冒充仓库PDA操作员', denied, forbidden(denied) && traced(denied), trace(denied), OPTIONS)
|
||||
if (runAuthorizationProbes && context.token) {
|
||||
const denied = await context.request('GET', '/api/pda/v1/last-leg/receiving-containers/candidates', undefined, context.token, {})
|
||||
context.addStep(MODULE, '平台管理员访问令牌不能冒充仓库PDA操作员', denied, forbidden(denied) && traced(denied), trace(denied), OPTIONS)
|
||||
}
|
||||
if (runAuthorizationProbes && merchantToken) {
|
||||
const denied = await context.requestOverseas('GET', '/api/pda/v1/last-leg/receiving-containers/candidates', undefined, {}, merchantToken)
|
||||
context.addStep(MODULE, '客户海外令牌不能冒充仓库PDA操作员', denied, forbidden(denied) && traced(denied), trace(denied), OPTIONS)
|
||||
const denied = await context.request('GET', '/api/pda/v1/last-leg/receiving-containers/candidates', undefined, merchantToken, {})
|
||||
context.addStep(MODULE, '客户访问令牌不能冒充仓库PDA操作员', denied, forbidden(denied) && traced(denied), trace(denied), OPTIONS)
|
||||
}
|
||||
const before = await context.requestOverseas('GET', '/api/pda/v1/last-leg/receiving-containers/current', undefined, {}, token)
|
||||
const before = await context.request('GET', '/api/pda/v1/last-leg/receiving-containers/current', undefined, token, {})
|
||||
const noCurrent = notFound(before)
|
||||
context.addStep(MODULE, '无当前柜返回404属于正常空态', before, noCurrent || success(before),
|
||||
`${noCurrent ? 'empty=true' : `resumeStatus=${before?.body?.data?.status || 'missing'}`}; ${trace(before)}`, OPTIONS)
|
||||
@@ -848,7 +826,7 @@ async function runReceiving(context, token, secondaryToken, merchantToken, prefe
|
||||
const completeReplay = await pda(context, token, 'POST', '完成当前柜同键同载荷重放', '/api/pda/v1/last-leg/receiving-containers/current/complete', completeBody)
|
||||
context.assert(MODULE, '完成幂等重放仍返回COMPLETED', completeReplay.response,
|
||||
success(completeReplay.response) && completeReplay.data?.status === 'COMPLETED', trace(completeReplay.response), OPTIONS)
|
||||
const after = await context.requestOverseas('GET', '/api/pda/v1/last-leg/receiving-containers/current', undefined, {}, token)
|
||||
const after = await context.request('GET', '/api/pda/v1/last-leg/receiving-containers/current', undefined, token, {})
|
||||
context.addStep(MODULE, '完成后current返回404正常空态', after, notFound(after), trace(after), OPTIONS)
|
||||
context.createdResources.phase4OverseasPda.finalStatus = completed.data?.status || null
|
||||
if (context.createdResources.phase4OverseasPda.finalStatus === 'COMPLETED') {
|
||||
|
||||
Reference in New Issue
Block a user