test(fleet): close shared trips and verify resource release
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
# 后端脚本管理与全量 HTTP 验收索引
|
||||
|
||||
`fleet-cross-customer-test-env.mjs` 是最小两客户共享车次的串行 TEST 专项入口,默认 `--plan` 零网络。显式运行参数为 `--execute --environment TEST --run-id <新批次> --warehouse-id <实际海外收货仓ID> --destination-warehouse-id <FBA目的仓ID>`,凭据仅从显式 `node --env-file="<TEST配置>" --env-file="<Jenkins配置>"` 与 `FBA_CORE_JENKINS_AGENT` 提供。通过普通转运来货、PDA实收和客户确认创建两个新客户各一托一箱,分别预排并关联同车;一单未拣齐时验证全车拒绝及完整库存快照不变,再统一发车、原键重放、逐客户签收。完整库存由只读取证器获取,不写业务SQL,不部署或改共享配置。报告位于 `reports/fleet-cross-<新批次>/`,每次请求发送前记录意图;失败保留原现场,不自动重跑或续跑,已存在批次拒绝覆盖。finally停用本轮PDA并退出独立会话;业务样本保留。前置拒绝不代表事务中途回滚,后者另验。对应入口保护合同为 `fleet-cross-customer-test-env.test.mjs`;内部生成与验收函数见模块README。
|
||||
`fleet-cross-customer-test-env.mjs` 是最小两客户共享车次的串行 TEST 专项入口,默认 `--plan` 零网络。显式运行参数为 `--execute --environment TEST --run-id <新批次> --warehouse-id <实际海外收货仓ID> --destination-warehouse-id <FBA目的仓ID>`,凭据仅从显式 `node --env-file="<TEST配置>" --env-file="<Jenkins配置>"` 与 `FBA_CORE_JENKINS_AGENT` 提供。通过普通转运来货、PDA实收和客户确认创建两个新客户各一托一箱,分别预排并关联同车;一单未拣齐时验证全车拒绝及完整库存快照不变,再统一发车、原键重放、逐客户签收,最后正式结案并分别释放车辆/司机,核对结案前后库存零变化。完整库存由只读取证器获取,不写业务SQL,不部署或改共享配置。报告位于 `reports/fleet-cross-<新批次>/`,每次请求发送前记录意图;失败保留原现场,不自动重跑或续跑,已存在批次拒绝覆盖。finally停用本轮PDA并退出独立会话;业务样本保留。前置拒绝不代表事务中途回滚,后者另验。对应入口保护合同为 `fleet-cross-customer-test-env.test.mjs`;内部生成与验收函数见模块README。
|
||||
|
||||
头程订单提交使用首次原始详情的 `expectedVersion` 与原 `Idempotency-Key`。生产单票的 `SUBMIT_INTENT` 必须先持久化 `submitCommand` 再发送,显式续跑不得使用当前版本替换原命令,缺少原命令的提交意图及后续检查点停止并等待人工核对。源 Excel 造数同样在提交前保存订单条目的 `submitCommand`;中断或失败条目保留原检查点且不自动重跑,不重建订单或换版本继续。
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { createTestEvidenceReaders } from './fba-core-regression-evidence.mjs'
|
||||
import { redactEvidence } from './fba-core-regression-test-env.mjs'
|
||||
import { resolvePickerAccess, createPicker, disablePicker } from './admin-test-env-modules/phase4-overseas-pda.mjs'
|
||||
import { verifyFleetCrossCustomer } from './admin-test-env-modules/fleet-cross-customer-acceptance.mjs'
|
||||
import { finishFleetTrip } from './admin-test-env-modules/fleet-trip-fulfillment.mjs'
|
||||
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const ok = response => response?.http === 200 && String(response.body?.code) === '200'
|
||||
@@ -33,12 +34,12 @@ export function parseCrossCustomerArguments(args) {
|
||||
export function crossCustomerPlan(config) {
|
||||
return { environment: 'TEST', execute: config.execute, runId: config.runId, warehouseId: config.warehouseId || null, destinationWarehouseId: config.destinationWarehouseId || null,
|
||||
output: `reports/fleet-cross-${config.runId}`, stages: ['two new customers', 'two ordinary transfer receipts', 'two dispatches on one trip',
|
||||
'unready customer gate rejection', 'shared departure and replay', 'independent receipts'],
|
||||
'unready customer gate rejection', 'shared departure and replay', 'independent receipts', 'trip completion and separate resource release'],
|
||||
limits: 'Prerequisite rejection is not mid-transaction rollback. No deployment, configuration change, SQL writes or automatic rerun. Retain business samples; disable temporary PDA in finally.' }
|
||||
}
|
||||
|
||||
export async function runCrossCustomer(config, { env = process.env, outputRoot = ROOT, readers, prepare, verify = verifyFleetCrossCustomer,
|
||||
contextFactory = options => new AcceptanceRun(options), pickerAccess = resolvePickerAccess, pickerCreate = createPicker, pickerDisable = disablePicker } = {}) {
|
||||
finish = finishFleetTrip, contextFactory = options => new AcceptanceRun(options), pickerAccess = resolvePickerAccess, pickerCreate = createPicker, pickerDisable = disablePicker } = {}) {
|
||||
const plan = crossCustomerPlan(config)
|
||||
if (!config.execute) return plan
|
||||
if (config.environment !== 'TEST' || env.CHONGCHONG_ENV !== 'test' || !/^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/.test(config.runId)
|
||||
@@ -107,7 +108,16 @@ export async function runCrossCustomer(config, { env = process.env, outputRoot =
|
||||
const fixture = await prepare(context, { execute: true, warehouseId: config.warehouseId, destinationWarehouseId: config.destinationWarehouseId, pdaToken: picker.token, readInventory })
|
||||
save('fixture.json', fixture)
|
||||
await verify(context, fixture, { execute: true, readInventory })
|
||||
report = { success: context.createdResources.fleetCrossCustomer?.status === 'VERIFIED' }
|
||||
const trip = context.createdResources.fleetTrips?.find(row => row.tripNo === fixture.tripNo)
|
||||
if (!trip) throw Error('Original fixture trip missing for resource completion')
|
||||
const beforeClosure = []
|
||||
for (const member of fixture.members) beforeClosure.push(await readInventory({ marker: context.marker, customerId: member.customerId, warehouseId: config.warehouseId }))
|
||||
await finish(context, 'fleet-cross-customer-closure', trip)
|
||||
const afterClosure = []
|
||||
for (const member of fixture.members) afterClosure.push(await readInventory({ marker: context.marker, customerId: member.customerId, warehouseId: config.warehouseId }))
|
||||
if (JSON.stringify(beforeClosure) !== JSON.stringify(afterClosure)) throw Error('Trip closure changed customer inventory')
|
||||
report = { success: context.createdResources.fleetCrossCustomer?.status === 'VERIFIED'
|
||||
&& trip.closure?.status === 'CARGO_COMPLETED_AND_RESOURCES_RELEASED', closureInventoryUnchanged: true }
|
||||
} catch (error) {
|
||||
report = { success: false, error: error instanceof Error ? error.message : 'CROSS_CUSTOMER_FAILED' }
|
||||
} finally {
|
||||
|
||||
@@ -24,11 +24,13 @@ function harness(root, fail = false) {
|
||||
assert.equal(options.pdaToken, 'private-pda')
|
||||
await context.request('POST', '/api/test-owned-command', { idempotencyKey: 'stable-command', password: 'private-pda-password' })
|
||||
if (fail) throw Error('FIXTURE_FAILED')
|
||||
return { pdaToken: options.pdaToken, marker: context.marker }
|
||||
return { pdaToken: options.pdaToken, marker: context.marker, tripNo: 'TR-OFFLINE', members: [{ customerId: '42' }, { customerId: '43' }] }
|
||||
}, verify: async (_context, _fixture, options) => {
|
||||
await options.readInventory({ marker: context.marker, customerId: '42', warehouseId: '900' })
|
||||
context.createdResources.fleetCrossCustomer = { status: 'VERIFIED' }
|
||||
context.createdResources.fleetTrips = [{ tripNo: 'TR-OFFLINE' }]
|
||||
},
|
||||
finish: async (_context, _module, trip) => { trip.closure = { status: 'CARGO_COMPLETED_AND_RESOURCES_RELEASED' } },
|
||||
} }
|
||||
}
|
||||
test('plan is network-free and execution requires explicit TEST, unique run and warehouse', async () => {
|
||||
|
||||
Reference in New Issue
Block a user