157 lines
7.8 KiB
JavaScript
157 lines
7.8 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { readFileSync } from 'node:fs'
|
|
import test from 'node:test'
|
|
import { beijingDate, matchesProcurementNo, runFixedCustomerProcurement, run } from './phase4-overseas-wallet-and-pallet-cost.mjs'
|
|
import { AcceptanceRun } from '../admin-test-env-acceptance.mjs'
|
|
|
|
const source = readFileSync(new URL('./phase4-overseas-wallet-and-pallet-cost.mjs', import.meta.url), 'utf8')
|
|
const entry = readFileSync(new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8')
|
|
const pda = readFileSync(new URL('./phase4-overseas-pda.mjs', import.meta.url), 'utf8')
|
|
|
|
function fixedHarness({ warehouse = true, profileId = '101', logoutCode = '200' } = {}) {
|
|
const context = new AcceptanceRun({ environment: 'test', fixedCustomerId: 'TEN101', quiet: true })
|
|
context.marker = 'PROC-FIXED-TEST'
|
|
context.context.merchantQualificationFixture = { marker: context.marker, tenantId: '101',
|
|
tenantName: 'QA', reused: true, username: 'fixture-user', password: 'fixture-password' }
|
|
const calls = []
|
|
context.request = async (method, path) => {
|
|
calls.push({ method, path })
|
|
let data, code = '200'
|
|
if (method === 'GET' && path.endsWith('/warehouse-options')) data = warehouse ? [{ warehouseId: '201' }] : []
|
|
else if (method === 'POST' && path === '/api/auth/login') data = { accessToken: 'test-token', refreshToken: 'test-refresh' }
|
|
else if (method === 'GET' && path === '/api/merchant/v1/iam/tenant/profile') data = { id: profileId, status: 'ACTIVE' }
|
|
else if (method === 'POST' && path === '/api/auth/logout') code = logoutCode
|
|
else throw new Error(`unexpected request ${method} ${path}`)
|
|
return { http: 200, body: { code, data } }
|
|
}
|
|
return { context, calls }
|
|
}
|
|
|
|
test('固定客户采购进入默认模块,仅复用候选仓与会话,不初始化钱包', async () => {
|
|
const { context, calls } = fixedHarness()
|
|
let invoked = 0
|
|
await context.executeModule('phase4-overseas-wallet-and-pallet-cost', () => runFixedCustomerProcurement(context,
|
|
async (actualContext, fixture, token, warehouseId) => {
|
|
invoked++
|
|
assert.equal(actualContext, context)
|
|
assert.equal(fixture.tenantId, '101')
|
|
assert.equal(token, 'test-token')
|
|
assert.equal(warehouseId, '201')
|
|
}))
|
|
assert.equal(invoked, 1)
|
|
assert.equal(context.modules[0].skipped, undefined)
|
|
assert.equal(context.modules[0].success, true)
|
|
assert.equal(context.coverageGaps.length, 1)
|
|
assert.equal(calls.at(-1).path, '/api/auth/logout')
|
|
assert.match(source, /if \(context.options\?\.fixedCustomerId\) return runFixedCustomerProcurement\(context\)/)
|
|
})
|
|
|
|
test('错误客户及旧批次资格在任何网络请求前阻断默认入口', async () => {
|
|
for (const patch of [{ tenantId: '102' }, { marker: 'OLD' }, { reused: false }]) {
|
|
const { context, calls } = fixedHarness()
|
|
Object.assign(context.context.merchantQualificationFixture, patch)
|
|
await assert.rejects(run(context), /固定客户资格/)
|
|
assert.equal(calls.length, 0)
|
|
}
|
|
})
|
|
|
|
test('缺少候选仓不建仓;登录身份漂移不执行采购且退出会话', async () => {
|
|
for (const options of [{ warehouse: false }, { profileId: '102' }]) {
|
|
const { context, calls } = fixedHarness(options)
|
|
let invoked = false
|
|
await assert.rejects(runFixedCustomerProcurement(context, async () => { invoked = true }), /合作仓|身份不匹配/)
|
|
assert.equal(invoked, false)
|
|
if (options.profileId) assert.equal(calls.at(-1).path, '/api/auth/logout')
|
|
else assert.equal(calls.length, 1)
|
|
}
|
|
})
|
|
|
|
test('采购执行失败仍退出会话,退出失败保留失败断言', async () => {
|
|
const { context, calls } = fixedHarness()
|
|
await assert.rejects(runFixedCustomerProcurement(context, async () => { throw new Error('unknown result') }), /unknown result/)
|
|
assert.equal(calls.at(-1).path, '/api/auth/logout')
|
|
const failedLogout = fixedHarness({ logoutCode: 'A0409' })
|
|
await runFixedCustomerProcurement(failedLogout.context, async () => {})
|
|
assert.ok(failedLogout.context.steps.some(step => !step.passed))
|
|
})
|
|
|
|
test('已存在采购证据不重复创建样本', async () => {
|
|
const { context, calls } = fixedHarness()
|
|
context.createdResources.procurementSample = { status: 'FAILED' }
|
|
await assert.rejects(runFixedCustomerProcurement(context), /不自动重跑/)
|
|
assert.equal(calls.length, 0)
|
|
})
|
|
|
|
test('采购单号按北京时间日期执行精确合同', () => {
|
|
const instant = new Date('2026-09-05T16:30:00.000Z')
|
|
assert.equal(beijingDate(instant), '20260906')
|
|
assert.equal(matchesProcurementNo('CG202609060001', instant), true)
|
|
assert.equal(matchesProcurementNo('CG202609050001', instant), false)
|
|
assert.equal(matchesProcurementNo('CG20260906001', instant), false)
|
|
assert.equal(matchesProcurementNo('QLYSXDKH202609060001', instant), false)
|
|
})
|
|
|
|
test('钱包闭环覆盖充值、授信、采购冻结结算和换标结算释放', () => {
|
|
for (const token of [
|
|
'/accounts/${encodeURIComponent(customerId)}/credit-limit',
|
|
'creditExpiresOn',
|
|
'/accounts/${encodeURIComponent(customerId)}/adjustments',
|
|
"const CLIENT_FINANCE = '/api/client/finance/v1'",
|
|
"walletStatus === 'FROZEN'",
|
|
"['FREEZE', 'CONSUMPTION', 'UNFREEZE']",
|
|
'item.sourceNo === businessNo',
|
|
"'RELEASED'",
|
|
"terminalStatus === 'COMPLETED'",
|
|
"status: 'CUSTOMER_CONFIRM_PENDING'",
|
|
"status: 'READY_FOR_OUTBOUND'",
|
|
"'CANCELLED'",
|
|
'relabelPayload(context, warehouseId, source, suffix, 0.01)',
|
|
'/relabel-applications/inventory-candidates',
|
|
'operationWarehouseId: id(warehouseId)',
|
|
'sourceInventoryBalanceId:',
|
|
'sourceInventoryPositionId:',
|
|
'sourceInboundOrderId:',
|
|
'destinationFbaWarehouseCode: source.destinationFbaWarehouseCode',
|
|
'cartonMark:',
|
|
'fbaCartonMark:',
|
|
"deliveryMethod: 'TRUCK'",
|
|
]) assert.ok(source.includes(token), token)
|
|
assert.match(source, /actualBalance[\s\S]*frozenBalance[\s\S]*availableBalance[\s\S]*creditLimit[\s\S]*creditUsed[\s\S]*availableCredit/)
|
|
assert.match(source, /verifyProcurementSample\(procurementContext/)
|
|
const sample = readFileSync(new URL('./procurement-sample-fulfillment.mjs', import.meta.url), 'utf8')
|
|
assert.match(sample, /walletStatus === 'SETTLED'/)
|
|
assert.match(sample, /sourceLinked/)
|
|
assert.match(source, /Number\(procurement.settledAmountCny\) - 0.01/)
|
|
assert.match(source, /procurement.walletBusinessNos/)
|
|
assert.doesNotMatch(source, /inboundQuantity|PENDING_OUTBOUND/)
|
|
assert.doesNotMatch(source, /customerOrderNo:[^\n]+overseasWarehouseId:/)
|
|
assert.doesNotMatch(source, /internal\/finance|jdbc|mysql|process\.env|https?:\/\//i)
|
|
})
|
|
|
|
test('托盘汇总和拆柜成本验证总数、分摊及跨区金额守恒', () => {
|
|
for (const token of [
|
|
'/api/platform/v1/pallets/summary',
|
|
'/api/platform/v1/overseas-warehouse-resources/pallets/summary',
|
|
'/pallets/summary?page=1&size=20',
|
|
'OVERSEAS_CONTAINER',
|
|
'DESTUFF_PALLET',
|
|
'expectedPalletCost',
|
|
'sourceFeeId',
|
|
'pollRequests=',
|
|
]) assert.ok(source.includes(token), token)
|
|
assert.doesNotMatch(source, /\/warehouse-admin\/pallets\/summary/)
|
|
assert.match(source, /Number\(d\.totalCount\) === Number\(d\.idleCount\) \+ Number\(d\.occupiedCount\)/)
|
|
assert.match(source, /allocated === money\(fact\.expectedPalletCost\)/)
|
|
assert.match(source, /delivered\.reduce[\s\S]*fact\.expectedPalletCost/)
|
|
assert.match(pda, /unitPrice: Number\(pallet\?\.unitPrice \|\| 0\)/)
|
|
assert.match(pda, /Number\(stored\.unitPrice \|\| pallet\.unitPrice \|\| 0\)/)
|
|
assert.match(pda, /completedContainerFacts\.push/)
|
|
})
|
|
|
|
test('模块进入默认全量并绑定真实商户和海外PDA生产链', () => {
|
|
assert.match(entry, /CONFIGURED_MODULES[^\n]*'phase4-overseas-wallet-and-pallet-cost'/)
|
|
assert.match(entry, /\['phase4-overseas-wallet-and-pallet-cost', \['phase4-overseas-business', 'phase4-overseas-pda-operations'\]\]/)
|
|
assert.match(source, /OPTIONS = \{ scenario: SCENARIO, deploymentBlocker: true \}/)
|
|
assert.match(source, /completedFacts\.length > 0/)
|
|
})
|