This commit is contained in:
2026-10-27 17:44:41 +08:00
parent 57cd211973
commit db0e72daeb
37 changed files with 701 additions and 671 deletions
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import vm from 'node:vm'
import { qualificationCreditCode, normalizeFixedCustomerId, run } from './merchant-qualification-fixture.mjs'
import { normalizeFixedCustomerId, run } from './merchant-qualification-fixture.mjs'
import { AcceptanceRun } from '../admin-test-env-acceptance.mjs'
function fixedContext(profileId = '2100000000000000001') {
@@ -76,13 +76,26 @@ for (const fault of ['same customer', 'wrong profile', 'missing credentials']) t
else assert.equal(context.calls.at(-1).path, '/api/auth/logout')
})
test('TEST fixture without fixed customer fails before any request', async () => {
for (const environment of ['test', 'gray']) test(`${environment} fixture without fixed customer fails before any request`, async () => {
const context = fixedContext()
context.options = { environment: 'test' }
context.options = { environment }
await assert.rejects(run(context), /必须配置固定客户/)
assert.equal(context.calls.length, 0)
})
test('GRAY fixed customer reuses its credentials and does not publish them into TEST variables', async () => {
const context = fixedContext(); context.options.environment = 'gray'
const before = Object.fromEntries(['MERCHANT_TEST_USERNAME', 'MERCHANT_TEST_PASSWORD', 'MERCHANT_GRAY_USERNAME', 'MERCHANT_GRAY_PASSWORD'].map(key => [key, process.env[key]]))
try {
await run(context)
assert.equal(context.createdResources.merchantQualificationFixture.reused, true)
assert.equal(process.env.MERCHANT_TEST_USERNAME, before.MERCHANT_TEST_USERNAME)
assert.equal(process.env.MERCHANT_TEST_PASSWORD, before.MERCHANT_TEST_PASSWORD)
assert.equal(process.env.MERCHANT_GRAY_USERNAME, context.options.merchantUsername)
assert.equal(context.calls.some(call => call.path === '/api/platform/v1/iam/tenants'), false)
} finally { for (const [key, value] of Object.entries(before)) { if (value === undefined) delete process.env[key]; else process.env[key] = value } }
})
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'],
@@ -133,29 +146,15 @@ const acceptance = readFileSync(new URL('../admin-test-env-acceptance.mjs', impo
const fixture = readFileSync(new URL('./merchant-qualification-fixture.mjs', import.meta.url), 'utf8')
const orders = readFileSync(new URL('./orders.mjs', import.meta.url), 'utf8')
test('credit codes distinguish repeated CUSTOMER-B and SIGNED-B fixtures using the complete marker', () => {
const customerId = '2100000000000000001'
const values = []
for (const ending of ['CUSTOMER-B', 'SIGNED-B']) {
const firstMarker = `ADMIN-API-20260930121500000-${ending}`
const secondMarker = `ADMIN-API-20260930121600000-${ending}`
const first = qualificationCreditCode(firstMarker, customerId)
const second = qualificationCreditCode(secondMarker, customerId)
assert.notEqual(first, second, `时间不同但后缀同为 ${ending} 必须生成不同代码`)
assert.equal(qualificationCreditCode(firstMarker, customerId), first)
for (const value of [first, second]) assert.match(value, /^91440300[0-9A-F]{10}$/)
values.push(first, second)
for (const environment of [undefined, 'dev', 'prod']) test(`unconfigured customer fixture never provisions even outside normal entry: ${environment}`, async () => {
const context = fixedContext(); context.options = { environment }
await assert.rejects(run(context), /必须配置固定客户/)
assert.equal(context.calls.length, 0)
const runner = new AcceptanceRun({ environment })
for (const entry of ['request', 'requestWithBody', 'requestMultipart']) {
for (const path of ['/api/platform/v1/iam/tenants/', '/api/auth/register', '/api/platform/v1/iam/tenants/123/archive'])
await assert.rejects(runner[entry]('POST', path, new FormData()), /固定客户模式禁止/)
}
assert.equal(new Set(values).size, values.length)
assert.match(fixture, /unifiedSocialCreditCode = qualificationCreditCode\(context\.marker, tenantId\)/)
})
test('a new real customer under the same marker receives a distinct stable code', () => {
const marker = 'ADMIN-API-20260930121500000-CUSTOMER-B'
assert.notEqual(qualificationCreditCode(marker, '2100000000000000001'),
qualificationCreditCode(marker, '2100000000000000002'))
assert.throws(() => qualificationCreditCode('', '2100000000000000001'), /完整批次标识/)
assert.throws(() => qualificationCreditCode(marker, ''), /真实客户编号/)
})
test('默认全量验收先准备已审核商户再运行订单链路', () => {
@@ -165,24 +164,12 @@ test('默认全量验收先准备已审核商户再运行订单链路', () => {
assert.match(acceptance, /DEFAULT_MODULES\s*=\s*normalizeModuleOrder\(CONFIGURED_MODULES\)/)
})
test('商户夹具通过正式 File 和 IAM 审核流程建立下单资格', () => {
assert.match(fixture, /files\/qualifications\/\$\{encodeURIComponent\(tenantId\)\}\/required-package/)
assert.match(fixture, /required-package\?unifiedSocialCreditCode=\$\{encodeURIComponent\(unifiedSocialCreditCode\)\}/)
assert.doesNotMatch(fixture, /packageForm\.append\('unifiedSocialCreditCode'/)
assert.match(fixture, /iam\/tenants\/\$\{encodeURIComponent\(tenantId\)\}\/submit-review/)
assert.match(fixture, /iam\/tenants\/\$\{encodeURIComponent\(tenantId\)\}\/dimension-initialization/)
test('商户夹具只核验现有客户,不包含建客、上传资质和审核实现', () => {
assert.match(fixture, /dimension-initialization/)
assert.match(fixture, /status === 'READY'/)
assert.match(fixture, /setTimeout\(resolve, 500\)/)
assert.match(fixture, /iam\/reviews\/\$\{encodeURIComponent\(review\.id\)\}\/approve/)
assert.match(fixture, /item\.status === 'APPROVED'/)
assert.match(fixture, /MERCHANT_TEST_USERNAME/)
assert.match(fixture, /MERCHANT_TEST_PASSWORD/)
assert.match(fixture, /context\.options\?\.customerName/)
assert.match(fixture, /tenantName/)
assert.doesNotMatch(fixture, /expectedProcurementPrefix/)
assert.match(acceptance, /administrator-phone-availability\?phone=/)
assert.match(acceptance, /attempt < 100/)
assert.doesNotMatch(acceptance, /phoneSequence % 10/)
assert.match(fixture, /environment === 'gray' \? 'MERCHANT_GRAY' : 'MERCHANT_TEST'/)
for (const removed of ['required-package', 'submit-review', '/approve', 'administratorUsername', 'temporaryPassword'])
assert.ok(!fixture.includes(removed), removed)
assert.doesNotMatch(fixture, /internal\/iam|UPDATE\s+iam_|INSERT\s+INTO\s+iam_/i)
})