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: (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 } }) })