import { beforeEach, describe, expect, it, vi } from 'vitest' import { http } from '@/shared/api/http' import { projectContainerSchedule } from './schedule' import { getCalendarSources, getWarehouseArrivals } from './scheduleApi' vi.mock('@/shared/api/http', async original => ({ ...await original(), http: { get: vi.fn(), post: vi.fn() }, })) const range = { from: '2026-09-28', to: '2026-11-02' } const bookingId = '9007199254740993' // 使用真实接口的结构:Long 数量序列化为字符串,空时间字段不输出。 const wire = { sourceContainerBookingId: bookingId, containerNo: 'CALENDAR-CONT', warehouseName: '接仓仓库', warehouseCode: 'US-W', version: 0, planCount: '0', canEdit: true, } beforeEach(() => { vi.clearAllMocks() vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'OK', data: { containers: [{ containerBookingId: bookingId, cabinetNo: 'D-CALENDAR', containerNo: 'CALENDAR-CONT', vesselName: null, voyageNo: null, destinationPortName: null, destinationWarehouseCode: null, arrivalAt: '2026-10-20T23:00:00', arrivalType: 'ESTIMATED' }], emptyReturns: [], emptyReturnsReadable: true, } }) }) async function readSchedule(overrides: Record = {}) { vi.mocked(http.post).mockResolvedValue({ code: '200', message: 'OK', data: [{ ...wire, ...overrides }] }) const sources = await getCalendarSources(range) const warehouses = await getWarehouseArrivals(range, [bookingId]) return { warehouses, event: projectContainerSchedule(sources, warehouses, range, true).find(row => row.type === 'warehouse') } } describe('warehouse calendar response to schedule', () => { it.each(['0', 0])('uses zero plan count %j to show arrival plus two days without inventing a time', async planCount => { const { warehouses, event } = await readSchedule({ planCount, plannedDate: '2026-10-22', isDefault: true }) expect(warehouses[0]).toMatchObject({ sourceContainerBookingId: bookingId, planCount: 0, plannedAt: null, actualAt: null }) expect(event).toMatchObject({ date: '2026-10-22', time: '', canEdit: true, subtitle: '到港日期+2个自然日(默认)' }) expect(event?.fields).toContainEqual({ label: '计划到仓', value: '2026-10-22' }) expect(event?.fields).toContainEqual({ label: '实际到仓', value: '—' }) }) it('keeps the authoritative frozen plan instead of recalculating from arrival', async () => { const { warehouses, event } = await readSchedule({ planCount: '2', plannedAt: '2026-10-25T09:30:00', canEdit: false }) expect(warehouses[0]?.planCount).toBe(2) expect(event).toMatchObject({ date: '2026-10-25', time: '09:30', canEdit: false, subtitle: '已有入库计划,日期已冻结' }) }) it('keeps a manually maintained time when there is no inbound plan', async () => { const { event } = await readSchedule({ version: 1, plannedAt: '2026-10-24T08:00:00' }) expect(event).toMatchObject({ date: '2026-10-24', time: '08:00', subtitle: '人工维护' }) }) it('does not calculate a default when existing plans have no unified time', async () => { const { event } = await readSchedule({ planCount: '2', canEdit: false }) expect(event?.date).toBeNull() }) it('keeps conflicting authoritative versions unresolved even with zero plans', async () => { const { event } = await readSchedule({ version: -1, canEdit: false }) expect(event).toMatchObject({ date: null, canEdit: false, subtitle: '柜内计划时间不一致,待核实' }) }) it.each([undefined, null, '', 'invalid', '-1', '1.5', '9007199254740992', -1, Infinity])( 'rejects invalid or unsafe plan count %j rather than treating it as no plans', async planCount => { await expect(readSchedule({ planCount })).rejects.toThrow('入库计划数量无效') }, ) })