import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' import { run } from './suppliers.mjs' import { AcceptanceRun } from '../admin-test-env-acceptance.mjs' const source=readFileSync(new URL('./suppliers.mjs',import.meta.url),'utf8') const orchestrator=readFileSync(new URL('../admin-test-env-acceptance.mjs',import.meta.url),'utf8') 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, carrier matrix and boundary failures',()=>{ for(const marker of ['列表分页与订舱筛选','查询四类供应商统计','新增供应商','查询供应商详情','编辑供应商','停用供应商','重复名称返回冲突','tenantId非0被拒绝','非BOOKING携带carrierCodes返回A0400并保留链路ID','客户账号调用平台供应商接口被拒绝','requestId','traceId']) assert.match(source,new RegExp(marker)) assert.match(source,/carrierCodes:\['MATSON','COSCO'\]/) assert.match(source,/sameCarrierCodes\(detail\?\.carrierCodes,\['MATSON','COSCO'\]\)/) 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 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/) }) function existingHarness({ empty = false, wrongDetail = false, failStats = false } = {}) { const runner = new AcceptanceRun({ environment: 'test', fixedCustomerId: '123', quiet: true }) const calls = [] const categories = ['BOOKING', 'CUSTOMS', 'TRUCKING', 'CERTIFICATE'] runner.request = async (method, path) => { calls.push({ method, path }) assert.equal(method, 'GET', 'fixed suppliers must never mutate master data') const url = new URL(path, 'http://offline.invalid') let data let code = '200' if (url.searchParams.get('tenantId') === '1') code = 'A0400' else if (url.pathname.endsWith('/stats')) { data = { booking: 1, customs: 1, trucking: 1, certificate: 1 } if (failStats) code = 'S0500' } else if (url.searchParams.has('category')) { const category = url.searchParams.get('category') data = { records: empty ? [] : [{ id: String(categories.indexOf(category) + 1), category, status: 'ACTIVE' }], page: 1, size: 20, total: empty ? 0 : 1, pages: empty ? 0 : 1 } } else { const id = url.pathname.split('/').at(-1) data = { id, tenantId: wrongDetail ? '88' : '0', category: categories[Number(id) - 1], status: 'ACTIVE' } } return { http: 200, body: { code, data }, requestId: 'request', traceId: 'trace' } } return { runner, calls } } test('fixed supplier module runs through orchestrator using only existing read-only records', async () => { const { runner, calls } = existingHarness() await runner.executeModule('suppliers', () => run(runner)) assert.equal(calls.length, 10) assert.equal(runner.modules[0].skipped, undefined) assert.equal(runner.modules[0].success, true) assert.equal(runner.coverageGaps.length, 1) assert.equal(runner.coverageGaps[0].scenario, 'fixed-resource-lifecycle') }) test('empty supplier lists retain detail gaps and never create replacements', async () => { const { runner, calls } = existingHarness({ empty: true }) await run(runner) assert.equal(calls.length, 6) assert.equal(runner.coverageGaps.length, 5) assert.ok(runner.steps.every(step => step.passed)) }) test('supplier detail scope drift and failed statistics remain failures', async () => { for (const options of [{ wrongDetail: true }, { failStats: true }]) { const { runner } = existingHarness(options) await runner.executeModule('suppliers', () => run(runner)) assert.equal(runner.modules[0].success, false) assert.ok(runner.steps.some(step => !step.passed)) } })