Merge remote-tracking branch 'origin/test' into dev
# Conflicts: # scripts/admin-test-env-acceptance.mjs # scripts/agents-index-contract.ps1 # scripts/full-regression-entry-contract.test.mjs
This commit is contained in:
@@ -5,7 +5,7 @@ import { dirname, resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import process from 'node:process'
|
||||
|
||||
const CONFIGURED_MODULES = ['merchant-qualification-fixture', 'customers', 'overseas-storage-fees', 'system', 'notifications', 'master-data', 'suppliers', 'domestic-container', 'orders', 'fee-items', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'inbound-fees', 'phase3-container-loading', 'phase3-container-overview', 'phase3-loading', 'phase3-loading-model-bridge', 'phase3-pda-convergence', 'phase3-evidence-gate', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow', 'phase3-loading-finalize', 'phase3-container-documents', 'phase3-customs', 'phase3-container-customs', 'phase3-port-schedules', 'phase3-carrier-schedule-queries', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate', 'phase3-dispatch-gate', 'phase3-waiting-for-loading', 'phase3-warehouse-rbac', 'phase3-order-splitting', 'manual-state-matrix', 'phase4-overseas-warehouse', 'phase4-carrier-pickup-ready', 'phase4-overseas-pda', 'phase4-overseas-pda-operations', 'phase4-overseas-inbound-operations', 'phase4-overseas-client-services', 'phase4-overseas-dispatch', 'phase5-overseas-fulfillment', 'phase4-cross-region-events', 'phase4-overseas-business', 'phase4-overseas-master-data', 'phase4-carrier-nodes', 'finance', 'finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full', 'finance-downloads', 'customer-operating-statistics']
|
||||
const CONFIGURED_MODULES = ['merchant-qualification-fixture', 'customers', 'overseas-storage-fees', 'system', 'notifications', 'master-data', 'suppliers', 'domestic-container', 'orders', 'fee-items', 'pricing', 'downloads', 'inbound-weight-ocr', 'inbound-measurement', 'inbound-fees', 'phase3-container-loading', 'phase3-container-overview', 'phase3-loading', 'phase3-loading-model-bridge', 'phase3-pda-convergence', 'phase3-evidence-gate', 'supplemental', 'supplemental-boundaries', 'ocean-bookings', 'truck-appointments', 'service-center-flow', 'phase3-loading-finalize', 'phase3-container-documents', 'phase3-customs', 'phase3-container-customs', 'phase3-port-schedules', 'phase3-carrier-schedule-queries', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate', 'phase3-dispatch-gate', 'phase3-waiting-for-loading', 'phase3-warehouse-rbac', 'phase3-order-splitting', 'manual-state-matrix', 'phase4-overseas-warehouse', 'phase4-carrier-pickup-ready', 'phase4-overseas-pda', 'phase4-overseas-pda-operations', 'phase4-overseas-inbound-operations', 'phase4-overseas-client-services', 'phase4-overseas-dispatch', 'phase5-overseas-fulfillment', 'phase4-cross-region-events', 'phase4-overseas-business', 'phase4-overseas-master-data', 'phase4-carrier-nodes', 'finance', 'finance-full-fixtures', 'finance-reconciliation-full', 'finance-settlement-full', 'finance-downloads', 'firstleg-container-fee-allocation', 'finance-direct-paid-isolation', 'phase4-overseas-finance-cross-region', 'customer-operating-statistics']
|
||||
const OPTIONAL_MODULES = ['manual-order-splitting-fixture', 'manual-order-splitting-verify',
|
||||
'manual-preload-push-fixture', 'manual-preload-push-verify',
|
||||
'manual-pda-ready-fixture', 'manual-pda-ready-verify',
|
||||
@@ -78,6 +78,10 @@ const MODULE_DEPENDENCIES = new Map([
|
||||
['phase5-overseas-fulfillment', ['phase4-overseas-dispatch']],
|
||||
['phase4-cross-region-events', ['system', 'phase3-customs', 'phase4-overseas-pda-operations']],
|
||||
['phase4-carrier-nodes', ['phase4-overseas-pda-operations']],
|
||||
['phase4-overseas-business', ['merchant-qualification-fixture']],
|
||||
['firstleg-container-fee-allocation', []],
|
||||
['finance-direct-paid-isolation', ['finance-full-fixtures', 'finance-reconciliation-full']],
|
||||
['phase4-overseas-finance-cross-region', ['phase4-overseas-business', 'phase4-overseas-pda-operations']],
|
||||
])
|
||||
|
||||
function stableTopologicalOrder(modules) {
|
||||
@@ -242,7 +246,7 @@ export function normalizeModuleOrder(modules) {
|
||||
const missing = dependencyClosure(module).filter((dependency) => !normalized.includes(dependency))
|
||||
if (missing.length > 0) normalized.splice(normalized.indexOf(module), 0, ...missing)
|
||||
}
|
||||
return stableTopologicalOrder(normalized)
|
||||
return stableTopologicalOrder([...new Set(normalized)])
|
||||
}
|
||||
|
||||
const DEFAULT_MODULES = normalizeModuleOrder(CONFIGURED_MODULES)
|
||||
@@ -315,7 +319,9 @@ export function parseArguments(argv) {
|
||||
values.set(name, next); index += 1
|
||||
}
|
||||
}
|
||||
const modules = normalizeModuleOrder((values.get('modules') || DEFAULT_MODULES.join(',')).split(',').map((v) => v.trim()).filter(Boolean))
|
||||
const requestedModules = (values.get('modules') || DEFAULT_MODULES.join(','))
|
||||
.split(',').map((v) => v.trim()).filter(Boolean)
|
||||
const modules = normalizeModuleOrder(requestedModules)
|
||||
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 && !modules.includes('phase4-overseas-business')) {
|
||||
@@ -328,6 +334,7 @@ export function parseArguments(argv) {
|
||||
username: values.get('username') || process.env.ADMIN_TEST_USERNAME || '',
|
||||
password: values.get('password') || process.env.ADMIN_TEST_PASSWORD || '',
|
||||
modules,
|
||||
requestedModules,
|
||||
customerName: values.get('customer-name') || process.env.ADMIN_TEST_CUSTOMER_NAME || '',
|
||||
merchantUsername,
|
||||
merchantPassword,
|
||||
@@ -401,9 +408,9 @@ export function validateExecutionOptions(options) {
|
||||
}
|
||||
if (options.waitSplitCount > 1) {
|
||||
const fixtureModules = new Set(['merchant-qualification-fixture', 'orders', 'inbound-measurement',
|
||||
'manual-order-splitting-fixture', 'coload-groups'])
|
||||
'manual-order-splitting-fixture', 'coload-groups', 'firstleg-container-fee-allocation'])
|
||||
if (options.modules.some((module) => !fixtureModules.has(module))) {
|
||||
throw new Error('批量WAIT_SPLIT只允许执行merchant-qualification-fixture、orders、inbound-measurement、manual-order-splitting-fixture')
|
||||
throw new Error('批量WAIT_SPLIT只允许执行WAIT_SPLIT造数、同柜组与柜次跨客户分摊模块')
|
||||
}
|
||||
}
|
||||
if (options.modules.includes('manual-order-splitting-fixture') && options.waitSplitCount < 4) {
|
||||
@@ -526,23 +533,31 @@ export class AcceptanceRun {
|
||||
const requestBody = path === '/api/auth/login' && body?.username && !body?.loginId
|
||||
? { ...body, loginId: body.username, username: undefined }
|
||||
: body
|
||||
try {
|
||||
const response = await fetch(`${this.options.gateway}${path}`, {
|
||||
method, headers, body: requestBody === undefined ? undefined : JSON.stringify(requestBody),
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
})
|
||||
const content = await response.text()
|
||||
let payload = null
|
||||
try { payload = content ? JSON.parse(content) : null } catch { /* Raw response is recorded below. */ }
|
||||
return {
|
||||
method, path, http: response.status, body: payload,
|
||||
raw: payload === null ? content.slice(0, 500) : null,
|
||||
requestId: payload?.requestId || response.headers.get('x-request-id'),
|
||||
traceId: payload?.traceId || response.headers.get('x-trace-id'),
|
||||
const maxAttempts = ['GET', 'HEAD'].includes(String(method).toUpperCase()) ? 3 : 1
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
try {
|
||||
const response = await fetch(`${this.options.gateway}${path}`, {
|
||||
method, headers, body: requestBody === undefined ? undefined : JSON.stringify(requestBody),
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
})
|
||||
const content = await response.text()
|
||||
let payload = null
|
||||
try { payload = content ? JSON.parse(content) : null } catch { /* Raw response is recorded below. */ }
|
||||
return {
|
||||
method, path, http: response.status, body: payload,
|
||||
raw: payload === null ? content.slice(0, 500) : null,
|
||||
requestId: payload?.requestId || response.headers.get('x-request-id'),
|
||||
traceId: payload?.traceId || response.headers.get('x-trace-id'),
|
||||
}
|
||||
} catch (error) {
|
||||
if (attempt < maxAttempts) {
|
||||
await new Promise((resolve) => setTimeout(resolve, attempt * 250))
|
||||
continue
|
||||
}
|
||||
return { method, path, http: 0, body: null, raw: error instanceof Error ? error.message : String(error), requestId: null, traceId: null }
|
||||
}
|
||||
} catch (error) {
|
||||
return { method, path, http: 0, body: null, raw: error instanceof Error ? error.message : String(error), requestId: null, traceId: null }
|
||||
}
|
||||
throw new Error('unreachable request attempt state')
|
||||
}
|
||||
async requestOverseas(method, path, body = undefined, extraHeaders = {}, token = this.overseasToken) {
|
||||
const headers = { Accept: 'application/json', ...extraHeaders }
|
||||
@@ -1332,6 +1347,7 @@ export class AcceptanceRun {
|
||||
internalRemark: fixtureOrder.internalRemark,
|
||||
cargoRemark: fixtureOrder.cargoRemark,
|
||||
taxMode: fixtureOrder.taxMode || null,
|
||||
requireInventoryMutation: requireSharedOverflow,
|
||||
} : { marker: originalMarker, sourceMarker: fixtureMarker, failed: true }
|
||||
const incrementalMarker = `${originalMarker}-LOAD${suffix}-INC`
|
||||
this.marker = incrementalMarker
|
||||
@@ -1738,7 +1754,11 @@ export class AcceptanceRun {
|
||||
}
|
||||
if (module === 'phase3-loading') {
|
||||
try {
|
||||
restoreFixture = await this.preparePhase3LoadingFixture()
|
||||
// The A0 stale-inventory mutation is intentionally destructive and expensive on
|
||||
// a large shared test warehouse. Run it only when phase3-loading was explicitly
|
||||
// requested; downstream phase4/finance chains need the normal loading fixture only.
|
||||
const requireSharedOverflow = this.options.requestedModules.includes('phase3-loading')
|
||||
restoreFixture = await this.preparePhase3LoadingFixture('', requireSharedOverflow)
|
||||
} catch (error) {
|
||||
this.marker = this.marker.replace(/-LOAD$/, '')
|
||||
const reason = error instanceof Error ? error.message : String(error)
|
||||
|
||||
Reference in New Issue
Block a user