优化
This commit is contained in:
@@ -4,161 +4,73 @@ import test from 'node:test'
|
||||
|
||||
import { oceanBookingDraft, oceanBookingDraftWithoutCustomer, publishPlatformLogisticsChannels, resolveOceanBookingMaster } from './ocean-booking-fixture.mjs'
|
||||
|
||||
test('ocean booking fixture resolves the current inbound customer channel and warehouse', async () => {
|
||||
const read = (name) => readFileSync(new URL(name, import.meta.url), 'utf8')
|
||||
const master = { customerId: '21', customerName: 'IT 测试客户', channelCode: 'SEA-US', channelName: '美西海运', warehouseId: '41', warehouseName: '深圳仓' }
|
||||
|
||||
test('fixture resolves current-marker customer channel and warehouse without inventing data', async () => {
|
||||
const steps = []
|
||||
const context = {
|
||||
marker: 'ADMIN-RUN-1',
|
||||
createdResources: { orchestrationFixtures: { phase3Container: {
|
||||
marker: 'ADMIN-RUN-1', tenantId: '21', inboundId: '31',
|
||||
} } },
|
||||
request: async () => ({ body: { code: '200', data: { summary: {
|
||||
tenantId: 21, customerName: 'IT 测试客户', channelCode: 'SEA-US', channelName: '美西海运',
|
||||
warehouseId: 41, warehouseName: '深圳仓',
|
||||
} } } }),
|
||||
addStep: (...args) => steps.push(args),
|
||||
}
|
||||
const master = await resolveOceanBookingMaster(context, 'test')
|
||||
assert.deepEqual(master, { customerId: '21', customerName: 'IT 测试客户', channelCode: 'SEA-US',
|
||||
channelName: '美西海运', warehouseId: '41', warehouseName: '深圳仓' })
|
||||
const context = { marker: 'ADMIN-RUN-1', createdResources: { orchestrationFixtures: { phase3Container: { marker: 'ADMIN-RUN-1', tenantId: '21', inboundId: '31' } } },
|
||||
request: async () => ({ body: { code: '200', data: { summary: { tenantId: 21, customerName: master.customerName, channelCode: master.channelCode, channelName: master.channelName, warehouseId: 41, warehouseName: master.warehouseName } } } }),
|
||||
addStep: (...args) => steps.push(args) }
|
||||
assert.deepEqual(await resolveOceanBookingMaster(context, 'test'), master)
|
||||
assert.equal(steps.length, 0)
|
||||
const blocked = { marker: 'ADMIN-RUN-2', createdResources: {}, addStep: (...args) => steps.push(args) }
|
||||
assert.equal(await resolveOceanBookingMaster(blocked, 'test'), null)
|
||||
assert.match(steps.at(-1)[4], /missing=customerId,customerName,channelCode,channelName,warehouseId,warehouseName/)
|
||||
})
|
||||
|
||||
test('ocean booking fixture blocks creation instead of inventing missing master data', async () => {
|
||||
const steps = []
|
||||
const context = { marker: 'ADMIN-RUN-2', createdResources: {}, addStep: (...args) => steps.push(args) }
|
||||
assert.equal(await resolveOceanBookingMaster(context, 'test'), null)
|
||||
assert.equal(steps.length, 1)
|
||||
assert.match(steps[0][4], /missing=customerId,customerName,channelCode,channelName,warehouseId,warehouseName/)
|
||||
})
|
||||
|
||||
test('shared booking data omits server-owned names and registers unique real containers', () => {
|
||||
const draft = oceanBookingDraft({ marker: 'ADMIN-RUN-3' }, '3', {
|
||||
customerId: '21', customerName: 'IT 测试客户', channelCode: 'SEA-US', channelName: '美西海运',
|
||||
warehouseId: '41', warehouseName: '深圳仓',
|
||||
})
|
||||
for (const key of ['customerId', 'customerName', 'channelCode', 'warehouseId']) {
|
||||
assert.ok(draft[key], `${key} is required`)
|
||||
}
|
||||
test('shared DRAFT uses requirements and never registers a real container number', () => {
|
||||
const draft = oceanBookingDraft({ marker: 'ADMIN-RUN-3' }, '3', master)
|
||||
assert.equal(draft.channelName, undefined)
|
||||
assert.equal(draft.warehouseName, undefined)
|
||||
assert.equal(draft.shippingCompanyId, null)
|
||||
assert.equal(draft.shippingCompanyName, null)
|
||||
assert.equal(draft.bookingAgentId, null)
|
||||
assert.equal(draft.bookingAgentName, null)
|
||||
assert.deepEqual(draft.containerRequirements, [{ containerType: '40HC', containerKind: 'FCL', quantity: 1, capacityCbm: 76, maxWeightKg: 28000 }])
|
||||
assert.equal(Object.hasOwn(draft, 'containers'), false)
|
||||
assert.equal(JSON.stringify(draft).includes('containerNo'), false)
|
||||
assert.equal(draft.serviceTerm, 'CY-CY')
|
||||
assert.equal(draft.freightTerm, 'NINGBO')
|
||||
assert.deepEqual(draft.containers, [{ containerNo: 'TST-3-01', containerType: '40HC',
|
||||
containerKind: 'FCL', capacityCbm: 76, maxWeightKg: 28000 }])
|
||||
assert.equal(Object.hasOwn(draft, 'containerRequirements'), false)
|
||||
assert.equal(draft.billOfLadingNo, 'BL-3')
|
||||
assert.equal(draft.vesselName, '验收船-3')
|
||||
assert.equal(draft.voyageNo, 'V-3')
|
||||
assert.equal(draft.estimatedSailingAt, '2026-09-01T08:00:00')
|
||||
assert.equal(draft.estimatedArrivalAt, '2026-09-20T08:00:00')
|
||||
const fixtureSource = readFileSync(new URL('./ocean-booking-fixture.mjs', import.meta.url), 'utf8')
|
||||
const builderSource = fixtureSource.slice(fixtureSource.indexOf('export function oceanBookingDraft'), fixtureSource.indexOf('export function oceanBookingDraftWithoutCustomer'))
|
||||
for (const key of ['billOfLadingNo', 'vesselName', 'voyageNo', 'estimatedSailingAt', 'estimatedArrivalAt']) {
|
||||
assert.match(builderSource, new RegExp(`${key}\\s*:`), `${key} must be emitted by the confirmed booking builder`)
|
||||
}
|
||||
for (const field of ['billOfLadingNo', 'vesselName', 'voyageNo', 'estimatedSailingAt', 'estimatedArrivalAt']) assert.ok(draft[field])
|
||||
})
|
||||
|
||||
test('shared booking data never sends supplier names without valid supplier ids', () => {
|
||||
const draft = oceanBookingDraft({ marker: 'ADMIN-RUN-SUPPLIER' }, 'supplier', {
|
||||
customerId: '21', customerName: 'IT 测试客户', channelCode: 'SEA-US', channelName: '美西海运',
|
||||
warehouseId: '41', warehouseName: '深圳仓',
|
||||
}, { shippingCompanyName: '无来源船公司', bookingAgentName: '无来源代理' })
|
||||
test('shared DRAFT permits an omitted customer without inventing placeholders', () => {
|
||||
const draft = oceanBookingDraftWithoutCustomer({ marker: 'ADMIN-RUN-OPTIONAL' }, 'optional', master)
|
||||
assert.equal(Object.hasOwn(draft, 'customerId'), false)
|
||||
assert.equal(Object.hasOwn(draft, 'customerName'), false)
|
||||
assert.equal(draft.containerRequirements[0].quantity, 1)
|
||||
})
|
||||
|
||||
test('explicit requirement rows preserve quantity and fixed catalog facts', () => {
|
||||
const requirements = [
|
||||
{ containerType: '40HC', containerKind: 'FCL', quantity: 2, capacityCbm: 76, maxWeightKg: 28000 },
|
||||
{ containerType: '20GP', containerKind: 'FCL', quantity: 1, capacityCbm: 33, maxWeightKg: 28000 },
|
||||
]
|
||||
const draft = oceanBookingDraft({ marker: 'ADMIN-RUN-MULTI' }, 'multi', master, { containerRequirements: requirements })
|
||||
assert.deepEqual(draft.containerRequirements, requirements)
|
||||
assert.equal(JSON.stringify(draft).includes('containerNo'), false)
|
||||
})
|
||||
|
||||
test('supplier names are emitted only with formal supplier ids', () => {
|
||||
const draft = oceanBookingDraft({ marker: 'ADMIN-RUN-SUPPLIER' }, 'supplier', master,
|
||||
{ shippingCompanyName: '无来源船公司', bookingAgentName: '无来源代理' })
|
||||
assert.equal(draft.shippingCompanyName, null)
|
||||
assert.equal(draft.bookingAgentName, null)
|
||||
})
|
||||
|
||||
test('shared booking draft can omit customer identity without inventing placeholders', () => {
|
||||
const draft = oceanBookingDraftWithoutCustomer({ marker: 'ADMIN-RUN-OPTIONAL' }, 'optional', {
|
||||
customerId: '21', customerName: '不应写入', channelCode: 'SEA-US', channelName: '美西海运',
|
||||
warehouseId: '41', warehouseName: '深圳仓',
|
||||
})
|
||||
assert.equal(Object.hasOwn(draft, 'customerId'), false)
|
||||
assert.equal(Object.hasOwn(draft, 'customerName'), false)
|
||||
assert.equal(draft.channelCode, 'SEA-US')
|
||||
assert.equal(draft.warehouseId, '41')
|
||||
assert.equal(draft.containers[0].containerType, '40HC')
|
||||
assert.equal(draft.containers[0].capacityCbm, 76)
|
||||
assert.equal(draft.containers[0].maxWeightKg, 28000)
|
||||
})
|
||||
|
||||
test('shared booking builder accepts explicit isolated multi-container rows without quantity', () => {
|
||||
const containers = [
|
||||
{ containerNo: 'TST-MULTI-01', containerType: '40HC', containerKind: 'FCL', capacityCbm: 76, maxWeightKg: 28000 },
|
||||
{ containerNo: 'TST-MULTI-02', containerType: '20GP', containerKind: 'FCL', capacityCbm: 33, maxWeightKg: 28000 },
|
||||
]
|
||||
const draft = oceanBookingDraft({ marker: 'ADMIN-RUN-MULTI' }, 'multi', {
|
||||
customerId: '21', customerName: 'IT 测试客户', channelCode: 'SEA-US', channelName: '美西海运',
|
||||
warehouseId: '41', warehouseName: '深圳仓',
|
||||
}, { containers })
|
||||
assert.deepEqual(draft.containers, containers)
|
||||
assert.equal(draft.containers.some((item) => Object.hasOwn(item, 'quantity')), false)
|
||||
})
|
||||
|
||||
test('platform channel producer queries the selected origin warehouse and publishes current-marker resources', async () => {
|
||||
test('platform channel producer uses the selected origin warehouse and publishes current-marker resources', async () => {
|
||||
const calls = []
|
||||
const context = {
|
||||
marker: 'ADMIN-RUN-CHANNELS', createdResources: {},
|
||||
request: async (method, path) => {
|
||||
calls.push({ method, path })
|
||||
return { body: { code: '200', data: [
|
||||
{ channelCode: 'SEA-A', channelName: '海运 A' },
|
||||
{ channelCode: 'SEA-B', channelName: '海运 B' },
|
||||
] } }
|
||||
},
|
||||
apiSuccess: (_module, _name, response) => response.body.data,
|
||||
assert: (_module, _name, _response, condition) => assert.equal(condition, true),
|
||||
}
|
||||
const context = { marker: 'ADMIN-RUN-CHANNELS', createdResources: {}, request: async (method, path) => {
|
||||
calls.push({ method, path }); return { body: { code: '200', data: [{ channelCode: 'SEA-A', channelName: '海运 A' }] } }
|
||||
}, apiSuccess: (_module, _name, response) => response.body.data,
|
||||
assert: (_module, _name, _response, condition) => assert.equal(condition, true) }
|
||||
const catalog = await publishPlatformLogisticsChannels(context, '41', 'test')
|
||||
assert.deepEqual(calls, [{ method: 'GET', path: '/api/platform/v1/ocean-bookings/channels?originWarehouseId=41' }])
|
||||
assert.equal(catalog.marker, context.marker)
|
||||
assert.equal(catalog.originWarehouseId, '41')
|
||||
assert.deepEqual(catalog.channels.map((value) => value.channelCode), ['SEA-A', 'SEA-B'])
|
||||
assert.deepEqual(context.createdResources.platformLogisticsChannels, catalog)
|
||||
})
|
||||
|
||||
test('all direct ocean booking payload builders omit channelName under the frozen DraftData contract', () => {
|
||||
const regions = [
|
||||
['manual-state-loading-pda.mjs', 'const draft = {', 'const createResponse', false],
|
||||
['manual-state-ocean-trucking.mjs', 'function oceanPayload', 'async function createOcean', false],
|
||||
['phase3-loading.mjs', 'const bookingDraft = {', 'const bookingCreate', false],
|
||||
['phase3-order-splitting.mjs', 'function bookingPayload', 'async function loadOneFragment', true],
|
||||
['phase3-container-loading.mjs', 'const bookingDraft = {', "context.assert(MODULE, '订舱渠道与仓库快照完整'", false],
|
||||
]
|
||||
for (const [name, start, end, usesSharedBuilder] of regions) {
|
||||
const value = readFileSync(new URL(name, import.meta.url), 'utf8')
|
||||
const region = value.slice(value.indexOf(start), value.indexOf(end))
|
||||
assert.doesNotMatch(region, /channelName\s*:/, name)
|
||||
if (usesSharedBuilder) {
|
||||
assert.match(region, /oceanBookingDraft\(/, `${name} must use the shared formal booking builder`)
|
||||
} else {
|
||||
for (const field of ['billOfLadingNo', 'vesselName', 'voyageNo', 'estimatedSailingAt', 'estimatedArrivalAt']) {
|
||||
assert.match(region, new RegExp(`${field}\\s*:`), `${name} must register ${field}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
const container = readFileSync(new URL('phase3-container-loading.mjs', import.meta.url), 'utf8')
|
||||
assert.doesNotMatch(container, /data:\s*\{[^}]*channelName\s*:/s)
|
||||
})
|
||||
|
||||
test('ocean booking producers register CONFIRMED data directly without draft submit or confirm transitions', () => {
|
||||
for (const name of ['ocean-bookings.mjs', 'phase3-loading.mjs', 'phase3-container-loading.mjs',
|
||||
'phase3-order-splitting.mjs', 'manual-state-loading-pda.mjs', 'manual-state-ocean-trucking.mjs']) {
|
||||
const source = readFileSync(new URL(name, import.meta.url), 'utf8')
|
||||
assert.doesNotMatch(source, /ocean-bookings\/[^`'"\\s]+\/(?:submit|confirm)|booking-(?:submit|confirm)/, name)
|
||||
test('all production booking fixtures use DRAFT release and the shared requirements contract', () => {
|
||||
for (const name of ['ocean-bookings.mjs', 'phase3-loading.mjs', 'phase3-container-loading.mjs', 'phase3-order-splitting.mjs', 'manual-state-loading-pda.mjs', 'manual-state-ocean-trucking.mjs']) {
|
||||
const source = read(name)
|
||||
assert.doesNotMatch(source, /realContainerBookingId|ocean-bookings\/[^`'"\s]+\/(?:submit|confirm)/, name)
|
||||
assert.match(source, /containerRequirements/, name)
|
||||
}
|
||||
})
|
||||
|
||||
test('all independent ocean booking producers use the shared master-data contract', () => {
|
||||
for (const name of ['ocean-bookings.mjs', 'phase3-port-schedules.mjs', 'phase3-carrier-schedule-queries.mjs']) {
|
||||
const source = readFileSync(new URL(name, import.meta.url), 'utf8')
|
||||
assert.match(source, /resolveOceanBookingMaster\(context, MODULE\)/, name)
|
||||
assert.match(source, /oceanBookingDraft\(context, suffix, master/, name)
|
||||
}
|
||||
const container = readFileSync(new URL('phase3-container-loading.mjs', import.meta.url), 'utf8')
|
||||
for (const snippet of ['channelCode', 'publishPlatformLogisticsChannels', 'warehouseId', 'warehouseName',
|
||||
"containerType: '40HC'", 'capacityCbm: 76', 'maxWeightKg: 28000']) assert.match(container, new RegExp(snippet), snippet)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user