This commit is contained in:
2026-10-27 17:44:41 +08:00
parent 57cd211973
commit db0e72daeb
37 changed files with 701 additions and 671 deletions
+23 -32
View File
@@ -1,10 +1,12 @@
import { openFixedActor, requireFixedActors, POOL_ACTOR_SLOTS } from './admin-test-env-modules/fixed-test-actors.mjs'
import { mkdirSync, openSync, writeFileSync, fsyncSync, closeSync, readFileSync } from 'node:fs'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
import { createHash, randomUUID } from 'node:crypto'
import { createHash } from 'node:crypto'
import { AcceptanceRun } from './admin-test-env-acceptance.mjs'
import { normalizeFixedCustomerId } from './admin-test-env-modules/merchant-qualification-fixture.mjs'
import { parseCrossCustomerArguments, runCrossCustomer } from './fleet-cross-customer-test-env.mjs'
import { guardFleetWrites, redactFleetWrite, findFleetActorPhone } from './fleet-test-env-acceptance.mjs'
import { guardFleetWrites, redactFleetWrite } from './fleet-test-env-acceptance.mjs'
import { confirmFleetLoading } from './admin-test-env-modules/fleet-trip-fulfillment.mjs'
import { testPlannedCapacity } from './admin-test-env-modules/fleet-booking-reservation-acceptance.mjs'
import { pickAll, saveReceiptAndComplete, readAutomaticDispatchDelivery } from './admin-test-env-modules/phase5-overseas-fulfillment.mjs'
@@ -291,25 +293,14 @@ async function poolScenario(context, fixture, { execute = false, readInventory,
check(label, denied(response) && response.requestId && response.traceId, response)
return response
}
const permissions = await request('GET', `${IAM}/permissions`)
requireFixedActors(context.options, POOL_ACTOR_SLOTS)
const actorSessions = [], merchantSessions = []
const businessPath = order => `${API}/${order.businessType === 'US_TRUCK' ? 'us-truck-orders' : 'local-looseboard-orders'}/${encode(order.businessOrderNo)}?customerId=${encode(order.customerId)}`
const createActor = async (kind, index, codes) => {
const suffix = createHash('sha256').update(`${context.marker}:${kind}:${index}`).digest('hex').slice(0, 16)
const found = codes.map(code => permissions.filter(p => p.permissionCode === code && p.status === 'ACTIVE'))
check('Exact pool permissions exist', found.every(rows => rows.length === 1 && rows[0].id))
const role = await request('POST', `${IAM}/roles`, { roleCode: `POOL_${kind}_${suffix}`.toUpperCase(), roleName: `${context.marker}-${kind}-${index}`, description: context.marker })
const actor = { kind, index, roleId: id(role.id) }; proof.actors.push(actor)
await request('PUT', `${IAM}/roles/permissions`, { roleId: actor.roleId, permissionIds: found.map(rows => id(rows[0].id)) })
const username = `pool_${kind}_${suffix}`, password = `Pool${randomUUID().replaceAll('-', '')}A9`
const user = await request('POST', `${IAM}/users`, { username, displayName: `${context.marker}-同名测试人员`,
phone: await findFleetActorPhone(context, suffix), email: `${username}@example.com`, tenantId: '0', roleIds: [actor.roleId], password, status: 'ACTIVE' })
actor.membershipId = id(user.membershipId)
const login = await request('POST', '/api/auth/login', { loginId: username, password }, '')
actorSessions.push({ accessToken: login.accessToken, refreshToken: login.refreshToken })
const effective = await request('GET', `${IAM}/authorization/permissions`, undefined, login.accessToken)
check('Temporary actor has exact permissions', Array.isArray(effective) && JSON.stringify([...effective].sort()) === JSON.stringify([...codes].sort()))
return login.accessToken
const session = await openFixedActor(context, `pool.${kind}.${index}`, codes)
actorSessions.push(session)
proof.actors.push({ kind, index, roleId: session.roleId, membershipId: session.membershipId, username: session.username, reused: true })
return session.token
}
try {
const sales = [], dispatchers = []
@@ -531,11 +522,8 @@ async function poolScenario(context, fixture, { execute = false, readInventory,
return proof
} catch (error) { proof.status = 'FAILED'; throw error }
finally {
for (const actor of proof.actors) {
if (actor.membershipId) await request('PUT', `${IAM}/users/${actor.membershipId}/status`, { status: 'DISABLED' })
if (actor.roleId) await request('PUT', `${IAM}/roles/${actor.roleId}/status`, { status: 'DISABLED' })
}
// The outer runner tracks and logs out all successful login sessions, including these.
const closed = await Promise.allSettled(actorSessions.map(actor => actor.close()))
if (closed.some(row => row.status === 'rejected')) throw Error('固定拼车账号会话退出未全部确认')
actorSessions.length = 0; merchantSessions.length = 0
}
}
@@ -653,14 +641,12 @@ export async function runPoolAcceptance(config, { env = process.env, outputRoot
try {
const result = await runner(config, { env, outputRoot, verificationScope: 'CAPACITY_POOL',
contextFactory: options => {
const context = new AcceptanceRun(options), original = context.request
context.request = async function (...args) {
const response = await original.apply(this, args)
if (args[0] === 'POST' && args[1] === `${IAM}/tenants` && success(response)) {
const data = response.body.data, customer = data.tenant || data.customer || data
credentials.set(id(customer.id || data.tenantId), { username: data.administratorUsername, password: data.temporaryPassword })
}
return response
const context = new AcceptanceRun(options)
requireFixedActors(context.options, POOL_ACTOR_SLOTS)
for (const customer of [{ customerId: context.options.fixedCustomerId, username: context.options.merchantUsername,
password: context.options.merchantPassword }, context.options.fixedSecondaryCustomer]) {
if (customer?.customerId && customer.username && customer.password)
credentials.set(normalizeFixedCustomerId(customer.customerId), { username: customer.username, password: customer.password })
}
const directory = join(outputRoot, 'reports', poolReportName(config), 'pool-write-journal')
mkdirSync(directory)
@@ -670,7 +656,12 @@ export async function runPoolAcceptance(config, { env = process.env, outputRoot
}, { guardFetch: true })
return context
},
verify: (context, fixture, options) => poolScenario(context, fixture, { ...options, credentials }) })
verify: (context, fixture, options) => {
requireThat(fixture?.members?.length === 2 && new Set(fixture.members.map(member => id(member.customerId))).size === 2
&& fixture.members.every(member => credentials.has(id(member.customerId))),
'CURRENT_FIXED_POOL_CUSTOMER_CREDENTIALS_REQUIRED')
return poolScenario(context, fixture, { ...options, credentials })
} })
result.scope = config.environment === 'GRAY' ? 'FLEET_CAPACITY_POOL_GRAY' : 'FLEET_CAPACITY_POOL_HTTP'; result.fullAcceptance = false
result.remainingSeparateAcceptance = [...POOL_REMAINING]
const output = join(outputRoot, 'reports', poolReportName(config))