test: prepare measurement review fixtures without approval
This commit is contained in:
@@ -284,6 +284,8 @@ Client 全量、Client 入库测量与计费、待审核订单和批量待分柜
|
||||
|
||||
同一入口可显式添加 `--prepare-domestic-pda-h5 true`,仅允许单独指定 `phase5-root-fulfillment`:创建本轮客户和两箱根订单,经测量、分柜、订舱放舱和计划确认生成一箱待拣任务及箱唛后停止,不领取/拣货/完成任务、不进入海外链。报告标记 `fullAcceptance=false`、`PREPARED_NOT_VERIFIED`,`createdResources.phase5RootFulfillment.domesticPdaH5Fixture` 保存任务、柜次、计划、仓库、入仓来源及箱信息供 H5;成功只代表准备完成。样本保留,失败不自动重试或清理,默认不传该参数时继续完整链。
|
||||
|
||||
测量审核页面准备使用 `--modules inbound-measurement --prepare-measurement-review true --output <独立报告>`,环境和凭据沿用本入口显式配置;会创建本轮订单与测量证据并提交待审,不审核通过。仅允许 TEST 单票最小依赖,不能混用其他准备模式或后续模块;报告标注 `MEASUREMENT_REVIEW_UI_FIXTURE/fullAcceptance=false`,失败保留数据,不自动重试或清理。具体样本字段见模块说明。
|
||||
|
||||
|
||||
`admin-test-env-acceptance.mjs --environment gray` 显式支持临时灰度环境,当前仅开放 `--modules phase5-root-fulfillment` 及自动展开的本轮客户资质 fixture。该链会通过正式 HTTP 新建隔离客户、两箱根订单及分柜子来源,推进国内测量、装柜、报关、人工船节点、海外收货、排车、出库和签收;不是默认全量验收。默认全量中包含 TEST 专用故障注入和未经灰度副作用核对的模块,因此 gray 拒绝省略模块、附加模块、批量订单和卸柜故障开关,也不会创建 TEST fault executor。`--f40-tax-mode TAX_REFUND` 覆盖退税文件确认链;真实邮件和供应商自动查询不在此闭环的完成声明中。
|
||||
|
||||
|
||||
@@ -432,6 +432,7 @@ export function parseArguments(argv) {
|
||||
if (fixtureIndex >= 0) modules.splice(fixtureIndex, 1)
|
||||
}
|
||||
if (values.has('prepare-domestic-pda-h5') && values.get('prepare-domestic-pda-h5') !== 'true') throw new Error('prepare-domestic-pda-h5 必须明确为 true;完整验收请省略此参数')
|
||||
if (values.has('prepare-measurement-review') && values.get('prepare-measurement-review') !== 'true') throw new Error('prepare-measurement-review 必须明确为 true')
|
||||
return {
|
||||
environment,
|
||||
gateway: (values.get('gateway') || envValue('ADMIN_TEST_GATEWAY', 'ADMIN_GRAY_GATEWAY')).replace(/\/$/, ''),
|
||||
@@ -456,6 +457,7 @@ export function parseArguments(argv) {
|
||||
waitSplitSequence: Number(values.get('wait-split-sequence') || 1),
|
||||
waitSplitRandomValue: Number(values.get('wait-split-random-value') || values.get('wait-split-sequence') || 1),
|
||||
prepareDropshipManualCount: values.get('prepare-dropship-manual-count') === 'true',
|
||||
prepareMeasurementReview: values.get('prepare-measurement-review') === 'true',
|
||||
verifyPalletLocationGuide: values.has('verify-pallet-location-guide'),
|
||||
routeMode: values.get('route-mode') || 'fixed',
|
||||
quiet: values.get('quiet') === 'true',
|
||||
@@ -492,6 +494,11 @@ export function validateExecutionOptions(options) {
|
||||
if (options.environment && !['test', 'gray'].includes(options.environment)) throw new Error('Only test or explicit gray acceptance is supported')
|
||||
if (options.f40TaxMode && !['BUY_DECLARATION', 'TAX_REFUND'].includes(options.f40TaxMode)) throw new Error('f40-tax-mode 必须为 BUY_DECLARATION 或 TAX_REFUND')
|
||||
if (options.prepareDomesticPdaH5 && (options.requestedModules?.length !== 1 || options.requestedModules[0] !== 'phase5-root-fulfillment')) throw new Error('prepare-domestic-pda-h5 仅允许单独指定 phase5-root-fulfillment')
|
||||
if (options.prepareMeasurementReview && (options.environment !== 'test'
|
||||
|| options.requestedModules?.length !== 1 || options.requestedModules[0] !== 'inbound-measurement'
|
||||
|| options.waitSplitCount !== 1 || options.prepareDomesticPdaH5 || options.prepareDropshipManualCount)) {
|
||||
throw new Error('prepare-measurement-review 仅允许 TEST 单票 inbound-measurement 待审准备,不能混用其他准备模式或后续模块')
|
||||
}
|
||||
if ('overseasGateway' in options) throw new Error('Use the single gateway option')
|
||||
if (options.modules.some((module) => ['phase3-loading', 'phase3-container-loading', 'phase5-root-fulfillment'].includes(module))
|
||||
&& (!options.pdaUsername?.trim() || !options.pdaPassword?.trim())) {
|
||||
@@ -1956,6 +1963,7 @@ export class AcceptanceRun {
|
||||
break
|
||||
}
|
||||
if (module === 'inbound-measurement'
|
||||
&& !this.options.prepareMeasurementReview
|
||||
&& this.captureWaitSplitFixture(this.options.waitSplitSequence || 1)) {
|
||||
await this.createAdditionalWaitSplitFixtures()
|
||||
}
|
||||
@@ -2007,7 +2015,7 @@ export class AcceptanceRun {
|
||||
plannedModules: [...this.options.modules],
|
||||
defaultModules: [...DEFAULT_MODULES],
|
||||
defaultPlanSelected: DEFAULT_MODULES.every((name) => this.options.modules.includes(name)),
|
||||
preparationOnly: this.options.prepareDomesticPdaH5 === true || this.options.prepareDropshipManualCount === true,
|
||||
preparationOnly: this.options.prepareDomesticPdaH5 === true || this.options.prepareDropshipManualCount === true || this.options.prepareMeasurementReview === true,
|
||||
returnMainChainOnly: this.options.returnMainChainOnly === true,
|
||||
evidenceType: 'HTTP',
|
||||
pageAcceptance: 'NOT_VERIFIED',
|
||||
@@ -2016,6 +2024,7 @@ export class AcceptanceRun {
|
||||
success: this.fatal === null && summary.failed === 0, fatal: this.fatal,
|
||||
fullCoverage: this.coverageGaps.length === 0 && !this.modules.some(module => module.skipped),
|
||||
...(this.options.prepareDomesticPdaH5 ? { fullAcceptance: false, purpose: 'DOMESTIC_PDA_H5_FIXTURE' } : {}),
|
||||
...(this.options.prepareMeasurementReview ? { fullAcceptance: false, purpose: 'MEASUREMENT_REVIEW_UI_FIXTURE' } : {}),
|
||||
summary,
|
||||
modules: this.modules, scenarios: this.scenarios, deploymentBlockers: this.deploymentBlockers,
|
||||
coverageGaps: this.coverageGaps,
|
||||
|
||||
@@ -64,6 +64,8 @@ Admin TEST 入口必须通过显式环境文件配置 `ADMIN_TEST_FIXED_CUSTOMER
|
||||
|
||||
固定托盘造数中,`inbound-measurement.mjs` 只复用 `TP-001`、`YW-TP-002` 指定池中的现有托盘;一只可复用,不要求两只同时存在,两只均缺失才阻断,不自动选择池外托盘。仅明确设置内部 `allowAnyExistingPallet` 的通用场景可读取当前仓库已有资源;两者均不创建托盘。
|
||||
|
||||
待审页面验收可用总入口 `--modules inbound-measurement --prepare-measurement-review true`:只允许 TEST 单票及最小依赖,正式创建、测量并提交后停在 `MEASURE_PENDING/PENDING`,不调用审核或生成 WAIT_SPLIT 后续样本。报告 `fullAcceptance=false`、`preparationOnly=true`,业务身份在 `createdResources.measurementReviewUiFixture`;失败保留原数据和报告,不自动重跑或清理。默认完整测量链不受影响。
|
||||
|
||||
OCR模块必须通过`ADMIN_TEST_OCR_IMAGE`显式指定本轮图片,不依赖个人桌面路径;定向运行自动展开客户资质及订单前置。识别失败的空字段允许省略,但识别成功必须有有效重量与单位;人工修正和确认分别增加版本。合成称重测试图只证明上传、识别响应及人工确认协议,不代表现场称重照片或设备验收。费用项目模块按受控编码验证112条正式基线,合法扩展项目保留并单独核对分页和筛选。散板普通状态接口仅验证取消及非法派车拒绝,原单须出现在待匹配需求池;需求池接车到签收另列覆盖缺口,不以手工跳状态冒充履约。
|
||||
|
||||
预约工作查询在本轮业务写入后,仅对服务端明确返回的 A0409“预约批次或运输关联已变化,服务端正在协调完整工作清单”或“已开船工作范围变化,请先完成服务端同步”每 5 秒重读,最多等待 240 秒;超时保留失败,其他错误不重试。这是后台完整扫描的就绪等待,不是页面响应性能验收,也不触发同步写接口。
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
import { parseArguments, validateExecutionOptions } from '../admin-test-env-acceptance.mjs'
|
||||
import { completeDropshipPalletization, verifyMeasurementSubmitReplay,
|
||||
verifyMeasurementApprovalReplay } from './inbound-measurement.mjs'
|
||||
|
||||
@@ -11,6 +12,18 @@ const measurementTests = read('../../firstleg/src/test/java/com/baiyue/firstleg/
|
||||
const cartonTests = read('../../firstleg/src/test/java/com/baiyue/firstleg/service/CartonMeasurementServiceTest.java')
|
||||
const reviewTests = read('../../firstleg/src/test/java/com/baiyue/firstleg/service/MeasurementReviewServiceTest.java')
|
||||
|
||||
test('measurement review preparation is explicit and cannot continue downstream or bulk flows', () => {
|
||||
const args = ['--environment', 'test', '--gateway', 'https://test.invalid', '--username', 'fixture', '--password', 'fixture-only', '--modules', 'inbound-measurement', '--prepare-measurement-review', 'true']
|
||||
const options = parseArguments(args)
|
||||
assert.equal(options.prepareMeasurementReview, true)
|
||||
assert.doesNotThrow(() => validateExecutionOptions(options))
|
||||
assert.throws(() => parseArguments([...args.slice(0, -1), 'false']), /必须明确/)
|
||||
for (const change of [{ waitSplitCount: 2 }, { prepareDropshipManualCount: true }, { requestedModules: ['inbound-measurement', 'phase3-customs'] }]) {
|
||||
assert.throws(() => validateExecutionOptions({ ...options, ...change }), /待审准备/)
|
||||
}
|
||||
assert.equal(parseArguments(args.slice(0, -2)).prepareMeasurementReview, false)
|
||||
})
|
||||
|
||||
test('measurement acceptance scans the stable box code frozen by first print', () => {
|
||||
assert.match(source, /const boxCode = `\$\{inboundCode\}#\$\{originalPackageCount\}-1`/)
|
||||
assert.doesNotMatch(source, /#V\d+#/)
|
||||
|
||||
@@ -1313,6 +1313,21 @@ export async function run(context) {
|
||||
&& Number.isInteger(Number(pendingSummary?.version)),
|
||||
`status=${pendingSummary?.processStatus ?? 'missing'}, version=${pendingSummary?.version ?? 'missing'}`, OPTIONS)
|
||||
|
||||
if (context.options?.prepareMeasurementReview) {
|
||||
const ready = pendingSummary?.processStatus === 'MEASURE_PENDING'
|
||||
&& pendingSummary?.measureReviewStatus === 'PENDING'
|
||||
&& Number.isInteger(Number(pendingSummary?.version))
|
||||
context.createdResources.measurementReviewUiFixture = {
|
||||
marker: context.marker, inboundId, tenantId, inboundCode,
|
||||
measurementId: asId(draft.measurementId), expectedVersion: pendingSummary?.version,
|
||||
status: ready ? 'PREPARED_NOT_VERIFIED' : 'PREPARATION_FAILED', fullAcceptance: false,
|
||||
}
|
||||
context.assert(MODULE, '待审页面样本准备完成,未执行审核', pendingInboundResponse, ready,
|
||||
`inboundCode=${inboundCode}, measurementId=${asId(draft.measurementId)}`, OPTIONS)
|
||||
finishScenario(context, start)
|
||||
return
|
||||
}
|
||||
|
||||
const approvalPath = `/api/platform/v1/inbounds/${inboundId}/measurements/approve?${query(tenantId)}`
|
||||
const approvalPayload = {
|
||||
measurementId: draft.measurementId, expectedVersion: Number(pendingSummary?.version), reason: null,
|
||||
|
||||
Reference in New Issue
Block a user