This commit is contained in:
2026-10-21 21:30:24 +08:00
parent 234216b3eb
commit 5356a8c898
28 changed files with 1543 additions and 45 deletions
@@ -1,7 +1,96 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { qualificationCreditCode } from './merchant-qualification-fixture.mjs'
import vm from 'node:vm'
import { qualificationCreditCode, normalizeFixedCustomerId, run } from './merchant-qualification-fixture.mjs'
import { AcceptanceRun } from '../admin-test-env-acceptance.mjs'
function fixedContext(profileId = '2100000000000000001') {
const calls = []
return {
calls, marker: 'FIXED-CUSTOMER-TEST', context: {}, createdResources: {},
options: { fixedCustomerId: 'TEN2100000000000000001', merchantUsername: 'fixture-user', merchantPassword: 'fixture-password' },
async request(method, path) {
calls.push({ method, path })
const data = path.endsWith('/login') ? { accessToken: 'fixture-token', refreshToken: 'fixture-refresh' }
: path.endsWith('/profile') ? { id: profileId, status: 'ACTIVE', tenantName: 'Fixture' }
: { status: 'READY' }
return { http: 200, body: { code: 200, data } }
},
apiSuccess(_module, _label, response) { return response.body.data },
assert() {},
}
}
test('fixed customer accepts business number without losing integer precision', () => {
assert.equal(normalizeFixedCustomerId('TEN2100000000000000001'), '2100000000000000001')
assert.throws(() => normalizeFixedCustomerId('TENinvalid'))
})
test('fixed customer preparation only authenticates and reads existing customer', async () => {
const context = fixedContext()
await run(context, { publishEnvironment: false, requireDistinctCustomer: false })
assert.equal(context.context.merchantQualificationFixture.reused, true)
assert.equal(context.context.merchantQualificationFixture.tenantId, '2100000000000000001')
assert.equal(context.context.customerId, '2100000000000000001')
assert.deepEqual(context.calls.filter(call => call.method !== 'GET').map(call => call.path), ['/api/auth/login', '/api/auth/logout'])
assert.equal(context.createdResources.merchantQualificationFixture.password, undefined)
})
test('wrong customer identity stops preparation and logs out', async () => {
const context = fixedContext('2100000000000000002')
await assert.rejects(run(context, { publishEnvironment: false, requireDistinctCustomer: false }), /归属或状态不匹配/)
assert.equal(context.context.merchantQualificationFixture, undefined)
assert.equal(context.calls.at(-1).path, '/api/auth/logout')
assert.equal(context.calls.length, 3)
})
test('second customer fixture cannot silently reuse the first customer', async () => {
const context = fixedContext()
await assert.rejects(run(context, { publishEnvironment: false }), /跨客户场景阻断/)
assert.equal(context.calls.length, 0)
})
test('TEST fixture without fixed customer fails before any request', async () => {
const context = fixedContext()
context.options = { environment: 'test' }
await assert.rejects(run(context), /必须配置固定客户/)
assert.equal(context.calls.length, 0)
})
test('cross-customer financial helpers stop before provisioning or writing', async () => {
for (const [file, name, end] of [
['finance-direct-paid-isolation.mjs', 'createSecondCustomer', 'async function verifyCustomerIsolation'],
['firstleg-container-fee-allocation.mjs', 'provisionCrossCustomerPlan', 'export async function run'],
]) {
const source = readFileSync(new URL(file, import.meta.url), 'utf8')
const helper = source.slice(source.indexOf(`async function ${name}(`), source.indexOf(end))
const scope = vm.createContext({})
vm.runInContext(helper.replaceAll('import.meta.url', "'file:///unused-test-module.mjs'"), scope)
await assert.rejects(scope[name]({ options: { fixedCustomerId: '123' } }), /缺少第二个固定客户/)
}
})
test('fixed resource runner excludes lifecycle handlers without marking them passed', async () => {
const runner = new AcceptanceRun({ environment: 'test', fixedCustomerId: '123', quiet: true })
let calls = 0
for (const name of ['customers', 'supplemental-boundaries', 'system', 'master-data', 'suppliers', 'notifications', 'pricing',
'phase4-overseas-master-data', 'phase4-overseas-wallet-and-pallet-cost']) {
await runner.executeModule(name, async () => { calls++ })
}
assert.equal(calls, 0)
assert.equal(runner.modules.length, 9)
assert.ok(runner.modules.every(module => module.skipped && !module.success))
})
test('both request entrypoints reject customer creation before network access', async () => {
const runner = new AcceptanceRun({ environment: 'test' })
for (const method of ['request', 'requestWithBody']) {
for (const path of ['/api/platform/v1/iam/tenants', '/api/platform/v1/iam/tenants/123/archive', '/api/platform/v1/iam/tenants/123/submit-review']) {
await assert.rejects(runner[method]('POST', path, {}), /禁止创建、归档客户/)
}
}
})
const acceptance = readFileSync(new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8')
const fixture = readFileSync(new URL('./merchant-qualification-fixture.mjs', import.meta.url), 'utf8')