Files
baiyue-firstleg/scripts/admin-test-env-modules/ocean-booking-prototype-contract.test.mjs
T

64 lines
3.9 KiB
JavaScript

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 adminPage = read('../../../apps/admin-web/src/modules/ocean-bookings/pages/OceanBookingDetailView.vue')
const adminTypes = read('../../../apps/admin-web/src/modules/ocean-bookings/types.ts')
const prototype = read('../../../docs/悦/货代SAAS平台后台/prototype-src/modules/06-containers.html')
const dto = read('../../firstleg/src/main/java/com/baiyue/firstleg/oceanbooking/dto/OceanBookingDtos.java')
const openapi = read('../../firstleg/src/main/resources/openapi/ocean-bookings-v1.yaml')
const fixture = read('ocean-booking-fixture.mjs')
const booking = read('ocean-bookings.mjs')
const loading = read('phase3-container-loading.mjs')
const transportFields = ['billOfLadingNo', 'vesselName', 'voyageNo', 'estimatedSailingAt', 'estimatedArrivalAt']
test('prototype evidence keeps terms and optional customer while current DRAFT stays editable', () => {
assert.match(prototype, /主要运输条款 Service Term \*<\/label><select><option>CY-CY<\/option>/)
assert.match(prototype, /运费条款 Freight Term \*<\/label><select><option>NINGBO<\/option>/)
assert.match(prototype, /<label>客户\/公司名称<\/label><input id="booking-customer"/)
assert.match(adminPage, /const editable = computed\(\(\) => isNew\.value \|\| detail\.value\?\.status === 'DRAFT'\)/)
assert.match(adminTypes, /customerId:\s*string \| null;\s*customerName:\s*string \| null/)
assert.match(dto, /record DraftData\(@Positive Long customerId/)
assert.match(openapi, /customerId: \{[^\n]*nullable: true/)
})
test('DRAFT can be partial but release validates and freezes the five transport facts', () => {
assert.match(openapi, /description: Fields may be incomplete in DRAFT\. RELEASE validates the complete persisted aggregate/)
assert.match(openapi, /ReleaseRequest: \{type: object, required: \[expectedVersion\]/)
for (const field of transportFields) {
assert.match(adminPage, new RegExp(`form\\.data\\.${field}`), field)
assert.match(fixture, new RegExp(`${field}:`), field)
}
assert.match(booking, /const frozen = transportSnapshot\(completedDraft\?\.oceanBooking \|\| data\)/)
assert.match(booking, /documentType', 'BOOKING_CONFIRMATION'/)
assert.match(booking, /\/documents\/upload/)
assert.match(booking, /expectedVersion: documentVersion/)
assert.match(booking, /完整 DRAFT 放舱为 RELEASED 且冻结五项运输事实/)
assert.match(booking, /Object\.entries\(frozen\)\.every/)
assert.match(booking, /RELEASED 编辑拒绝且零写入/)
})
test('booking requirements replace booking-time real container rows', () => {
assert.match(openapi, /containerRequirements:/)
assert.match(openapi, /ContainerRequirementRequest: \{type: object, required: \[containerType, containerKind, quantity, capacityCbm, maxWeightKg\]/)
assert.match(fixture, /containerRequirements/)
assert.doesNotMatch(fixture, /containerNo|realContainerBookingId/)
assert.match(booking, /订舱创建返回 DRAFT 且只持久化柜型需求/)
assert.match(booking, /没有预生成货柜/)
})
test('port link supplies the real number and preserves current-marker customer isolation', () => {
assert.match(loading, /oceanBookingId: bookingId, requirementId: id\(requirement\.id\), containerNo: realContainerNo/)
assert.match(loading, /source\?\.marker !== context\.marker/)
assert.match(loading, /id\(candidate\?\.accountId\) === tenantId/)
assert.match(loading, /id\(item\.accountId\) === tenantId/)
assert.doesNotMatch(loading, /realContainerBookingId|MERGED/)
})
test('normal booking producers have only DRAFT and RELEASED lifecycle operations', () => {
assert.doesNotMatch(booking, /CHANGE_PENDING|CANCELLED|\/submit|\/confirm/)
assert.match(booking, /status === 'DRAFT'/)
assert.match(booking, /status === 'RELEASED'/)
})