160 lines
10 KiB
TypeScript
160 lines
10 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { http } from '@/shared/api/http'
|
|
import { reviewCargoDiscard, updateLastMileOrderPalletMerge } from './lastMileOrders'
|
|
import { changeContainerReceivingWarehouse, convertLastMileContainerToInboundPlans, createManualInboundPlan, getContainerPlanProgress, getLastMileOrder, getLastMileOrderManualNodes, listContainerPlanProgress, listLastMileOrders, transferLastMileOrders, updateLastMileOrderHold, updateLastMileOrderManualNodes } from './lastMileOrders'
|
|
|
|
vi.mock('@/shared/api/http', async (importOriginal) => ({ ...(await importOriginal<typeof import('@/shared/api/http')>()), http: { get: vi.fn(), put: vi.fn(), post: vi.fn() } }))
|
|
|
|
describe('last-mile order overseas API', () => {
|
|
it('reviews a discard by business number with an explicit string customer query', async () => {
|
|
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: { status: 'REJECTED' } })
|
|
await expect(reviewCargoDiscard('CD/TEST-1', '2092618717861048321', 'REJECT', ' 箱数不符 ')).resolves.toEqual({ status: 'REJECTED' })
|
|
expect(http.post).toHaveBeenCalledExactlyOnceWith('/platform/v1/last-leg/cargo-discards/CD%2FTEST-1/review',
|
|
{ decision: 'REJECT', remark: '箱数不符' }, { params: { customerId: '2092618717861048321' } })
|
|
})
|
|
|
|
beforeEach(() => {
|
|
vi.mocked(http.get).mockReset()
|
|
vi.mocked(http.put).mockReset()
|
|
vi.mocked(http.post).mockReset()
|
|
})
|
|
|
|
it('loads all platform orders without a customer scope', async () => {
|
|
const record = { id: '190000000000000099', customerId: '190000000000000001', sourceContainerBookingId: '190000000000000088', customerName: '华远贸易', orderNo: 'LMO-260827-0001' }
|
|
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: { records: [record], page: '2', size: '20', total: '1', pages: '1' } })
|
|
await expect(listLastMileOrders({ page: 2, size: 20, orderType: 'FIRST_LEG', currentStatus: 'PENDING_ASSIGNMENT', warehouse: 'LAX' })).resolves.toEqual({ records: [record], page: 2, size: 20, total: 1, pages: 1 })
|
|
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/orders', {
|
|
params: { page: 2, size: 20, orderType: 'FIRST_LEG', currentStatus: 'PENDING_ASSIGNMENT', warehouse: 'LAX' },
|
|
})
|
|
})
|
|
|
|
it('preserves a long order id as a string in the detail path', async () => {
|
|
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: { order: { id: '190000000000000099' }, cargo: [], statusHistory: [] } })
|
|
await getLastMileOrder('190000000000000099')
|
|
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/orders/190000000000000099')
|
|
})
|
|
|
|
it('does not call outbound or delivery endpoints', () => {
|
|
const source = `${listLastMileOrders}${getLastMileOrder}`
|
|
expect(source).not.toMatch(/outbound|delivery|dispatch/)
|
|
})
|
|
|
|
it('passes all supported list filters and pagination without customerId', async () => {
|
|
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: { records: [], page: 2, size: 50, total: 0, pages: 0 } })
|
|
await listLastMileOrders({
|
|
page: 2, size: 50, orderNo: 'LM-1', inboundCode: 'IN-1', sourceOrderNo: 'SRC-1',
|
|
containerNo: 'CONT-1', status: 'PENDING_DELIVERY', transferWarehouseCode: 'ONT8',
|
|
abnormal: true, detained: false, createdFrom: '2026-08-01T00:00:00', createdTo: '2026-08-31T23:59:59',
|
|
})
|
|
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/orders', {
|
|
params: expect.objectContaining({ page: 2, size: 50, sourceOrderNo: 'SRC-1', transferWarehouseCode: 'ONT8', abnormal: true, detained: false }),
|
|
})
|
|
expect(JSON.stringify(vi.mocked(http.get).mock.calls[0]?.[1])).not.toContain('customerId')
|
|
})
|
|
|
|
it('uses the real destination transfer endpoint and edit contract', async () => {
|
|
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: 1 })
|
|
await transferLastMileOrders({
|
|
orderIds: ['190000000000000099'], warehouseId: 'WH-1', warehouseCode: 'ONT8', warehouseName: 'Ontario',
|
|
})
|
|
expect(http.put).toHaveBeenCalledWith('/platform/v1/last-leg/orders/destination', expect.objectContaining({ orderIds: ['190000000000000099'] }))
|
|
})
|
|
|
|
|
|
|
|
|
|
it('uses the manual inbound-plan endpoint with the complete form and idempotency evidence', async () => {
|
|
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: {} })
|
|
const payload = { orderId: '190000000000000099', customerId: '190000000000000001', expectedOrderVersion: 4,
|
|
palletCount: 8, palletType: 'STANDARD' as const,
|
|
assigned: false, mergeRequired: false, urgent: true, remark: '加急' }
|
|
await createManualInboundPlan(payload, 'plan-key-1')
|
|
expect(http.post).toHaveBeenCalledWith('/platform/v1/last-leg/inbound-plans/manual', payload,
|
|
{ headers: { 'Idempotency-Key': 'plan-key-1' } })
|
|
expect(payload).not.toHaveProperty('inboundCode')
|
|
expect(payload).not.toHaveProperty('plannedArrivalDate')
|
|
expect(payload).not.toHaveProperty('plannedWarehouseId')
|
|
expect(payload).not.toHaveProperty('plannedWarehouseCode')
|
|
expect(payload).not.toHaveProperty('plannedWarehouseName')
|
|
})
|
|
|
|
it('uses versioned idempotent order hold and complete manual-node snapshot endpoints', async () => {
|
|
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: {} })
|
|
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: { nodes: [] } })
|
|
await updateLastMileOrderHold('190000000000000099', { held: true, chargeable: true, reason: '海关查验', expectedVersion: 4 }, 'hold-key-1')
|
|
expect(http.put).toHaveBeenLastCalledWith('/platform/v1/last-leg/orders/190000000000000099/hold',
|
|
{ held: true, chargeable: true, reason: '海关查验', expectedVersion: 4 }, { headers: { 'Idempotency-Key': 'hold-key-1' } })
|
|
await getLastMileOrderManualNodes('190000000000000099')
|
|
expect(http.get).toHaveBeenLastCalledWith('/platform/v1/last-leg/orders/190000000000000099/manual-nodes')
|
|
const nodes = [{ nodeCode: 'ESTIMATED_ARRIVAL' as const, nodeAt: '2026-09-10T10:00:00' }]
|
|
await updateLastMileOrderManualNodes('190000000000000099', 5, nodes, 'node-key-1')
|
|
expect(http.put).toHaveBeenLastCalledWith('/platform/v1/last-leg/orders/190000000000000099/manual-nodes',
|
|
{ expectedVersion: 5, nodes }, { headers: { 'Idempotency-Key': 'node-key-1' } })
|
|
})
|
|
|
|
it('uses one atomic container request without looping individual conversions', async () => {
|
|
vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'success', data: [] })
|
|
await expect(convertLastMileContainerToInboundPlans({
|
|
sourceContainerBookingId: '2092873463568597001',
|
|
snapshotVersion: 9,
|
|
snapshotHash: 'a'.repeat(64),
|
|
}, 'container-plan-idempotency-key')).resolves.toEqual([])
|
|
expect(http.post).toHaveBeenCalledTimes(1)
|
|
expect(http.post).toHaveBeenCalledWith('/platform/v1/last-leg/inbound-plans/convert-container', {
|
|
sourceContainerBookingId: '2092873463568597001',
|
|
snapshotVersion: 9,
|
|
snapshotHash: 'a'.repeat(64),
|
|
}, { headers: { 'Idempotency-Key': 'container-plan-idempotency-key' } })
|
|
})
|
|
|
|
it('loads server-side container progress with pagination and filters', async () => {
|
|
const record = {
|
|
sourceContainerBookingId: '2092873463568597001', containerNo: 'CONT-15', totalOrderCount: 15,
|
|
inboundCodeCount: 13, missingInboundCodeCount: 2, planCount: 10, plannedInboundCodeCount: 10,
|
|
pendingPlanCount: 5, resourcePendingPlanCount: 0, plannedArrivalAt: '2026-09-01T08:00:00', allPlansReady: false, blockingReason: '存在2票缺少入仓码',
|
|
}
|
|
vi.mocked(http.get).mockResolvedValue({
|
|
code: '200', message: 'success', data: { records: [record], page: '2', size: '10', total: '21', pages: '3' },
|
|
})
|
|
await expect(listContainerPlanProgress({
|
|
page: 2, size: 10, containerNo: 'CONT-15',
|
|
createdFrom: '2026-08-01T00:00:00', createdTo: '2026-08-31T23:59:59',
|
|
})).resolves.toEqual({ records: [record], page: 2, size: 10, total: 21, pages: 3 })
|
|
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/orders/container-summaries', {
|
|
params: {
|
|
page: 2, size: 10, containerNo: 'CONT-15',
|
|
createdFrom: '2026-08-01T00:00:00', createdTo: '2026-08-31T23:59:59',
|
|
},
|
|
})
|
|
})
|
|
|
|
it('loads one container detail with the encoded stable long id', async () => {
|
|
const detail = { summary: { sourceContainerBookingId: '2092873463568597001' }, orders: [] }
|
|
vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: detail })
|
|
await expect(getContainerPlanProgress('2092873463568597001/unsafe')).resolves.toEqual(detail)
|
|
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/orders/container-summaries/2092873463568597001%2Funsafe')
|
|
})
|
|
|
|
it('changes one whole container receiving warehouse with version and idempotency evidence', async () => {
|
|
const updated = { sourceContainerBookingId: '2092873463568597001', overseasWarehouseId: '9007199254740993' }
|
|
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: updated })
|
|
await expect(changeContainerReceivingWarehouse('2092873463568597001/unsafe', {
|
|
overseasWarehouseId: '9007199254740993', expectedVersion: 7, remark: '改派洛杉矶接柜',
|
|
}, 'receiving-key-1')).resolves.toEqual(updated)
|
|
expect(http.put).toHaveBeenCalledWith(
|
|
'/platform/v1/last-leg/orders/container-summaries/2092873463568597001%2Funsafe/receiving-warehouse',
|
|
{ overseasWarehouseId: '9007199254740993', expectedVersion: 7, remark: '改派洛杉矶接柜' },
|
|
{ headers: { 'Idempotency-Key': 'receiving-key-1' } },
|
|
)
|
|
})
|
|
})
|
|
|
|
it('sets and cancels a merge group with string IDs and explicit customer scope', async () => {
|
|
vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: null })
|
|
for (const required of [true, false]) {
|
|
await updateLastMileOrderPalletMerge(['2092873463568596994'], '2092618717861048321', required)
|
|
expect(http.put).toHaveBeenLastCalledWith('/platform/v1/last-leg/orders/pallet-merge-required/batch',
|
|
{ orderIds: ['2092873463568596994'], palletMergeRequired: required }, { params: { customerId: '2092618717861048321' } })
|
|
}
|
|
})
|