Files
baiyue-firstleg/scripts/admin-test-env-modules/phase4-overseas-pda-operations-contract.test.mjs

177 lines
10 KiB
JavaScript

import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import { normalizeModuleOrder } from '../admin-test-env-acceptance.mjs'
import { run, remainingRelabelItem, selectPhysicalRelabelSources, fundRelabelWallet, RELABEL_CHARGE_AMOUNT } from './phase4-overseas-pda-operations.mjs'
test('fixed customer relabel wallet only reads existing available funds', async () => {
assert.equal(RELABEL_CHARGE_AMOUNT, 0.01)
for (const availableBalance of ['0.01', '0.00', undefined]) {
const calls = [], steps = []
const context = { options: { fixedCustomerId: '123' },
async request(method, path) {
calls.push({ method, path })
return { http: 200, body: { code: '200', data: [{ customerId: '123', accountVersion: 1, actualBalance: 100, availableBalance }] } }
},
addStep(...args) { steps.push(args) },
}
assert.equal(await fundRelabelWallet(context, '123'), availableBalance === '0.01')
assert.equal(calls.length, 1)
assert.equal(calls[0].method, 'GET')
}
})
test('relabel uses three physical cartons across current inbounds, never UNBOUND or duplicate positions', () => {
const a = { inventoryBalanceId: 'A', inboundOrderId: '1', cartons: [
{ cartonMark: 'A1', positionId: '10' }, { cartonMark: 'A2', positionId: '10' }, { cartonMark: 'A3', positionId: '11' }], positions: [
{ positionId: '10', palletNo: 'REAL-A', availablePackages: 2 },
{ positionId: '11', palletNo: 'UNBOUND', availablePackages: 1 }] }
const b = { inventoryBalanceId: 'B', inboundOrderId: '2', cartons: [{ cartonMark: 'B1', positionId: '20' }], positions: [
{ positionId: '20', palletNo: 'REAL-B', availablePackages: 1 }] }
const selected = selectPhysicalRelabelSources([b, a, a], 'A')
assert.deepEqual(selected.map(line => line.position.palletNo), ['REAL-A', 'REAL-A', 'REAL-B'])
assert.equal(selectPhysicalRelabelSources([a, a], 'A').length, 2)
assert.equal(selectPhysicalRelabelSources([{ ...a, cartons: [] }], 'A').length, 0)
})
test('exception uses the untouched third carton, independent of response ordering', () => {
const third = { itemId: '3', expectedPackages: 1 }
const detail = { items: [third, { itemId: '2' }, { itemId: '1' }] }
assert.equal(remainingRelabelItem(detail, [1, '2']), third)
assert.equal(remainingRelabelItem(detail, ['1', '2', '3']), null)
assert.equal(remainingRelabelItem(null, []), null)
})
const here = path.dirname(fileURLToPath(import.meta.url))
const source = fs.readFileSync(path.join(here, 'phase4-overseas-pda-operations.mjs'), 'utf8')
const receivingSource = fs.readFileSync(path.join(here, 'phase4-overseas-pda.mjs'), 'utf8')
const entrySource = fs.readFileSync(path.join(here, '..', 'admin-test-env-acceptance.mjs'), 'utf8')
test('PDA operations run immediately after receiving and all later consumers depend on cleanup completion', () => {
const modules = normalizeModuleOrder(['phase4-overseas-client-services'])
const receiving = modules.indexOf('phase4-overseas-pda')
const operations = modules.indexOf('phase4-overseas-pda-operations')
const consumer = modules.indexOf('phase4-overseas-client-services')
assert.ok(receiving >= 0 && operations === receiving + 1 && consumer > operations)
assert.match(entrySource, /'phase4-overseas-pda-operations', \['phase4-overseas-pda'\]/)
for (const module of ['phase4-overseas-inbound-operations', 'phase4-overseas-client-services',
'phase4-overseas-dispatch', 'phase4-cross-region-events', 'phase4-carrier-nodes']) {
assert.match(entrySource, new RegExp(`'${module}', \\[[^\\]]*'phase4-overseas-pda-operations'`))
}
})
test('receiving hands off only transient credentials and completed warehouse facts', () => {
assert.match(receivingSource, /context\.context\.phase4OverseasPdaOperations = \{/)
assert.match(receivingSource, /completedFixtures: \[\]/)
assert.match(receivingSource, /resourceKey: producerFixture\?\.resourceKey \|\| null/)
assert.match(receivingSource, /merchantToken,/)
assert.match(receivingSource, /const refreshedMerchantToken = await loginMerchantForNegativeProbe\(context\)/)
assert.match(receivingSource, /phase4OverseasPdaOperations\.merchantToken = refreshedMerchantToken/)
assert.match(receivingSource, /if \(!handoffReady\) \{[\s\S]*await cleanup\(\)/)
assert.doesNotMatch(receivingSource, /createdResources\.phase4OverseasPdaOperations/)
assert.doesNotMatch(receivingSource, /primary: primary \? \{[^}]*password/)
assert.doesNotMatch(entrySource.match(/const reportSurface = JSON\.stringify\(\{([\s\S]*?)\n \}\)/)?.[1] || '',
/context/)
})
test('P2 to P5 cover all frozen endpoints, replays and conflict boundaries without outbound domains', () => {
for (const endpoint of [
'/api/pda/v1/last-leg/stocktakes',
'/api/pda/v1/last-leg/inventory-moves',
'/api/pda/v1/last-leg/pallet-operations/pallets/resolve',
'/swap', '/disassemble', '/assemble', '/unbind', '/relabel-pallet-label', '/relabel-disassemble',
'/api/pda/v1/last-leg/relabel-tasks',
'/candidates', '/claim', '/units', '/skip', '/exceptions', '/photos/files', '/complete', '/rework',
]) assert.ok(source.includes(endpoint), endpoint)
for (const marker of ['按原数计数', '同键重放', '同键异载荷A0409', '旧版本A0409',
'SUBMITTED', '移回原库位', 'WAIT_REVIEW', 'REWORK', 'COMPLETED',
'chargeAmount: RELABEL_CHARGE_AMOUNT', 'cartonMark:', 'fbaCartonMark:',
'readCurrentBoxedInventory', 'sourceInventoryBalanceId:', 'sourceInventoryPositionId:',
'sourceInboundOrderId:', 'operationWarehouseId:', 'relabelItemForUnit', 'sourceLines.map',
'sourceLines.push({ source, position, carton, packageCount: 1 })', 'oldLabel: item.cartonMark',
'id(candidate.itemId) !== id(item.itemId)', 'oldLabel: null',
'newLabel: item.fbaCartonMark || item.newSkuCode',
"operationType: changesCartonLabel ? 'CARTON_AND_SKU' : 'SKU_LABEL'", 'skuItems:',
"deliveryMethod: 'TRUCK'", 'printShippingLabel: false',
'P5换标费用冻结后开放PDA作业', 'P5换标完成后钱包结算收敛', 'CUSTOMER_CONFIRM_PENDING', 'confirm-completion',
'P5库存流水记录换标拆托原因和正负变动', 'phase4OverseasRelabel']) {
assert.ok(source.includes(marker), marker)
}
for (const forbidden of ['/returns/outbound', '/dispatch', '/delivery', '/internal/']) {
assert.equal(source.includes(forbidden), false, forbidden)
}
assert.doesNotMatch(source, /fetch\(|https?:\/\//)
assert.match(source, /requestId=/)
assert.match(source, /traceId=/)
})
test('PDA operations reuse received plan pallets and the public location without creating resources', () => {
assert.match(receivingSource,
/auxiliaryPallets: generatedContainerPallets\.filter/)
assert.match(source, /resolvePublicLocation/)
assert.match(source, /resolveReusableAuxiliaryPallet/)
assert.match(source, /\['STORAGE', 'NORMAL'\]\.includes\(item\?\.locationType\)/)
assert.match(source, /status === 'IDLE' && item\?\.locationCode !== sourceLocationCode/)
assert.doesNotMatch(source, /P3托盘已在公共库位不重复移库/)
assert.doesNotMatch(source, /warehouse-admin[^'`]*\/locations`,\s*\{/)
assert.doesNotMatch(source, /warehouse-admin[^'`]*\/pallets`,\s*\{/)
assert.doesNotMatch(source, /createLocation|createEmptyPallet/)
})
test('P5 follows warehouse review, customer confirmation and fulfillment completion in order', () => {
assert.match(source, /request\(context, context.token, 'POST', 'P5平台复核退回REWORK'/)
assert.doesNotMatch(source, /decision: 'APPROVE'/)
assert.match(source, /manualCompletion: true/)
assert.match(source, /P5返工重新确认箱标阶段/)
assert.match(source, /P5返工重新完成SKU阶段/)
assert.match(source, /newLabel: redoItem\.fbaCartonMark \|\| redoItem\.newSkuCode/)
assert.ok(source.indexOf('P5换标箱${index + 1}拆托') < source.indexOf('P5返工组托后提交完工等待客户确认'))
assert.ok(source.indexOf('P5返工组托后提交完工等待客户确认') < source.indexOf('const confirmBody'))
assert.match(source, /applicationStatus === 'CUSTOMER_CONFIRM_PENDING'/)
assert.match(source, /const merchantRelabelBase = '\/api\/merchant\/v1\/last-leg\/relabel-applications'/)
assert.match(source, /`\$\{merchantRelabelBase\}\/\$\{encodeURIComponent\(relabelNo\)\}\/confirm-completion`/)
assert.match(source, /status === 'READY_FOR_OUTBOUND'/)
assert.match(source, /status: 'COMPLETED'/)
assert.match(source, /walletStatus === 'SETTLED'/)
})
test('fixture failure still disables both picker accounts and removes all transient tokens', async () => {
const cleanups = []
const gaps = []
const steps = []
const handoff = {
marker: 'PDAOPS-CONTRACT',
primary: { membershipId: '1', username: 'primary', token: 'primary-secret-token',
refreshToken: 'primary-secret-refresh', password: 'primary-secret-password', userUpdate: { phone: '13900000001' } },
secondary: { membershipId: '2', username: 'secondary', token: 'secondary-secret-token' },
merchantToken: 'merchant-secret-token',
completedFixtures: [],
async cleanup() { cleanups.push('secondary', 'primary') },
}
const context = {
marker: 'PDAOPS-CONTRACT', context: { phase4OverseasPdaOperations: handoff },
createdResources: {},
addCoverageGap(...args) { gaps.push(args) },
assert(module, name, response, passed, evidence, options) {
steps.push({ module, name, passed, evidence, options })
},
}
await run(context)
assert.deepEqual(cleanups, ['secondary', 'primary'])
assert.equal(handoff.primary.token, undefined)
assert.equal(handoff.primary.refreshToken, undefined)
assert.equal(handoff.primary.password, undefined)
assert.equal(handoff.primary.userUpdate, undefined)
assert.equal(handoff.secondary.token, undefined)
assert.equal(handoff.merchantToken, undefined)
assert.equal(context.context.phase4OverseasPdaOperations, undefined)
assert.ok(gaps.some((gap) => gap[2] === 'P2-P5本轮PDA收货交接'))
assert.ok(steps.some((step) => !step.passed && step.options?.deploymentBlocker))
assert.equal(JSON.stringify(context).includes('secret-token'), false)
})