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:
2026-09-02 20:32:23 +08:00
28 changed files with 1412 additions and 58 deletions
@@ -105,7 +105,20 @@ async function loginMerchant(context, activeCustomer) {
async function createMerchantProcurement(context, activeCustomer, overseasToken) {
const path = '/api/merchant/v1/last-leg/overseas-procurements'
const payload = procurementPayload(context, activeCustomer)
const response = await context.requestOverseas('POST', path, payload, {}, overseasToken)
let activeToken = overseasToken
let response = await context.requestOverseas('POST', path, payload, {}, activeToken)
// A newly approved customer is projected to the overseas region asynchronously. The
// domestic login can therefore succeed a few seconds before the overseas gateway has
// received the matching USER/TENANT/AUTHORIZATION versions. Retry only that exact
// security-projection conflict and always issue a fresh short-lived overseas token.
for (let attempt = 1; attempt <= 12
&& response?.http === 403 && codeOf(response) === 'A0403'; attempt += 1) {
await new Promise((resolve) => setTimeout(resolve, 1_000))
const refreshedToken = await loginMerchant(context, activeCustomer)
if (!refreshedToken) break
activeToken = refreshedToken
response = await context.requestOverseas('POST', path, payload, {}, activeToken)
}
const detail = context.apiSuccess(MODULE, '创建本轮客户采购订单', response, trace(response), OPTIONS)
const value = summary(detail)
const resource = nonEmpty(value?.orderNo) && longId(value?.customerId)
@@ -119,7 +132,7 @@ async function createMerchantProcurement(context, activeCustomer, overseasToken)
`orderNo=${id(value?.orderNo) || 'missing'}, customerId=${id(value?.customerId) || 'missing'}, status=${value?.status || 'missing'}, version=${value?.version ?? 'missing'}; ${trace(response)}`, OPTIONS)
if (!valid) return null
const replayResponse = await context.requestOverseas('POST', path, payload, {}, overseasToken)
const replayResponse = await context.requestOverseas('POST', path, payload, {}, activeToken)
const replayDetail = context.apiSuccess(MODULE, '采购创建同幂等键同载荷精确重放', replayResponse, trace(replayResponse), OPTIONS)
const replay = summary(replayDetail)
context.assert(MODULE, '采购幂等重放复用订单且版本不漂移', replayResponse,
@@ -127,7 +140,7 @@ async function createMerchantProcurement(context, activeCustomer, overseasToken)
`orderNo=${id(replay?.orderNo) || 'missing'}, version=${replay?.version ?? 'missing'}; ${trace(replayResponse)}`, OPTIONS)
const mismatch = await context.requestOverseas('POST', path,
{ ...payload, productName: `${payload.productName}-DIFFERENT` }, {}, overseasToken)
{ ...payload, productName: `${payload.productName}-DIFFERENT` }, {}, activeToken)
expectedConflict(context, '采购创建同幂等键异载荷返回冲突', mismatch)
return { resource, payload }