121 lines
6.2 KiB
JavaScript
121 lines
6.2 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 } from './phase4-overseas-pda-operations.mjs'
|
|
|
|
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, /merchantToken,/)
|
|
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',
|
|
'/api/pda/v1/last-leg/relabel-tasks',
|
|
'/candidates', '/claim', '/units', '/skip', '/exceptions', '/photos/files', '/complete', '/review',
|
|
]) assert.ok(source.includes(endpoint), endpoint)
|
|
for (const marker of ['按原数计数', '同键重放', '同键异载荷A0409', '旧版本A0409',
|
|
'SUBMITTED', '移回原库位', 'WAIT_REVIEW', 'REWORK', 'APPROVE', 'COMPLETED',
|
|
'chargeAmount: 0.01', 'cartonMark:', 'fbaCartonMark:',
|
|
'inventory-candidates', 'sourceInventoryBalanceId:', 'sourceInventoryPositionId:',
|
|
'sourceInboundOrderId:', 'operationWarehouseId:', 'relabelItemForUnit', 'sourceLines.map',
|
|
'const packageCount = 1', 'line.packageCount += additionalPackages', '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换标完成后钱包结算收敛']) {
|
|
assert.ok(source.includes(marker), marker)
|
|
}
|
|
for (const forbidden of ['/outbound', '/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, /locationType === 'VIRTUAL'/)
|
|
assert.match(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, /applicationStatus === 'CUSTOMER_CONFIRM_PENDING'/)
|
|
assert.match(source, /\/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' },
|
|
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.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)
|
|
})
|