优化
This commit is contained in:
+2
-2
@@ -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
|
||||
|
||||
@@ -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 <n>/)
|
||||
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('随机线路只固定义乌仓并从客户真实可计价候选选择', () => {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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/)
|
||||
|
||||
@@ -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 <url> --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')
|
||||
|
||||
|
||||
@@ -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*'[^']+'/)
|
||||
})
|
||||
Reference in New Issue
Block a user