import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' const read = (path) => readFileSync(new URL(path, import.meta.url), 'utf8') const controller = read('../../firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java') const containerDtos = read('../../firstleg/src/main/java/com/baiyue/firstleg/dto/ContainerDtos.java') const containerService = read('../../firstleg/src/main/java/com/baiyue/firstleg/service/ContainerService.java') const oceanService = read('../../firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/service/OceanBookingService.java') const oceanMapper = read('../../firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/mapper/OceanBookingMapper.java') const firstlegOpenApi = read('../../firstleg/src/main/resources/openapi/firstleg-v1.yaml') const oceanOpenApi = read('../../firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml') const loadingFixture = read('phase3-container-loading.mjs') const fullEntry = read('../admin-test-env-acceptance.mjs') const matrix = read('../../../docs/三期/货柜管理全链验收矩阵-2026-08-13.md') test('operational ocean booking list excludes unconfirmed data', () => { assert.match(oceanService, /OPERATIONAL_LIST_STATUSES\s*=\s*Set\.of\([\s\S]*CONFIRMED[\s\S]*CHANGE_PENDING[\s\S]*RELEASED[\s\S]*CANCELLED/) assert.match(oceanMapper, /status IN \('CONFIRMED','CHANGE_PENDING','RELEASED','CANCELLED'\)/) assert.match(oceanOpenApi, /enum: \[CONFIRMED, CHANGE_PENDING, RELEASED, CANCELLED\]/) assert.match(oceanOpenApi, /DRAFT is internal to the create transaction and legacy SUBMITTED\/FAILED values are invalid/) assert.doesNotMatch(oceanOpenApi.match(/name: status[^\n]*/)?.[0] || '', /DRAFT|SUBMITTED/) }) test('simulation creation uses server-owned identity and formal master data', () => { assert.match(controller, /@PostMapping[\s\S]*SimulationCreateRequest request[\s\S]*createSimulation/) assert.match(containerDtos, /SimulationCreateRequest\(@NotBlank[\s\S]*String channelCode[\s\S]*Long warehouseId[\s\S]*String containerType[\s\S]*BigDecimal capacityCbm[\s\S]*BigDecimal maxWeightKg/) assert.match(containerService, /requireDomesticTransitWarehouse\(request\.warehouseId\(\)\)/) assert.match(containerService, /activeVersionId\(\)[\s\S]*priceBooks\.channels\(versionId, warehouse\.id\(\)\)/) assert.match(containerService, /setCabinetNo\("CAB-" \+ value\.getId\(\)\)/) assert.match(firstlegOpenApi, /服务端生成稳定 containerBookingId\/cabinetNo,不要求真实柜号或订舱/) }) test('link consumes one active real booking container through the frozen request', () => { assert.match(controller, /@PostMapping\("\/\{id\}\/ocean-booking-link"\)/) assert.match(controller, /@DeleteMapping\("\/\{id\}\/ocean-booking-link"\)/) assert.match(containerDtos, /OceanBookingLinkRequest\(@NotNull @Positive Long realContainerBookingId,[\s\S]*expectedVersion/) assert.match(containerService, /requireFirstLinkMutable\(container\)/) assert.match(containerService, /repository\.findForUpdate\(request\.realContainerBookingId\(\)\)/) assert.match(containerService, /"OCEAN_BOOKING"\.equals\(source\.getSourceType\(\)\)/) assert.match(containerService, /"ACTIVE"\.equals\(source\.getRecordStatus\(\)\)/) assert.match(containerService, /"CONFIRMED"\.equals\(booking\.getStatus\(\)\)[\s\S]*"RELEASED"\.equals\(booking\.getStatus\(\)\)/) assert.match(containerService, /hasPickTask[\s\S]*hasEvidence[\s\S]*hasDocuments/) assert.match(firstlegOpenApi, /realContainerBookingId/) assert.match(firstlegOpenApi, /原真实柜置 MERGED 且不再出现在正常货柜列表/) }) test('real booking merge locks scope and changes all identities atomically', () => { for (const field of ['warehouseId', 'channelCode', 'containerType', 'capacityCbm', 'maxWeightKg']) { assert.match(containerService, new RegExp(`container\\.get${field[0].toUpperCase()}${field.slice(1)}\\(\\)`), field) } assert.match(containerService, /hasMismatchedCustomerItems/) assert.match(containerService, /consumeRealSource\(source\.getId\(\), id/) assert.match(containerService, /mergeRealSource\(id, source\.getId\(\), source\.getContainerNo\(\)/) assert.match(containerService, /reassignAllocation\(source\.getId\(\), id\)/) assert.doesNotMatch(containerService.match(/linkOceanBooking[\s\S]*?return required\(id\);/)?.[0] || '', /container\.setId\(|container\.setCabinetNo\(/) }) test('booking creation atomically registers one real operational container per input row', () => { assert.match(oceanOpenApi, /containers: \{type: array, minItems: 1, maxItems: 20/) assert.match(oceanOpenApi, /required: \[containerNo, containerType, containerKind, capacityCbm, maxWeightKg\]/) assert.match(oceanService, /request\.data\(\)\.containers\(\)\.forEach/) assert.match(oceanService, /同一订舱内集装箱号不能重复/) assert.match(oceanService, /createRealContainers\(value, requirements, request\.data\(\)\.containers\(\)/) assert.match(oceanService, /setSourceType\("OCEAN_BOOKING"\)/) assert.match(oceanService, /setRecordStatus\("ACTIVE"\)/) assert.match(oceanService, /setQuantity\(1\)/) }) test('HTTP fixture follows simulation arrange link PDA sequence without auto-container fallback', () => { const ordered = [ '新增内部模拟柜', '人工分配本轮候选货物', '首次关联真实已确认订舱柜', '查询本轮柜次 PDA 拣货任务', ].map((label) => loadingFixture.indexOf(label)) assert.ok(ordered.every((value) => value >= 0), `missing sequence label: ${ordered.join(',')}`) assert.ok(ordered.every((value, index) => index === 0 || value > ordered[index - 1]), `invalid sequence: ${ordered.join(',')}`) assert.match(loadingFixture, /ocean-booking-link/) assert.match(loadingFixture, /关联前后内部柜次、计划和排货明细身份稳定/) assert.match(loadingFixture, /已消费 MERGED 真实柜拒绝再次关联/) assert.match(loadingFixture, /已有排货后拒绝解绑或换绑/) assert.match(loadingFixture, /合并后列表仅保留模拟柜原地转成的一条 ACTIVE 操作身份/) assert.match(loadingFixture, /订舱真实柜 allocation 已原子改指稳定模拟柜身份/) assert.doesNotMatch(loadingFixture, /确认订舱并自动生成|确认响应包含自动生成/) }) test('default full acceptance preserves the linked simulation chain before downstream completion', () => { const loading = fullEntry.indexOf("'phase3-container-loading'") const pda = fullEntry.indexOf("'phase3-pda-convergence'") const finalize = fullEntry.indexOf("'phase3-loading-finalize'") const outbound = fullEntry.indexOf("'phase3-outbound-dispatch'") assert.ok(loading >= 0 && pda > loading && finalize > pda && outbound > finalize) assert.match(matrix, /内部模拟柜关联真实订舱增量/) assert.doesNotMatch(matrix, /CONFIRMED` 自动建柜[\s\S]*既有冻结/) })