57 lines
4.7 KiB
TypeScript
57 lines
4.7 KiB
TypeScript
import { beforeEach, expect, it, vi } from 'vitest'
|
|
import { http } from '@/shared/api/http'
|
|
import { getWorkCalendar, getWorkSummary, getWorkItems, getWorkDetail, submitWorkCommand, resolveBatchWork } from './workApi'
|
|
import { getCargoVerification, saveCargoVerification } from './cargoVerificationApi'
|
|
import { workCalendar, workDetail, workItem, workSummary } from './work.test-support'
|
|
vi.mock('@/shared/api/http', () => ({ http: { get: vi.fn(), post: vi.fn() }, unwrapApiResponse: <T>(value: { data: T }) => value.data }))
|
|
beforeEach(() => vi.clearAllMocks())
|
|
it('resolves a real batch and optional customer without manufacturing a work key', async () => {
|
|
const resolved = { batchKey: 'FBA_APPOINTMENT:40', customerId: '9007199254740999', detail: workDetail(), candidates: [] }
|
|
vi.mocked(http.get).mockResolvedValue({ data: resolved })
|
|
expect(await resolveBatchWork(resolved.batchKey, resolved.customerId)).toEqual(resolved)
|
|
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/appointment-work/items/resolve', { params: { batchKey: resolved.batchKey, customerId: resolved.customerId }, signal: undefined })
|
|
})
|
|
it('uses inclusive calendar dates and repeated type parameters with no duplicate API prefix', async () => {
|
|
vi.mocked(http.get).mockResolvedValue({ data: workCalendar })
|
|
const signal = new AbortController().signal
|
|
await getWorkCalendar({ from: '2026-10-01', to: '2026-10-31', flow: 'DIRECT', types: ['PICKUP', 'EMPTY_RETURN'] }, signal)
|
|
const [path, config] = vi.mocked(http.get).mock.calls[0]!
|
|
expect(path).toBe('/platform/v1/last-leg/appointment-work/calendar')
|
|
expect(config?.params.getAll('types')).toEqual(['PICKUP', 'EMPTY_RETURN'])
|
|
expect(config?.params.get('to')).toBe('2026-10-31'); expect(config?.signal).toBe(signal)
|
|
})
|
|
it('keeps global summary free of month, flow, types and page parameters', async () => {
|
|
vi.mocked(http.get).mockResolvedValue({ data: workSummary })
|
|
expect(await getWorkSummary()).toEqual(workSummary)
|
|
expect(http.get).toHaveBeenCalledExactlyOnceWith('/platform/v1/last-leg/appointment-work/summary', { signal: undefined })
|
|
})
|
|
it('preserves server totals, null work dates and long string identities', async () => {
|
|
vi.mocked(http.get).mockResolvedValue({ data: { records: [workItem()], page: '2', size: '8', total: '35', pages: '5' } })
|
|
const result = await getWorkItems({ view: 'UNDATED', page: 2, size: 8 })
|
|
expect(result.total).toBe(35); expect(result.records[0]!.calendarDate).toBeNull()
|
|
expect(result.records[0]!.containers[0]!.containerBookingId).toBe('9007199254740993')
|
|
const params = vi.mocked(http.get).mock.calls[0]![1]?.params as URLSearchParams
|
|
expect([...params.keys()]).toEqual(['view', 'page', 'size'])
|
|
})
|
|
it('encodes stable keys and forwards the immutable versioned command', async () => {
|
|
vi.mocked(http.get).mockResolvedValue({ data: workDetail() }); vi.mocked(http.post).mockResolvedValue({ data: { item: workItem(), affectedWorkItemKeys: [] } })
|
|
await getWorkDetail('work/ORDER:1')
|
|
const command = { command: 'CONFIRM_ROUTE' as const, expectedVersion: 3, idempotencyKey: 'same-command', payload: { flow: 'DIRECT' as const, reason: null } }
|
|
await submitWorkCommand('work/ORDER:1', command)
|
|
expect(http.post).toHaveBeenCalledWith('/platform/v1/last-leg/appointment-work/items/work%2FORDER%3A1/commands', command)
|
|
})
|
|
it('does not turn upstream failures into an empty calendar or zero summary', async () => {
|
|
vi.mocked(http.get).mockRejectedValue(new Error('unavailable'))
|
|
await expect(getWorkSummary()).rejects.toThrow('unavailable')
|
|
await expect(getWorkCalendar({ from: '2026-10-01', to: '2026-10-31' })).rejects.toThrow('unavailable')
|
|
})
|
|
it('scopes cargo verification to its explicit customer and never coerces unknown allocation to zero', async () => {
|
|
const source = { sourceKey: 'BOOKING:B/1', customerId: '9007199254740995', destinationKey: 'FBA:ONT8' }
|
|
vi.mocked(http.get).mockResolvedValue({ data: { availablePackages: null, sourceChanged: true } })
|
|
const context = await getCargoVerification(source); expect(context.availablePackages).toBeNull()
|
|
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/appointment-planning/sources/BOOKING%3AB%2F1/cargo-verification', { params: { customerId: source.customerId, destinationKey: source.destinationKey } })
|
|
const request = { destinationKey: source.destinationKey, expectedSourceVersion: null, expectedVersion: 0, packageCount: null, shipmentReferences: ['FBA-TEST'], remark: null, idempotencyKey: 'verify-1' }
|
|
vi.mocked(http.post).mockResolvedValue({ data: { version: 1 } }); await saveCargoVerification(source, request)
|
|
expect(http.post).toHaveBeenCalledWith(expect.stringContaining('/cargo-verification'), request, { params: { customerId: source.customerId } })
|
|
})
|