fix(test): resolve loading customer credentials per run
This commit is contained in:
@@ -46,6 +46,8 @@
|
||||
|
||||
`phase3-loading.mjs` 的 PDA 原子证据上传在未取得 HTTP 响应时最多尝试三次,冻结原 URL、作业员身份、幂等键、文件名/内容、证据版本和封签号;不重试 HTTP/业务拒绝或已收到响应后的读取失败。`createdResources.loadingEvidenceTransport` 保存各次结果及白名单网络错误码,不保存凭据或原始错误对象。`phase3-evidence-transport-contract.test.mjs` 用持久幂等模型验证响应丢失重放仅一条证据、持续传输失败有界终止及拒绝不重试;离线模型不替代真实 HTTP。
|
||||
|
||||
装柜模块每次执行时读取当前客户凭据,依次选本轮 `merchantQualificationFixture`、显式客户参数、显式加载的环境变量;选中的身份缺用户名或密码时在业务请求前停止,不能借另一客户补齐。不得在模块导入时缓存客户账号;`phase3-loading-credentials.test.mjs` 覆盖同进程切换客户、显式配置、当前环境读取及缺凭据写前拒绝。
|
||||
|
||||
`orders.mjs` 每次运行优先使用当前 `merchantQualificationFixture` 的完整账号对,其次为明确配置;不在模块 import 时缓存客户凭据。当前 fixture 不完整时停止,不与上一轮或环境账号拼接。`orders-session-contract.test.mjs` 在同进程重复调用真实订单登录入口,覆盖连续客户隔离,防止多模式串行时沿用前一轮身份。
|
||||
|
||||
`phase4-overseas-pda.mjs` 的收货入口对本轮 `containerBookingId + containerNo` 做有界候选收敛等待(最多 30 次、间隔 1 秒),只有唯一且可操作的原柜才继续;鉴权失败、畸形响应和重复候选立即阻断。它用于等待异步船节点投影,不构成事件重放证据。F40 在成功和失败时均保留 `receivingCandidateEvidence` 的逐次查询身份、结果和诊断信息,不覆盖已有收货会话,不借其他柜或历史柜继续。
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import * as loading from './phase3-loading.mjs'
|
||||
|
||||
test('cached loading module resolves each fresh customer independently', () => {
|
||||
const context={context:{merchantQualificationFixture:{username:'customer-a',password:'test-a'}},options:{merchantUsername:'configured',merchantPassword:'configured-test'}}
|
||||
assert.deepEqual(loading.loadingMerchantCredentials(context),{username:'customer-a',password:'test-a'})
|
||||
context.context.merchantQualificationFixture={username:'customer-b',password:'test-b'}
|
||||
assert.deepEqual(loading.loadingMerchantCredentials(context),{username:'customer-b',password:'test-b'})
|
||||
})
|
||||
|
||||
test('explicit credentials take precedence; environment is read at invocation', () => {
|
||||
const env={MERCHANT_TEST_USERNAME:'env-a',MERCHANT_TEST_PASSWORD:'test-env-a'}
|
||||
assert.deepEqual(loading.loadingMerchantCredentials({options:{merchantUsername:'configured',merchantPassword:'test-configured'}},env),{username:'configured',password:'test-configured'})
|
||||
assert.deepEqual(loading.loadingMerchantCredentials({},env),{username:'env-a',password:'test-env-a'})
|
||||
env.MERCHANT_TEST_USERNAME='env-b';env.MERCHANT_TEST_PASSWORD='test-env-b'
|
||||
assert.deepEqual(loading.loadingMerchantCredentials({},env),{username:'env-b',password:'test-env-b'})
|
||||
})
|
||||
|
||||
test('incomplete selected identity cannot fall back to another customer', () => {
|
||||
const env={MERCHANT_TEST_USERNAME:'unrelated',MERCHANT_TEST_PASSWORD:'unrelated-test'}
|
||||
for(const context of [{context:{merchantQualificationFixture:{username:'current'}}},{options:{merchantPassword:'orphan-test'}}]) {
|
||||
assert.throws(()=>loading.loadingMerchantCredentials(context,env),/完整客户登录凭据/)
|
||||
}
|
||||
})
|
||||
|
||||
test('loading stops before business requests when its current customer lacks credentials', async () => {
|
||||
let requests=0
|
||||
await assert.rejects(loading.run({context:{merchantQualificationFixture:{username:'missing-password'}},options:{},createdResources:{},addStep(){},request:async()=>{requests++;throw Error('business request must not run')}}),/完整客户登录凭据/)
|
||||
assert.equal(requests,0)
|
||||
})
|
||||
@@ -7,12 +7,19 @@ import { createHash } from 'node:crypto'
|
||||
|
||||
const MODULE = 'phase3-loading'
|
||||
const OPTIONS = { scenario: 'phase3-split-preload-loading', deploymentBlocker: true }
|
||||
const MERCHANT_USERNAME = process.env.MERCHANT_TEST_USERNAME || ''
|
||||
const MERCHANT_PASSWORD = process.env.MERCHANT_TEST_PASSWORD || ''
|
||||
const MATSON_SUPPLIER_NAME = 'MATSON 开发联调船司'
|
||||
const LATE_CONTAINER_GATE_ONLY = process.env.LATE_CONTAINER_GATE_ONLY === '1'
|
||||
|
||||
function id(value) { return value == null ? '' : String(value) }
|
||||
export function loadingMerchantCredentials(context, environment = process.env) {
|
||||
const fixture = context.context?.merchantQualificationFixture
|
||||
const options = context.options || {}
|
||||
const selected = fixture || (options.merchantUsername || options.merchantPassword
|
||||
? { username: options.merchantUsername, password: options.merchantPassword }
|
||||
: { username: environment.MERCHANT_TEST_USERNAME, password: environment.MERCHANT_TEST_PASSWORD })
|
||||
if (!selected.username || !selected.password) throw new Error('装柜验收当前运行缺少完整客户登录凭据')
|
||||
return { username: selected.username, password: selected.password }
|
||||
}
|
||||
export function loadingBookingCustomerFields(scope = 'CUSTOMER', customerId, customerName) {
|
||||
if (scope === 'PLATFORM') return {}
|
||||
if (scope !== 'CUSTOMER') throw new Error('三期订舱范围必须明确为 CUSTOMER 或 PLATFORM')
|
||||
@@ -184,6 +191,7 @@ function taskItemsSignature(task) {
|
||||
}
|
||||
|
||||
export async function run(context) {
|
||||
const merchantCredentials = loadingMerchantCredentials(context)
|
||||
const source = fixture(context)
|
||||
const tenantId = id(source?.tenantId)
|
||||
const inboundId = id(source?.inboundId)
|
||||
@@ -940,12 +948,12 @@ export async function run(context) {
|
||||
}
|
||||
|
||||
const merchantLogin = await context.request('POST', '/api/auth/login', {
|
||||
loginId: MERCHANT_USERNAME, password: MERCHANT_PASSWORD,
|
||||
loginId: merchantCredentials.username, password: merchantCredentials.password,
|
||||
}, '')
|
||||
const merchantToken = merchantLogin.http === 200 && codeOf(merchantLogin) === '200'
|
||||
? merchantLogin.body?.data?.accessToken : null
|
||||
context.addStep(MODULE, '取得客户侧隔离令牌用于 PDA 批次越权验证', merchantLogin,
|
||||
Boolean(merchantToken), `username=${MERCHANT_USERNAME}`, OPTIONS)
|
||||
Boolean(merchantToken), 'current-run customer credentials', OPTIONS)
|
||||
const customerIsolatedBatch = merchantToken
|
||||
? await request(context, 'POST', batchPath, batchPayload, {
|
||||
Authorization: `Bearer ${merchantToken}`,
|
||||
|
||||
Reference in New Issue
Block a user