优化
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
const supplierFixture = readFileSync(new URL('./suppliers.mjs', import.meta.url), 'utf8')
|
||||
const financeFixture = readFileSync(new URL('./finance-full-fixtures.mjs', import.meta.url), 'utf8')
|
||||
const supplierOpenapi = readFileSync(new URL('../../warehouse/src/main/resources/openapi/supplier-v1.yaml', import.meta.url), 'utf8')
|
||||
const firstlegSnapshot = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/service/WarehouseSupplierSnapshotService.java', import.meta.url), 'utf8')
|
||||
const firstlegClient = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/client/WarehouseSupplierClient.java', import.meta.url), 'utf8')
|
||||
|
||||
test('booking supplier fixtures keep ACTIVE and BOOKING but never invent a geographic scope gate', () => {
|
||||
assert.match(supplierFixture, /category:'BOOKING'/)
|
||||
assert.match(supplierFixture, /status:'ACTIVE'/)
|
||||
assert.doesNotMatch(supplierFixture, /scope\s*:/)
|
||||
|
||||
const supplierBody = financeFixture.slice(financeFixture.indexOf('export function buildSupplierBody'),
|
||||
financeFixture.indexOf('async function createOrder'))
|
||||
assert.match(supplierBody, /category,/)
|
||||
assert.match(supplierBody, /status: 'ACTIVE'/)
|
||||
assert.doesNotMatch(supplierBody, /scope|DOMESTIC|INTERNATIONAL/)
|
||||
})
|
||||
|
||||
test('Warehouse supplier contract filters by active category without scope in query, save or snapshot', () => {
|
||||
assert.match(supplierOpenapi, /name: requireActive/)
|
||||
assert.match(supplierOpenapi, /name: category/)
|
||||
assert.match(supplierOpenapi, /required: \[supplierId, name, category, status\]/)
|
||||
assert.doesNotMatch(supplierOpenapi, /\bScope\b|name: scope|\bscope:/)
|
||||
})
|
||||
|
||||
test('Firstleg booking supplier snapshot requires ACTIVE BOOKING and has no domestic/international argument', () => {
|
||||
assert.match(firstlegSnapshot, /return resolve\(supplierId, suppliedName, "BOOKING"\)/)
|
||||
assert.match(firstlegSnapshot, /!"ACTIVE"\.equals\(response\.data\(\)\.status\(\)\)/)
|
||||
assert.match(firstlegSnapshot, /!category\.equals\(response\.data\(\)\.category\(\)\)/)
|
||||
assert.doesNotMatch(firstlegSnapshot, /"INTERNATIONAL"|"DOMESTIC"|String scope/)
|
||||
assert.doesNotMatch(firstlegClient, /String scope|String category,\s*String scope/s)
|
||||
})
|
||||
@@ -45,7 +45,7 @@ test('PAYABLE fee selection uses the formal feeCode and feeTypeName fields', ()
|
||||
test('Finance PAYABLE fixture creates an isolated ACTIVE supplier through the formal API', () => {
|
||||
const body = buildSupplierBody('ADMIN-API-20260815-FINANCE', 'BOOKING')
|
||||
assert.equal(body.category, 'BOOKING')
|
||||
assert.equal(body.scope, 'DOMESTIC')
|
||||
assert.equal(Object.hasOwn(body, 'scope'), false)
|
||||
assert.equal(body.status, 'ACTIVE')
|
||||
assert.match(body.name, /^财务验收供应商-/)
|
||||
assert.match(source, /POST', '\/api\/platform\/v1\/suppliers\?tenantId=0'/)
|
||||
|
||||
@@ -41,7 +41,6 @@ export function buildSupplierBody(marker, category) {
|
||||
return {
|
||||
name: `财务验收供应商-${suffix}`,
|
||||
category,
|
||||
...(category === 'BOOKING' ? { scope: 'DOMESTIC' } : {}),
|
||||
region: '宁波', legalRepresentative: '财务验收法人', contactName: '财务验收联系人',
|
||||
phone: '13800000000', email: `finance-supplier-${suffix || 'fixture'}@example.com`,
|
||||
address: '财务验收地址', businessLicenseNo: `FIN${suffix}`.slice(0, 64),
|
||||
|
||||
@@ -277,7 +277,7 @@ export async function run(context) {
|
||||
`status=${approved?.customsCase?.status || 'missing'}, version=${approved?.customsCase?.version ?? 'missing'}`, OPTIONS)
|
||||
|
||||
const supplierCreateResponse = await context.request('POST', '/api/platform/v1/suppliers?tenantId=0', {
|
||||
name: `报关代理-${context.marker}`.slice(0, 128), category: 'CUSTOMS', scope: null, region: '深圳',
|
||||
name: `报关代理-${context.marker}`.slice(0, 128), category: 'CUSTOMS', region: '深圳',
|
||||
legalRepresentative: '验收法人', contactName: '报关联系人', phone: '13800000000',
|
||||
email: 'customs-acceptance@example.com', address: '深圳市验收地址',
|
||||
businessLicenseNo: `CUSTOMS${context.marker.replace(/\D/g, '')}`.slice(0, 64), licenseFileId: null,
|
||||
|
||||
@@ -6,11 +6,13 @@ const orchestrator=readFileSync(new URL('../admin-test-env-acceptance.mjs',impor
|
||||
const openapi=readFileSync(new URL('../../warehouse/src/main/resources/openapi/supplier-v1.yaml',import.meta.url),'utf8')
|
||||
|
||||
test('supplier acceptance covers full CRUD-like lifecycle and boundary failures',()=>{
|
||||
for(const marker of ['列表分页与订舱筛选','查询四类供应商统计','新增供应商','查询供应商详情','编辑供应商','停用供应商','重复名称返回冲突','非订舱分类携带scope被拒绝','tenantId非0被拒绝','客户账号调用平台供应商接口被拒绝','requestId','traceId']) assert.match(source,new RegExp(marker))
|
||||
for(const marker of ['列表分页与订舱筛选','查询四类供应商统计','新增供应商','查询供应商详情','编辑供应商','停用供应商','重复名称返回冲突','tenantId非0被拒绝','客户账号调用平台供应商接口被拒绝','requestId','traceId']) assert.match(source,new RegExp(marker))
|
||||
assert.doesNotMatch(source,/scope\s*:/)
|
||||
})
|
||||
test('supplier module is registered in the default full acceptance orchestration',()=>assert.match(orchestrator,/CONFIGURED_MODULES = \[[^\]]*'suppliers'/s))
|
||||
test('supplier fixture is retained only as inactive because API has no delete contract',()=>{assert.match(source,/finalStatus:inactive\?'INACTIVE'/);assert.doesNotMatch(source,/context\.request\('DELETE'/)})
|
||||
test('supplier OpenAPI exposes the internal active category and booking scope validation contract',()=>{
|
||||
test('supplier OpenAPI exposes the internal active category validation contract without scope',()=>{
|
||||
for(const marker of ['/internal/warehouse/v1/suppliers/{supplierId}','X-Internal-Service-Token','requireActive','InternalSnapshot','SupplierPageResponse','platform:supplier:list']) assert.match(openapi,new RegExp(marker.replace(/[{}]/g,'\\$&')))
|
||||
assert.doesNotMatch(openapi,/\bscope\b/)
|
||||
assert.doesNotMatch(openapi,/platform:supplier:view/)
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ function records(page) { return Array.isArray(page?.records) ? page.records : []
|
||||
function id(value) { return value == null ? '' : String(value) }
|
||||
function traced(response) { return Boolean(response?.requestId && response?.traceId) }
|
||||
function query(values = {}) { const p = new URLSearchParams(); for (const [k,v] of Object.entries(values)) if (v !== '' && v != null) p.set(k,String(v)); return `${BASE}${p.size ? `?${p}` : ''}` }
|
||||
function payload(marker, overrides = {}) { return { name:`供应商验收-${marker}`,category:'BOOKING',scope:'DOMESTIC',region:'宁波',legalRepresentative:'验收法人',contactName:'验收联系人',phone:'13800000000',email:'supplier-acceptance@example.com',address:'验收地址',businessLicenseNo:`ACPT${marker}`.slice(0,64),licenseFileId:null,status:'ACTIVE',settlementPeriod:'MONTHLY_30',remark:marker,...overrides } }
|
||||
function payload(marker, overrides = {}) { return { name:`供应商验收-${marker}`,category:'BOOKING',region:'宁波',legalRepresentative:'验收法人',contactName:'验收联系人',phone:'13800000000',email:'supplier-acceptance@example.com',address:'验收地址',businessLicenseNo:`ACPT${marker}`.slice(0,64),licenseFileId:null,status:'ACTIVE',settlementPeriod:'MONTHLY_30',remark:marker,...overrides } }
|
||||
|
||||
export async function run(context) {
|
||||
const marker=context.marker.replace(/[^0-9A-Za-z]/g,'').slice(-20)
|
||||
@@ -31,14 +31,11 @@ export async function run(context) {
|
||||
try {
|
||||
const detailResponse=await context.request('GET',`${BASE}/${encodeURIComponent(supplierId)}?tenantId=0`)
|
||||
const detail=context.apiSuccess(MODULE,'查询供应商详情',detailResponse,'',OPTIONS)
|
||||
context.assert(MODULE,'详情字段与平台作用域正确',detailResponse,id(detail?.id)===supplierId&&id(detail?.tenantId)==='0'&&detail?.name===createBody.name&&detail?.scope==='DOMESTIC',`id=${id(detail?.id)}, tenantId=${id(detail?.tenantId)}`,OPTIONS)
|
||||
context.assert(MODULE,'详情字段与平台作用域正确',detailResponse,id(detail?.id)===supplierId&&id(detail?.tenantId)==='0'&&detail?.name===createBody.name&&!Object.hasOwn(detail,'scope'),`id=${id(detail?.id)}, tenantId=${id(detail?.tenantId)}, scopeExposed=${Object.hasOwn(detail||{},'scope')}`,OPTIONS)
|
||||
|
||||
const duplicateResponse=await context.request('POST',`${BASE}?tenantId=0`,payload(`${marker}D`,{name:createBody.name}))
|
||||
context.assert(MODULE,'重复名称返回冲突并保留链路ID',duplicateResponse,code(duplicateResponse,'A0409')&&traced(duplicateResponse),`code=${duplicateResponse.body?.code}, requestId=${duplicateResponse.requestId||'missing'}, traceId=${duplicateResponse.traceId||'missing'}`,OPTIONS)
|
||||
|
||||
const scopeResponse=await context.request('POST',`${BASE}?tenantId=0`,payload(`${marker}S`,{name:`非法范围-${marker}`,category:'CUSTOMS',scope:'DOMESTIC'}))
|
||||
context.assert(MODULE,'非订舱分类携带scope被拒绝',scopeResponse,code(scopeResponse,'A0422')&&traced(scopeResponse),`code=${scopeResponse.body?.code}, requestId=${scopeResponse.requestId||'missing'}, traceId=${scopeResponse.traceId||'missing'}`,OPTIONS)
|
||||
|
||||
const tenantResponse=await context.request('GET',query({tenantId:1,page:1,size:20}))
|
||||
context.assert(MODULE,'tenantId非0被拒绝',tenantResponse,code(tenantResponse,'A0400')&&traced(tenantResponse),`code=${tenantResponse.body?.code}, requestId=${tenantResponse.requestId||'missing'}, traceId=${tenantResponse.traceId||'missing'}`,OPTIONS)
|
||||
|
||||
@@ -47,12 +44,12 @@ export async function run(context) {
|
||||
const forbidden=merchantToken?await context.request('GET',query({tenantId:0,page:1,size:20}),undefined,merchantToken):merchantLogin
|
||||
context.assert(MODULE,'客户账号调用平台供应商接口被拒绝',forbidden,Boolean(merchantToken)&&(forbidden.http===403||code(forbidden,'A0403'))&&traced(forbidden),`HTTP=${forbidden.http}, code=${forbidden.body?.code??'missing'}, requestId=${forbidden.requestId||'missing'}, traceId=${forbidden.traceId||'missing'}`,OPTIONS)
|
||||
|
||||
const updateBody=payload(marker,{name:`供应商验收已编辑-${marker}`,scope:'INTERNATIONAL',contactName:'编辑联系人',settlementPeriod:'MONTHLY_45'})
|
||||
const updateBody=payload(marker,{name:`供应商验收已编辑-${marker}`,contactName:'编辑联系人',settlementPeriod:'MONTHLY_45'})
|
||||
const updateResponse=await context.request('PUT',`${BASE}/${encodeURIComponent(supplierId)}?tenantId=0`,updateBody)
|
||||
context.apiSuccess(MODULE,'编辑供应商',updateResponse,'',OPTIONS)
|
||||
const editedResponse=await context.request('GET',`${BASE}/${encodeURIComponent(supplierId)}?tenantId=0`)
|
||||
const edited=context.apiSuccess(MODULE,'回查编辑结果',editedResponse,'',OPTIONS)
|
||||
context.assert(MODULE,'编辑字段已持久化',editedResponse,edited?.name===updateBody.name&&edited?.scope==='INTERNATIONAL'&&edited?.contactName==='编辑联系人'&&edited?.settlementPeriod==='MONTHLY_45',`name=${edited?.name||'missing'}`,OPTIONS)
|
||||
context.assert(MODULE,'编辑字段已持久化',editedResponse,edited?.name===updateBody.name&&!Object.hasOwn(edited,'scope')&&edited?.contactName==='编辑联系人'&&edited?.settlementPeriod==='MONTHLY_45',`name=${edited?.name||'missing'}, scopeExposed=${Object.hasOwn(edited||{},'scope')}`,OPTIONS)
|
||||
|
||||
const disableResponse=await context.request('PATCH',`${BASE}/${encodeURIComponent(supplierId)}/status?tenantId=0`,{status:'INACTIVE'})
|
||||
context.apiSuccess(MODULE,'停用供应商',disableResponse,'',OPTIONS); inactive=ok(disableResponse)
|
||||
|
||||
@@ -88,7 +88,7 @@ export function truckAppointmentPayload(context, suffix, identity, version = 0)
|
||||
|
||||
async function ensureTruckSupplier(context, suffix) {
|
||||
const response = await request(context, 'POST', SUPPLIER_BASE, {
|
||||
name: `拖车供应商-${suffix}`.slice(0, 128), category: 'TRUCKING', scope: null,
|
||||
name: `拖车供应商-${suffix}`.slice(0, 128), category: 'TRUCKING',
|
||||
region: '深圳', legalRepresentative: '验收法人', contactName: '拖车联系人',
|
||||
phone: '13800000000', email: 'trucking-acceptance@example.com', address: '深圳市验收地址',
|
||||
businessLicenseNo: `TRUCK${suffix}`.slice(0, 64), licenseFileId: null,
|
||||
|
||||
Reference in New Issue
Block a user