From 9a3b408de0a04df9e6093ed2a208441c4a59c4e8 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 1 Sep 2026 11:38:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/README.md | 4 +- .../it-test-wait-split-bulk-contract.test.mjs | 3 +- scripts/client-phase2-test-env-acceptance.mjs | 17 ++++----- scripts/client-test-env-acceptance.mjs | 22 +++++------ ...te-pending-review-orders-contract.test.mjs | 14 ++++--- scripts/create-pending-review-orders.mjs | 31 +++++++--------- .../http-script-environment-contract.test.mjs | 37 +++++++++++++++++++ 7 files changed, 82 insertions(+), 46 deletions(-) create mode 100644 scripts/http-script-environment-contract.test.mjs diff --git a/scripts/README.md b/scripts/README.md index cca052d0f..85c573b56 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -66,7 +66,7 @@ |---|---|---|---| | 平台后台全模块 HTTP 验收 | `scripts/admin-test-env-acceptance.mjs` | 会创建、更新或最终停用测试数据 | `--modules` 默认列表中的平台客户、仓储、订单、入库、分柜、装柜、船期、拖车、报关、财务、通知、统计等模块 | | 客户工作台完整 HTTP 链路验收 | `scripts/client-test-env-acceptance.mjs` | 会创建测试企业、订单、入库、权限和站内消息数据 | 注册/审核、客户隔离、订单、入库计费、文件、财务、客户侧权限,以及我的消息未读/已读/全部已读链路 | -| 固定客户批量创建待审核订单 | `scripts/create-pending-review-orders.mjs` | 只使用固定客户账号创建指定数量订单 | 登录 `17512095155`,选择真实可计价仓库/渠道,创建并提交订单,停在待审核,不创建客户、不审核订单 | +| 固定客户批量创建待审核订单 | `scripts/create-pending-review-orders.mjs` | 只使用 `MERCHANT_TEST_USERNAME/PASSWORD` 配置的客户账号创建指定数量订单 | 使用 `ADMIN_TEST_GATEWAY`,选择真实可计价仓库/渠道,创建并提交订单,停在待审核,不创建客户、不审核订单 | | Client 二期专项 HTTP 验收 | `scripts/client-phase2-test-env-acceptance.mjs` | 会写入测试环境业务数据 | 二期客户入库、测量、费用和权限专项场景;不是替代 Client 全量入口 | | 人工分柜至 LOADED 分段验收 | `scripts/admin-test-env-acceptance.mjs` 的 `manual-*-fixture/verify` 可选模块;步骤和参数见 `scripts/admin-test-env-modules/README.md` | fixture 通过正式 HTTP 准备本轮数据,verify 只读核验;不进入默认全量 | `WAIT_SPLIT → 人工分柜 → 推送预装 → 订舱关联/计划确认 → PDA 领取与原子 batch → 五类证据/完成 → LOADED` | @@ -76,7 +76,7 @@ ## 固定客户待审核订单 -需要为人工审核准备固定数量的订单时,使用 `create-pending-review-orders.mjs`。可直接在文件顶部 `CONFIG` 中填写 `GATEWAY`、`USERNAME`、`PASSWORD` 和 `COUNT`;固定账号为 `17512095155`。脚本不会创建客户,也不会调用平台审核、入库或后续履约接口。数量支持 `1-100`,每张订单创建后立即提交并回查客户端状态为 `SUBMITTED`,报告统一记录为 `PENDING_REVIEW`。 +需要为人工审核准备固定数量的订单时,使用 `create-pending-review-orders.mjs`。Gateway 统一读取 `ADMIN_TEST_GATEWAY`,客户账号统一读取 `MERCHANT_TEST_USERNAME/PASSWORD`;数量可通过 `--count` 或 `PENDING_REVIEW_ORDER_COUNT` 设置,默认 50。脚本不会创建客户,也不会调用平台审核、入库或后续履约接口。数量支持 `1-100`,每张订单创建后立即提交并回查客户端状态为 `SUBMITTED`,报告统一记录为 `PENDING_REVIEW`。 ```powershell node scripts/create-pending-review-orders.mjs diff --git a/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs b/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs index c070bb330..5a15afe65 100644 --- a/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs +++ b/scripts/admin-test-env-modules/it-test-wait-split-bulk-contract.test.mjs @@ -8,7 +8,7 @@ const measurement = readFileSync(new URL('./inbound-measurement.mjs', import.met const runner = readFileSync(new URL('../it-test-wait-split-bulk.mjs', import.meta.url), 'utf8') const warehouseLocationResources = readFileSync(new URL('../../warehouse/src/main/java/com/baiyue/warehouse/service/LocationResourceService.java', import.meta.url), 'utf8') -test('单票随机箱数严格分配到各 SKU 并限制为1到1000', () => { +test('Admin 通用单票支持到1000箱且八千票专项入口限制为300箱', () => { assert.match(acceptance, /--wait-split-package-count /) assert.match(acceptance, /wait-split-package-count必须是1-1000的整数或0/) assert.match(acceptance, /packageCounts\.push\(remaining\)/) @@ -16,6 +16,7 @@ test('单票随机箱数严格分配到各 SKU 并限制为1到1000', () => { assert.match(orders, /packageCounts\[index\] \|\| packageCountPerSku/) assert.match(measurement, /detailPackageCounts\.reduce\(\(sum, count\) => sum \+ count, 0\)/) assert.match(measurement, /detailPackageCounts\.slice\(0, detailIndex\)/) + assert.match(runner, /boxMax > 300/) }) test('随机线路只固定义乌仓并从客户真实可计价候选选择', () => { diff --git a/scripts/client-phase2-test-env-acceptance.mjs b/scripts/client-phase2-test-env-acceptance.mjs index 1815ee4d5..5252ef7c5 100644 --- a/scripts/client-phase2-test-env-acceptance.mjs +++ b/scripts/client-phase2-test-env-acceptance.mjs @@ -16,22 +16,21 @@ import { resolve } from 'node:path' import { createInterface } from 'node:readline/promises' const args = parseArgs(process.argv.slice(2)) -const gateway = trimSlash(args.gateway ?? process.env.CLIENT_PHASE2_TEST_GATEWAY - ?? 'http://8.163.30.128:9000') -const clientUsername = args['client-username'] ?? process.env.CLIENT_PHASE2_USERNAME +const gateway = trimSlash(args.gateway ?? process.env.ADMIN_TEST_GATEWAY) +const clientUsername = args['client-username'] ?? process.env.MERCHANT_TEST_USERNAME ?? await promptText('二期客户端账号(必须由正式注册/建档产生)', '') -const clientPassword = args['client-password'] ?? process.env.CLIENT_PHASE2_PASSWORD +const clientPassword = args['client-password'] ?? process.env.MERCHANT_TEST_PASSWORD ?? await promptSecret('二期客户端密码') -const platformUsername = args['platform-username'] ?? process.env.PLATFORM_USERNAME - ?? await promptText('平台夹具查询账号', 'platform_admin') -const platformPassword = args['platform-password'] ?? process.env.PLATFORM_PASSWORD +const platformUsername = args['platform-username'] ?? process.env.ADMIN_TEST_USERNAME + ?? await promptText('平台夹具查询账号', '') +const platformPassword = args['platform-password'] ?? process.env.ADMIN_TEST_PASSWORD ?? await promptSecret('平台夹具查询密码') const forcedOrderId = args['order-id'] ?? process.env.CLIENT_PHASE2_ORDER_ID ?? null const reportDir = resolve(args['report-dir'] ?? process.env.CLIENT_PHASE2_REPORT_DIR ?? 'reports') const timeoutMs = Number(args['timeout-ms'] ?? process.env.CLIENT_PHASE2_TIMEOUT_MS ?? 30_000) -if (!clientUsername || !clientPassword || !platformPassword) { - console.error('An onboarded Client account and platform credentials are required') +if (!gateway || !clientUsername || !clientPassword || !platformUsername || !platformPassword) { + console.error('ADMIN_TEST_GATEWAY, an onboarded Client account, and platform credentials are required') process.exit(2) } if (!Number.isFinite(timeoutMs) || timeoutMs < 1_000) { diff --git a/scripts/client-test-env-acceptance.mjs b/scripts/client-test-env-acceptance.mjs index b7afa8553..a996d8588 100644 --- a/scripts/client-test-env-acceptance.mjs +++ b/scripts/client-test-env-acceptance.mjs @@ -5,8 +5,8 @@ * * Run directly and enter the platform/client test credentials when prompted. * Optional environment variables for CI: - * CLIENT_TEST_GATEWAY, PLATFORM_USERNAME, EXISTING_CLIENT_USERNAME, - * PLATFORM_PASSWORD, EXISTING_CLIENT_PASSWORD, CLIENT_TEST_REPORT_DIR + * ADMIN_TEST_GATEWAY, ADMIN_TEST_USERNAME, ADMIN_TEST_PASSWORD, + * MERCHANT_TEST_USERNAME, MERCHANT_TEST_PASSWORD, CLIENT_TEST_REPORT_DIR * * Example: * node scripts/client-test-env-acceptance.mjs @@ -18,20 +18,20 @@ import { resolve } from 'node:path' import { createInterface } from 'node:readline/promises' const args = parseArgs(process.argv.slice(2)) -const gateway = trimSlash(args.gateway ?? process.env.CLIENT_TEST_GATEWAY ?? 'http://8.163.30.128:9000') -const platformUsername = args['platform-username'] ?? process.env.PLATFORM_USERNAME - ?? await promptText('平台审核账号', 'platform_admin') -const platformPassword = args['platform-password'] ?? process.env.PLATFORM_PASSWORD +const gateway = trimSlash(args.gateway ?? process.env.ADMIN_TEST_GATEWAY) +const platformUsername = args['platform-username'] ?? process.env.ADMIN_TEST_USERNAME + ?? await promptText('平台审核账号', '') +const platformPassword = args['platform-password'] ?? process.env.ADMIN_TEST_PASSWORD ?? await promptSecret('平台审核密码') -const existingClientUsername = args['existing-client-username'] ?? process.env.EXISTING_CLIENT_USERNAME +const existingClientUsername = args['existing-client-username'] ?? process.env.MERCHANT_TEST_USERNAME ?? await promptText('跨客户隔离测试账号(必须由正式注册/建档产生)', '') -const existingClientPassword = args['existing-client-password'] ?? process.env.EXISTING_CLIENT_PASSWORD +const existingClientPassword = args['existing-client-password'] ?? process.env.MERCHANT_TEST_PASSWORD ?? await promptSecret('跨客户隔离测试密码') const reportDir = resolve(args['report-dir'] ?? process.env.CLIENT_TEST_REPORT_DIR ?? 'reports') const timeoutMs = Number(args['timeout-ms'] ?? process.env.CLIENT_TEST_TIMEOUT_MS ?? 30_000) -if (!platformPassword || !existingClientUsername || !existingClientPassword) { - console.error('Platform password and an independently onboarded client account are required') +if (!gateway || !platformUsername || !platformPassword || !existingClientUsername || !existingClientPassword) { + console.error('ADMIN_TEST_GATEWAY, platform credentials, and an independently onboarded client account are required') process.exit(2) } if (!Number.isFinite(timeoutMs) || timeoutMs < 1_000) { @@ -1290,7 +1290,7 @@ async function orderLifecycleModule() { async function isolationModule() { if (!existingClientUsername || !existingClientPassword) { recordSkipped('isolation', 'cross-client order access is rejected', - 'Set EXISTING_CLIENT_USERNAME and EXISTING_CLIENT_PASSWORD to enable this check') + 'Set MERCHANT_TEST_USERNAME and MERCHANT_TEST_PASSWORD to enable this check') return } const login = await apiStep('isolation', 'existing client login', { diff --git a/scripts/create-pending-review-orders-contract.test.mjs b/scripts/create-pending-review-orders-contract.test.mjs index ac4c7a2d3..5f3744f75 100644 --- a/scripts/create-pending-review-orders-contract.test.mjs +++ b/scripts/create-pending-review-orders-contract.test.mjs @@ -6,11 +6,14 @@ import { PRODUCT_CATALOG, buildOrderPayload, diversifyRoutes, parseArguments, ra const source = readFileSync(new URL('./create-pending-review-orders.mjs', import.meta.url), 'utf8') -test('uses the fixed merchant account and stops at pending review', () => { - assert.ok(source.includes("USERNAME: '17512095155'"), 'fixed username is missing') - assert.ok(/PASSWORD:\s*'[^']*'/.test(source), 'CONFIG.PASSWORD is missing') - assert.ok(/COUNT:\s*\d+/.test(source), 'CONFIG.COUNT is missing') - assert.match(source, /COUNT:\s*50/) +test('uses the configured merchant account and stops at pending review', () => { + assert.match(source, /process\.env\.ADMIN_TEST_GATEWAY/) + assert.match(source, /process\.env\.MERCHANT_TEST_USERNAME/) + assert.match(source, /process\.env\.MERCHANT_TEST_PASSWORD/) + assert.match(source, /const DEFAULT_COUNT = 50/) + assert.doesNotMatch(source, /const CONFIG\s*=/) + assert.doesNotMatch(source, /8\.163\.30\.128/) + assert.doesNotMatch(source, /PASSWORD:\s*'[^']+'/) assert.match(source, /randomInt\(1, 31\)/) assert.match(source, /loadPricedRoutes/) assert.match(source, /destinationWarehouseCode/) @@ -28,6 +31,7 @@ test('uses the fixed merchant account and stops at pending review', () => { }) test('supports a bounded fixed order count', () => { + assert.equal(validateCount(undefined), 50) assert.equal(validateCount('1'), 1) assert.equal(validateCount('10'), 10) assert.throws(() => validateCount('0'), /between 1 and 100/) diff --git a/scripts/create-pending-review-orders.mjs b/scripts/create-pending-review-orders.mjs index 4da72837d..d83a44ea4 100644 --- a/scripts/create-pending-review-orders.mjs +++ b/scripts/create-pending-review-orders.mjs @@ -29,13 +29,7 @@ export const PRODUCT_CATALOG = [ { cargoName: '宠物橡胶玩具', cargoNameEn: 'Pet rubber toy', hsCode: '4016999090', material: '天然橡胶 / Natural rubber', usage: '宠物玩耍 / Pet play', brand: 'PETTEST', modelPrefix: 'PET', lengthCm: 46, widthCm: 36, heightCm: 34, unitsPerPackage: 48, cartonGrossWeightKg: 15.6, unitDeclaredValue: 3.4, productLengthCm: 12, productWidthCm: 7, productHeightCm: 7 }, ] -// Local test-environment settings. Keep PASSWORD empty in committed code. -const CONFIG = { - GATEWAY: 'http://8.163.30.128:9000', - USERNAME: '17512095155', - PASSWORD: 'M4mmp4YWcH5DiYPT', - COUNT: 50, -} +const DEFAULT_COUNT = 50 export function parseArguments(values) { const options = {} @@ -58,7 +52,7 @@ export function parseArguments(values) { } export function validateCount(value) { - const count = Number(value ?? CONFIG.COUNT) + const count = Number(value ?? DEFAULT_COUNT) if (!Number.isInteger(count) || count < 1 || count > MAX_COUNT) { throw new Error(`--count must be an integer between 1 and ${MAX_COUNT}`) } @@ -342,12 +336,13 @@ function printHelp() { node scripts/create-pending-review-orders.mjs --gateway --count <1-${MAX_COUNT}> [--same-channel] Environment: - CLIENT_TEST_GATEWAY Test environment Gateway URL + ADMIN_TEST_GATEWAY Test environment Gateway URL MERCHANT_TEST_PASSWORD Password for the configured fixed account - MERCHANT_TEST_USERNAME Optional username override + MERCHANT_TEST_USERNAME Username for the configured fixed account + PENDING_REVIEW_ORDER_COUNT Optional order count, defaults to ${DEFAULT_COUNT} PENDING_ORDER_REPORT Optional JSON report path -The GATEWAY, USERNAME, PASSWORD, and COUNT fields can also be filled in CONFIG at the top of this file.`) +Command-line arguments override environment variables. Credentials must not be written into this file.`) } export async function main(argv = process.argv.slice(2)) { @@ -356,13 +351,13 @@ export async function main(argv = process.argv.slice(2)) { printHelp() return 0 } - const gateway = trimSlash(args.gateway ?? process.env.CLIENT_TEST_GATEWAY ?? CONFIG.GATEWAY) - if (!gateway) throw new Error('Gateway is required via CONFIG.GATEWAY, --gateway, or CLIENT_TEST_GATEWAY') - const count = validateCount(args.count) - const username = args.username ?? process.env.MERCHANT_TEST_USERNAME ?? CONFIG.USERNAME - const password = args.password ?? process.env.MERCHANT_TEST_PASSWORD ?? CONFIG.PASSWORD - if (!username) throw new Error('Fixed merchant username is required in CONFIG.USERNAME') - if (!password) throw new Error('Merchant password is required in CONFIG.PASSWORD or MERCHANT_TEST_PASSWORD') + const gateway = trimSlash(args.gateway ?? process.env.ADMIN_TEST_GATEWAY) + if (!gateway) throw new Error('Gateway is required via --gateway or ADMIN_TEST_GATEWAY') + const count = validateCount(args.count ?? process.env.PENDING_REVIEW_ORDER_COUNT) + const username = args.username ?? process.env.MERCHANT_TEST_USERNAME + const password = args.password ?? process.env.MERCHANT_TEST_PASSWORD + if (!username) throw new Error('Merchant username is required via --username or MERCHANT_TEST_USERNAME') + if (!password) throw new Error('Merchant password is required via --password or MERCHANT_TEST_PASSWORD') const timeoutMs = Number(args['timeout-ms'] ?? process.env.CLIENT_TEST_TIMEOUT_MS ?? 30_000) if (!Number.isFinite(timeoutMs) || timeoutMs < 1_000) throw new Error('--timeout-ms must be at least 1000') diff --git a/scripts/http-script-environment-contract.test.mjs b/scripts/http-script-environment-contract.test.mjs new file mode 100644 index 000000000..063689a7b --- /dev/null +++ b/scripts/http-script-environment-contract.test.mjs @@ -0,0 +1,37 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import test from 'node:test' + +const read = (name) => readFileSync(new URL(name, import.meta.url), 'utf8') +const client = read('./client-test-env-acceptance.mjs') +const clientPhase2 = read('./client-phase2-test-env-acceptance.mjs') +const pendingOrders = read('./create-pending-review-orders.mjs') + +const activeHttpScripts = [client, clientPhase2, pendingOrders] +const removedEnvironmentVariables = [ + 'CLIENT_TEST_GATEWAY', 'CLIENT_PHASE2_TEST_GATEWAY', + 'PLATFORM_USERNAME', 'PLATFORM_PASSWORD', + 'EXISTING_CLIENT_USERNAME', 'EXISTING_CLIENT_PASSWORD', + 'CLIENT_PHASE2_USERNAME', 'CLIENT_PHASE2_PASSWORD', +] + +test('active Client and data-generation scripts share the canonical environment variables', () => { + for (const source of activeHttpScripts) { + assert.match(source, /process\.env\.ADMIN_TEST_GATEWAY/) + assert.match(source, /process\.env\.MERCHANT_TEST_USERNAME/) + assert.match(source, /process\.env\.MERCHANT_TEST_PASSWORD/) + } + assert.match(client, /process\.env\.ADMIN_TEST_USERNAME/) + assert.match(client, /process\.env\.ADMIN_TEST_PASSWORD/) + assert.match(clientPhase2, /process\.env\.ADMIN_TEST_USERNAME/) + assert.match(clientPhase2, /process\.env\.ADMIN_TEST_PASSWORD/) +}) + +test('active scripts no longer carry the retired variables, fixed Gateway, or source password config', () => { + for (const source of activeHttpScripts) { + for (const variable of removedEnvironmentVariables) assert.doesNotMatch(source, new RegExp(`process\\.env\\.${variable}`)) + assert.doesNotMatch(source, /8\.163\.30\.128/) + } + assert.doesNotMatch(pendingOrders, /const CONFIG\s*=/) + assert.doesNotMatch(pendingOrders, /PASSWORD:\s*'[^']+'/) +})