修正四期全量验收夹具

This commit is contained in:
2026-09-02 16:48:05 +08:00
parent 03d04431f2
commit 6cd477a037
5 changed files with 55 additions and 3 deletions
+14 -1
View File
@@ -317,7 +317,7 @@ export function parseArguments(argv) {
const modules = normalizeModuleOrder((values.get('modules') || DEFAULT_MODULES.join(',')).split(',').map((v) => v.trim()).filter(Boolean))
const merchantUsername = values.get('merchant-username') || process.env.MERCHANT_TEST_USERNAME || ''
const merchantPassword = values.get('merchant-password') || process.env.MERCHANT_TEST_PASSWORD || ''
if (merchantUsername && merchantPassword) {
if (merchantUsername && merchantPassword && !modules.includes('phase4-overseas-business')) {
const fixtureIndex = modules.indexOf('merchant-qualification-fixture')
if (fixtureIndex >= 0) modules.splice(fixtureIndex, 1)
}
@@ -1582,6 +1582,19 @@ export class AcceptanceRun {
if (this.options.waitSplitCount > 1 || this.options.waitSplitPackageCount > 0
|| this.options.orderProfileMode === 'randomized' || this.options.routeMode === 'random') {
this.applyWaitSplitVariant(this.options.waitSplitSequence || 1)
} else if (this.options.originWarehouseName || this.options.channelName || this.options.channelCode) {
this.context.waitSplitRoute = {
originWarehouseName: this.options.originWarehouseName || '',
channelName: this.options.channelName || '',
channelCode: this.options.channelCode || '',
randomize: false,
randomIndex: 0,
destinationIndex: 0,
taxModeIndex: 0,
}
// A fixed non-default origin may not carry the Yiwu-only TP-001/YW-TP-002 fixtures.
// Reuse an existing pallet from the selected warehouse while keeping order data deterministic.
this.context.allowAnyExistingPallet = true
}
const loginResponse = await this.request('POST', '/api/auth/login', { username: this.options.username, password: this.options.password }, '')
const login = this.apiSuccess('authentication', '平台管理员登录', loginResponse)
@@ -210,6 +210,8 @@ test('module is context injected, blocks missing prerequisites and excludes forb
assert.doesNotMatch(source, /https?:\/\/|process\.env|fetch\(/)
assert.doesNotMatch(source, /addCoverageGap|coverageGap\(/)
assert.match(source, /deploymentBlocker: true/)
assert.match(source, /contactPhone: '\d+'/)
assert.doesNotMatch(source, /contactPhone: '\+[^']+'/)
for (const forbidden of ['/outbounds', '/receive-pallets', '/outbound', '/delivery', '/dispatch', '/export']) {
assert.equal(source.includes(forbidden), false, forbidden)
}
@@ -56,7 +56,7 @@ function partnerWarehousePayload(context) {
return {
tenantId: 0, warehouseCode: `P4B-${suffix}`.slice(0, 50), warehouseName: `${context.marker} 采购合作仓`,
warehouseType: 'OVERSEAS_PARTNER', countryCode: 'US', timezone: 'UTC-8', contactName: 'Phase4 QA',
contactPhone: '+1-213-555-0177', contactEmail: null, status: 'ACTIVE', remark: `${context.marker}-procurement-fixture`,
contactPhone: '12135550177', contactEmail: null, status: 'ACTIVE', remark: `${context.marker}-procurement-fixture`,
address: { addressType: 'WAREHOUSE', countryCode: 'US', stateProvince: 'California', city: 'Los Angeles',
district: 'Commerce', postalCode: '90040', addressLine1: '601 Procurement Partner Avenue', addressLine2: null,
latitude: null, longitude: null },
@@ -6,6 +6,16 @@ const TRUCK = '/api/platform/v1/truck-appointments'
function id(value) { return value == null ? '' : String(value) }
function codeOf(response) { return response?.body?.code == null ? 'missing' : String(response.body.code) }
async function requestWithTransientRetry(context, method, path, body) {
let response = null
for (let attempt = 1; attempt <= 3; attempt += 1) {
response = await context.request(method, path, body)
if (response?.http !== 0) return response
if (attempt < 3) await new Promise((resolve) => setTimeout(resolve, 500 * attempt))
}
return response
}
async function actionRequest(context, path, endpoint, version) {
try {
const response = await fetch(`${context.options.gateway}${path}/${endpoint}`, {
@@ -64,7 +74,7 @@ export async function run(context) {
context.assert(MODULE, '已放舱订舱可直接创建订舱级拖车预约', oceanResponse,
id(booking?.id) === id(ocean.id) && booking?.status === 'RELEASED',
`oceanBookingId=${id(booking?.id) || 'missing'}, status=${booking?.status || 'missing'}`, OPTIONS)
const carrierResponse = await context.request('GET',
const carrierResponse = await requestWithTransientRetry(context, 'GET',
'/api/platform/v1/suppliers?tenantId=0&category=TRUCKING&status=ACTIVE&page=1&size=20')
const carrierPage = context.apiSuccess(MODULE, '读取 ACTIVE TRUCKING 承运商事实源', carrierResponse, '', OPTIONS)
const carrier = carrierPage?.records?.find?.((item) => item?.id && item?.name)
@@ -59,6 +59,33 @@ test('entry accepts a separate overseas Gateway without exposing token state', (
assert.match(source, /forbiddenValues = \[this\.options\.password, this\.options\.merchantPassword,[\s\S]*this\.token, this\.refreshToken, this\.overseasToken\]/)
})
test('overseas business keeps its marker-scoped customer fixture with reusable credentials configured', () => {
const previousUsername = process.env.MERCHANT_TEST_USERNAME
const previousPassword = process.env.MERCHANT_TEST_PASSWORD
process.env.MERCHANT_TEST_USERNAME = 'reusable-client'
process.env.MERCHANT_TEST_PASSWORD = 'reusable-password'
try {
const modules = parseArguments(['--modules', 'phase4-overseas-business']).modules
assert.ok(modules.includes('merchant-qualification-fixture'))
assert.ok(modules.indexOf('merchant-qualification-fixture') < modules.indexOf('phase4-overseas-business'))
} finally {
if (previousUsername === undefined) delete process.env.MERCHANT_TEST_USERNAME
else process.env.MERCHANT_TEST_USERNAME = previousUsername
if (previousPassword === undefined) delete process.env.MERCHANT_TEST_PASSWORD
else process.env.MERCHANT_TEST_PASSWORD = previousPassword
}
})
test('a fixed origin route does not require randomized order fixtures', () => {
const options = parseArguments(['--origin-warehouse-name', '东莞仓', '--modules', 'phase4-overseas-pda'])
assert.equal(options.originWarehouseName, '东莞仓')
assert.equal(options.routeMode, 'fixed')
assert.equal(options.orderProfileMode, 'fixed')
assert.match(source, /else if \(this\.options\.originWarehouseName \|\| this\.options\.channelName \|\| this\.options\.channelCode\)/)
assert.match(source, /this\.context\.waitSplitRoute = \{[\s\S]*randomize: false/)
assert.match(source, /this\.context\.allowAnyExistingPallet = true/)
})
test('dual-token refresh replaces the whole session only after a complete response', async () => {
const run = new AcceptanceRun({ gateway: 'http://localhost:9000', overseasGateway: 'http://localhost:9300',
username: 'contract-user', password: 'contract-password', modules: [], output: '' })