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 oceanBookingFixture = readFileSync(new URL('./ocean-bookings.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') const supplierDtos = readFileSync(new URL('../../warehouse/src/main/java/com/baiyue/warehouse/dto/SupplierDtos.java', import.meta.url), 'utf8') const supplierService = readFileSync(new URL('../../warehouse/src/main/java/com/baiyue/warehouse/service/SupplierService.java', import.meta.url), 'utf8') const supplierMapper = readFileSync(new URL('../../warehouse/src/main/java/com/baiyue/warehouse/mapper/SupplierMapper.java', import.meta.url), 'utf8') const supplierSchema = readFileSync(new URL('../../warehouse/src/main/resources/db/warehouse-supplier.sql', import.meta.url), 'utf8') const unifiedSql = readFileSync(new URL('../../deploy/sql/头程数据库统一增量-MySQL8.sql', import.meta.url), 'utf8') const oceanBookingController = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/controller/PlatformOceanBookingController.java', import.meta.url), 'utf8') const oceanBookingService = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/service/OceanBookingService.java', import.meta.url), 'utf8') const oceanBookingDtos = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/dto/OceanBookingDtos.java', import.meta.url), 'utf8') const oceanBookingOpenapi = readFileSync(new URL('../../firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml', import.meta.url), 'utf8') const carrierScheduleService = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/portschedule/service/CarrierScheduleQueryService.java', import.meta.url), 'utf8') const adminOceanPage = readFileSync(new URL('../../../apps/admin-web/src/modules/ocean-bookings/pages/OceanBookingDetailView.vue', import.meta.url), 'utf8') const adminOceanApi = readFileSync(new URL('../../../apps/admin-web/src/modules/ocean-bookings/api/platformOceanBookings.ts', import.meta.url), 'utf8') const adminOceanTypes = readFileSync(new URL('../../../apps/admin-web/src/modules/ocean-bookings/types.ts', import.meta.url), 'utf8') const adminBookingAgents = readFileSync(new URL('../../../apps/admin-web/src/modules/ocean-bookings/bookingAgents.ts', import.meta.url), 'utf8') const CARRIER_CODES = ['MATSON', 'SML', 'ZIM', 'COSCO', 'EMC', 'HEDE', 'OTHER'] const matrix = [ { id: 'A', carrierCodes: ['MATSON', 'COSCO'] }, { id: 'B', carrierCodes: ['MATSON'] }, { id: 'C', carrierCodes: ['COSCO'] }, ] const candidates = (carrierCode) => matrix.filter((agent) => agent.carrierCodes.includes(carrierCode)).map((agent) => agent.id) 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, carrierCodes\]/) 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) }) test('HTTP ocean booking acceptance selects an ACTIVE BOOKING supplier without a geographic scope', () => { assert.match(oceanBookingFixture, /resolveOceanBookingAgent\(context, MODULE, 'MATSON', OPTIONS\)/) assert.match(oceanBookingFixture, /bookingAgentId: id\(agent\.id\), bookingAgentName: agent\.name/) assert.match(oceanBookingFixture, /shippingCompanyId: id\(carrier\.id\), shippingCompanyName: carrier\.name, carrierCode: 'MATSON'/) assert.match(oceanBookingFixture, /使用 ACTIVE BOOKING 订舱代理事实源/) assert.doesNotMatch(oceanBookingFixture, /scope=/) }) test('frozen MATSON and COSCO matrix is many-to-many and changing B to COSCO clears the selection', () => { assert.deepEqual(candidates('MATSON'), ['A', 'B']) assert.deepEqual(candidates('COSCO'), ['A', 'C']) let selectedAgentId = 'B' const nextCarrierCode = 'COSCO' if (!candidates(nextCarrierCode).includes(selectedAgentId)) selectedAgentId = null assert.equal(selectedAgentId, null) }) test('supplier DTO, service and HTTP acceptance freeze normalized multi-carrier save and non-BOOKING rejection', () => { assert.match(supplierDtos, /List<@Pattern\(regexp = "MATSON\|SML\|ZIM\|COSCO\|EMC\|HEDE\|OTHER"\) String> carrierCodes/) assert.match(supplierService, /normalizeCarrierCodes\(request\.category\(\), request\.carrierCodes\(\)\)/) assert.match(supplierService, /非BOOKING供应商禁止携带carrierCodes/) assert.match(supplierService, /BOOKING供应商至少关联一个船司标准编码/) assert.match(supplierService, /List\.copyOf\(normalized\)/) assert.match(supplierFixture, /carrierCodes:\['MATSON','COSCO'\]/) assert.match(supplierFixture, /非BOOKING携带carrierCodes返回A0400并保留链路ID/) assert.match(supplierFixture, /code\(nonBookingCarrierResponse,'A0400'\)&&traced\(nonBookingCarrierResponse\)/) }) test('normalized relation table and lookup index exist in module baseline and unified SQL', () => { for (const sql of [supplierSchema, unifiedSql]) { assert.match(sql, /CREATE TABLE IF NOT EXISTS wh_supplier_carrier/) assert.match(sql, /PRIMARY KEY\s*\(?\s*supplier_id\s*,\s*carrier_code\s*\)?/i) assert.match(sql, /idx_wh_supplier_carrier_code\s*\(?\s*tenant_id\s*,\s*carrier_code\s*,\s*supplier_id\s*\)?/i) assert.match(sql, /carrier_code IN\s*\(?\s*'MATSON'\s*,\s*'SML'\s*,\s*'ZIM'\s*,\s*'COSCO'\s*,\s*'EMC'\s*,\s*'HEDE'\s*,\s*'OTHER'\s*\)?/i) } assert.match(supplierMapper, /FROM wh_supplier_carrier[\s\S]*tenant_id=#\{tenantId\} AND carrier_code=#\{carrierCode\}/) }) test('carrier-filtered booking-agent endpoint returns only ACTIVE BOOKING rows linked to the selected code', () => { assert.match(oceanBookingController, /@GetMapping\("\/booking-agents"\)[\s\S]*@RequestParam String carrierCode/) assert.match(firstlegClient, /candidates\([\s\S]*@RequestParam String category,[\s\S]*@RequestParam String carrierCode/) assert.match(firstlegSnapshot, /client\.candidates\(internalToken, "BOOKING", carrierCode\)/) assert.match(oceanBookingService, /bookingAgents\(String carrierCode\)[\s\S]*"ACTIVE"\.equals\(value\.status\(\)\)[\s\S]*"BOOKING"\.equals\(value\.category\(\)\)[\s\S]*value\.carrierCodes\(\)\.contains\(normalized\)/) assert.match(oceanBookingDtos, /record BookingAgentOption\(Long supplierId, String name, List carrierCodes\)/) assert.match(oceanBookingOpenapi, /\/api\/platform\/v1\/ocean-bookings\/booking-agents:/) assert.match(oceanBookingOpenapi, /name: carrierCode[\s\S]*enum: \[MATSON, SML, ZIM, COSCO, EMC, HEDE, OTHER\]/) }) test('Admin loads candidates by carrier and clears an invalid selected agent when the carrier changes', () => { assert.match(adminOceanApi, /listOceanBookingAgents/) assert.match(adminOceanApi, /\$\{basePath\}\/booking-agents/) assert.match(adminOceanTypes, /carrierCodes:\s*OceanBookingCarrierCode\[\]/) assert.match(adminOceanPage, /listOceanBookingAgents\(carrierCode\)/) assert.match(adminOceanPage, /bookingAgentId\s*=\s*null/) assert.match(adminOceanPage, /shouldClearBookingAgent\(form\.data\.bookingAgentId, options\)/) assert.match(adminBookingAgents, /!options\.some\(\(option\) => option\.supplierId === String\(currentSupplierId\)\)/) }) test('bypassed unlinked agent is A0400 with trace and both DRAFT save and RELEASE revalidate the matrix', () => { assert.match(oceanBookingService, /resolveDraft\(request\.data\(\)\)/) assert.match(oceanBookingService, /resolveBookingAgent\(d\.bookingAgentId\(\), d\.bookingAgentName\(\), carrier == null \? null : carrier\.carrierCode\(\)\)/) assert.match(oceanBookingService, /validateMasterData\(before\)/) assert.match(oceanBookingService, /resolveBookingAgent\(booking\.getBookingAgentId\(\), booking\.getBookingAgentName\(\), booking\.getCarrierCode\(\)\)/) assert.match(oceanBookingService, /throw badRequest\("所选订舱代理未关联当前船公司"\)/) const bypass = { http: 200, body: { code: 'A0400', message: '所选订舱代理未关联当前船公司', requestId: 'request-matrix', traceId: 'trace-matrix' } } assert.equal(bypass.http, 200) assert.equal(bypass.body.code, 'A0400') assert.ok(bypass.body.requestId && bypass.body.traceId) }) test('CARRIER master data keeps the fixed enum while shipping-node tasks consume carrierCode only', () => { assert.deepEqual(CARRIER_CODES, ['MATSON', 'SML', 'ZIM', 'COSCO', 'EMC', 'HEDE', 'OTHER']) assert.match(oceanBookingService, /CARRIER_CODES = Set\.of\("MATSON", "SML", "ZIM", "COSCO", "EMC", "HEDE", "OTHER"\)/) assert.match(carrierScheduleService, /setCarrierCode\(strategy\.carrierCode\(\)\)/) assert.doesNotMatch(carrierScheduleService, /carrierCodes\(\)|bookingAgentId|bookingAgentName|wh_supplier_carrier/) }) test('logistics channel contract remains independent from carrier-agent relationships', () => { const channelMethod = oceanBookingService.slice(oceanBookingService.indexOf('public List channels'), oceanBookingService.indexOf('public Detail detail')) const channelDto = oceanBookingDtos.slice(oceanBookingDtos.indexOf('record ChannelOption'), oceanBookingDtos.indexOf('record BookingAgentOption')) assert.match(channelMethod, /priceBooks\.channels/) assert.match(channelMethod, /channelOption/) assert.doesNotMatch(channelMethod, /carrierCodes|bookingAgent|shippingCompany|supplier/) assert.doesNotMatch(channelDto, /carrierCodes|bookingAgent|shippingCompany|supplier/) assert.match(oceanBookingOpenapi, /channelCode:/) })