test(acceptance): reuse fixed customers and align native fulfillment flows

This commit is contained in:
2026-10-23 04:48:02 +08:00
parent b92f7fe991
commit 139f6b1932
51 changed files with 1076 additions and 228 deletions
@@ -236,24 +236,27 @@ async function verifyFrozenSource(context, fixture) {
}
async function createSecondCustomer(context) {
if (context.options?.fixedCustomerId) throw new Error('缺少第二个固定客户,停止跨客户财务写入')
if (context.options?.fixedCustomerId && !context.options.fixedSecondaryCustomer) throw new Error('缺少第二个固定客户,停止跨客户财务写入')
const originalMarker = context.marker
const originalContext = context.context?.merchantQualificationFixture
const originalResource = context.createdResources?.merchantQualificationFixture
const originalCustomerId = context.context?.customerId
const originalUsername = process.env.MERCHANT_TEST_USERNAME
const originalPassword = process.env.MERCHANT_TEST_PASSWORD
context.marker = `${originalMarker}-FIN-ISO`
try {
const module = await import(new URL('./merchant-qualification-fixture.mjs', import.meta.url))
await module.run(context)
await module.run(context, { publishEnvironment: false, requireDistinctCustomer: Boolean(context.options?.fixedCustomerId) })
const fixture = context.context?.merchantQualificationFixture
if (!fixture?.tenantId || !fixture?.username || !fixture?.password || fixture.marker !== context.marker) return null
const loginResponse = await context.request('POST', '/api/auth/login',
{ loginId: fixture.username, password: fixture.password }, '')
const login = context.apiSuccess(MODULE, '第二隔离客户登录', loginResponse, diagnostic(loginResponse), OPTIONS)
return { customerId: id(fixture.tenantId), token: login?.accessToken }
return { customerId: id(fixture.tenantId), token: login?.accessToken, refreshToken: login?.refreshToken }
} finally {
context.marker = originalMarker
if (originalCustomerId === undefined) delete context.context.customerId
else context.context.customerId = originalCustomerId
if (originalContext) context.context.merchantQualificationFixture = originalContext
else delete context.context.merchantQualificationFixture
if (originalResource) context.createdResources.merchantQualificationFixture = originalResource
@@ -267,32 +270,39 @@ async function createSecondCustomer(context) {
async function verifyCustomerIsolation(context, fixture) {
const second = await createSecondCustomer(context)
const target = rows(fixture.statements).find((item) => statementId(item))
const apSource = rows(fixture.payableSettlements).flatMap((item) => rows(item?.feeSourceIds)).map(id).find(Boolean)
if (!second?.customerId || !second?.token || !target || !apSource) {
blocker(context, '建立第二隔离客户与跨客户单据 Fixture',
`customer=${second?.customerId || 'missing'}, token=${Boolean(second?.token)}, statement=${statementId(target) || 'missing'}, payableSource=${apSource || 'missing'}`,
null, 'second-customer-fixture-unavailable')
return
try {
const target = rows(fixture.statements).find((item) => statementId(item))
const apSource = rows(fixture.payableSettlements).flatMap((item) => rows(item?.feeSourceIds)).map(id).find(Boolean)
if (!second?.customerId || !second?.token || !target || !apSource) {
blocker(context, '建立第二隔离客户与跨客户单据 Fixture',
`customer=${second?.customerId || 'missing'}, token=${Boolean(second?.token)}, statement=${statementId(target) || 'missing'}, payableSource=${apSource || 'missing'}`,
null, 'second-customer-fixture-unavailable')
return
}
const targetId = statementId(target)
const read = await context.request('GET', `${CLIENT}/bills/${encodeURIComponent(targetId)}`, undefined, second.token)
const confirm = await context.request('POST', `${CLIENT}/statements/${encodeURIComponent(targetId)}/confirm`,
{ expectedVersion: Number(target.version ?? 0) }, second.token,
{ 'Idempotency-Key': key(context, 'foreign-confirm') })
const receipt = await write(context, 'POST', `${FINANCE}/receipts`, {
customerId: second.customerId, receivedDate: today(), amount: 0.01, currency: 'CNY',
paymentMethod: 'BANK_TRANSFER', voucherFileIds: [id(fixture.voucherFileId)], remark: context.marker,
allocations: [{ billId: targetId, amount: 0.01 }],
}, 'foreign-receipt')
const payment = await write(context, 'POST', `${FINANCE}/payable-settlements`, {
customerId: second.customerId, supplierId: id(fixture.supplier?.id ?? fixture.supplier?.supplierId),
feeSourceIds: [apSource],
dueDate: today(), currency: 'CNY',
}, 'foreign-payable')
context.assert(MODULE, '第二客户不能读取或确认他人账单且平台不能跨客户分配收付款', confirm,
[read, confirm, receipt, payment].every(rejected),
`read=${diagnostic(read)}; confirm=${diagnostic(confirm)}; receipt=${diagnostic(receipt)}; payment=${diagnostic(payment)}`, OPTIONS)
} finally {
if (second?.token) {
const logout = await context.request('POST', '/api/auth/logout', { refreshToken: second.refreshToken }, second.token)
context.apiSuccess(MODULE, '退出第二固定客户隔离测试会话', logout, diagnostic(logout), OPTIONS)
}
}
const targetId = statementId(target)
const read = await context.request('GET', `${CLIENT}/bills/${encodeURIComponent(targetId)}`, undefined, second.token)
const confirm = await context.request('POST', `${CLIENT}/statements/${encodeURIComponent(targetId)}/confirm`,
{ expectedVersion: Number(target.version ?? 0) }, second.token,
{ 'Idempotency-Key': key(context, 'foreign-confirm') })
const receipt = await write(context, 'POST', `${FINANCE}/receipts`, {
customerId: second.customerId, receivedDate: today(), amount: 0.01, currency: 'CNY',
paymentMethod: 'BANK_TRANSFER', voucherFileIds: [id(fixture.voucherFileId)], remark: context.marker,
allocations: [{ billId: targetId, amount: 0.01 }],
}, 'foreign-receipt')
const payment = await write(context, 'POST', `${FINANCE}/payable-settlements`, {
customerId: second.customerId, supplierId: id(fixture.supplier?.id ?? fixture.supplier?.supplierId),
feeSourceIds: [apSource],
dueDate: today(), currency: 'CNY',
}, 'foreign-payable')
context.assert(MODULE, '第二客户不能读取或确认他人账单且平台不能跨客户分配收付款', confirm,
[read, confirm, receipt, payment].every(rejected),
`read=${diagnostic(read)}; confirm=${diagnostic(confirm)}; receipt=${diagnostic(receipt)}; payment=${diagnostic(payment)}`, OPTIONS)
}
export async function run(context) {