import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' import { inspectOceanBookingIdentity, platformWarehouseIdentity, truckAppointmentPayload } from './truck-appointments.mjs' test('platform warehouse scope is fixed to zero without requiring response tenantId', () => { assert.deepEqual(platformWarehouseIdentity({ warehouseId: '41', warehouseName: '宁波仓' }), { warehouseId: '41', platformTenantId: '0' }) assert.deepEqual(platformWarehouseIdentity({ id: 42, warehouseName: '义乌仓' }), { warehouseId: '42', platformTenantId: '0' }) const source = readFileSync(new URL('truck-appointments.mjs', import.meta.url), 'utf8') assert.doesNotMatch(source, /item\?\.tenantId|item\.tenantId/) }) test('booking identity is marker scoped and contains no customer snapshot', () => { const result = inspectOceanBookingIdentity('RUN-2', { id: '91', marker: 'RUN-2', warehouseId: '41' }) assert.deepEqual(result.identity, { oceanBookingId: '91', warehouseId: '41' }) assert.equal(Object.hasOwn(result.identity, 'customerId'), false) assert.equal(inspectOceanBookingIdentity('RUN-2', { id: '90', marker: 'RUN-1', warehouseId: '41' }).identity, null) }) test('payload requires booking container and carrier but never emits customer fields', () => { const context = { createdResources: { truckSupplier: { supplierId: '501', supplierName: '正式拖车商' } } } const payload = truckAppointmentPayload(context, '2', { oceanBookingId: '91', containerBookingId: '81', loadingPlanId: '71', warehouseId: '41', billOfLadingNo: 'BL-1', }) assert.deepEqual({ oceanBookingId: payload.oceanBookingId, containerBookingId: payload.containerBookingId, carrierId: payload.carrierId, carrierName: payload.carrierName }, { oceanBookingId: '91', containerBookingId: '81', carrierId: '501', carrierName: '正式拖车商' }) assert.equal(Object.hasOwn(payload, 'customerId'), false) assert.equal(Object.hasOwn(payload, 'customerName'), false) }) test('backend DTO entity mapper service and OpenAPI have no truck customer protocol', () => { const paths = [ '../../firstleg/src/main/java/com/baiyue/firstleg/dto/TruckAppointmentDtos.java', '../../firstleg/src/main/java/com/baiyue/firstleg/entity/TruckAppointmentEntity.java', '../../firstleg/src/main/java/com/baiyue/firstleg/mapper/TruckAppointmentMapper.java', '../../firstleg/src/main/java/com/baiyue/firstleg/service/TruckAppointmentService.java', '../../firstleg/src/main/resources/openapi/truck-appointments-v1.yaml', ] for (const path of paths) { const source = readFileSync(new URL(path, import.meta.url), 'utf8') assert.doesNotMatch(source, /customerId|customerName|customer_id|customer_name/) } }) test('backend freezes released booking real container bill file and carrier rules', () => { const dto = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/dto/TruckAppointmentDtos.java', import.meta.url), 'utf8') assert.match(dto, /@NotNull @Positive Long oceanBookingId/) assert.match(dto, /@NotNull @Positive Long containerBookingId/) assert.match(dto, /@NotNull @Positive Long carrierId/) const service = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/service/TruckAppointmentService.java', import.meta.url), 'utf8') for (const token of ['"RELEASED"', 'containerBookingId未分配至该订舱', 'Iso6346ContainerNumber.require', 'oceanBookingDocuments.requireActiveValidDocument', 'suppliers.trucking', 'uk_truck_active_container']) { assert.ok(service.includes(token), `missing ${token}`) } }) test('HTTP regression is self-contained and covers no-customer and idempotency matrix', () => { const source = readFileSync(new URL('truck-appointments.mjs', import.meta.url), 'utf8') for (const label of ['自包含创建 DRAFT 订舱', '上传当前有效 BILL_OF_LADING 文件', '放舱自包含订舱', '绑定本轮真实 ISO 柜号', '拖车请求不含客户字段', '同柜第二条非终态预约拒绝且零漂移', '同键同载荷重放', '同键重放版本零漂移', '同键异载荷拒绝', '错误订舱拒绝创建', '错误柜号归属拒绝创建', '错误提单拒绝创建', '错误承运商拒绝创建']) assert.match(source, new RegExp(label)) assert.doesNotMatch(source, /phase3ContainerLoading|phase3Loading/) const entry = readFileSync(new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8') assert.match(entry, /\['truck-appointments', \['merchant-qualification-fixture'\]\]/) assert.doesNotMatch(entry, /\['truck-appointments', \[[^\]]*phase3-loading/) }) test('schema owns platform attachments and one active appointment per real container', () => { const sql = readFileSync(new URL('../../firstleg/src/main/resources/db/phase3-truck-appointment-schema.sql', import.meta.url), 'utf8') assert.doesNotMatch(sql, /customer_id|customer_name|idx_truck_appointment_customer/) assert.match(sql, /UNIQUE KEY uk_truck_active_container\(active_container_booking_id\)/) assert.match(sql, /visibility VARCHAR\(24\) NOT NULL DEFAULT 'PLATFORM'/) }) test('method permissions remain independently enforced after removing customer scope', () => { const controller = readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformTruckAppointmentController.java', import.meta.url), 'utf8') for (const permission of ['platform:firstleg:truck-appointment:list', 'platform:firstleg:truck-appointment:create', 'platform:firstleg:truck-appointment:edit', 'platform:firstleg:truck-appointment:operate']) { assert.match(controller, new RegExp(`hasAuthority\\('${permission}'\\)`)) } })