import { beforeEach, expect, it, vi } from 'vitest' import { http, ApiResponseError } from '@/shared/api/http' import { listFleetQuotes, createFleetQuote, updateFleetQuote, importFleetQuotes, fleetMoney, fleetVersion, isFleetCommandRejected, type FleetQuoteDraft } from './fleetQuotes' vi.mock('@/shared/api/http', async original => ({ ...(await original()), http: { get: vi.fn(), post: vi.fn(), put: vi.fn() } })) const details: FleetQuoteDraft = { kind: 'SALE', partyId: '90071992547409931', originCity: 'LA', destinationCode: 'ONT8', currency: 'USD', distanceMiles: 10, selfPickupPrice: 0, fullDeliveryPrice: null, looseDeliveryPrice: 2, validFrom: '2026-01-01', validTo: '2026-12-31', remark: null } const quote = { quoteNo: 'FQ-ONE', details: { ...details, selfPickupPrice: '0', looseDeliveryPrice: '2', distanceMiles: '10' }, partyName: '委托企业', status: 'ACTIVE', recordVersion: '90071992547409931', updatedAt: '2026-10-01T00:00:00Z' } const ok = (data: unknown) => ({ code: '200', data }) beforeEach(() => vi.clearAllMocks()) it('preserves long IDs and versions, explicit zero and absent prices', async () => { vi.mocked(http.get).mockResolvedValue(ok({ records: [quote], total: '1', size: '20', page: '1', pages: '1' })) const result = await listFleetQuotes({ kind: 'SALE' }) expect(result.records[0]).toMatchObject({ details, recordVersion: '90071992547409931' }) expect(result.total).toBe(1) vi.mocked(http.put).mockResolvedValue(ok(quote)); await updateFleetQuote(result.records[0]!, details) expect(http.put).toHaveBeenCalledWith('/platform/v1/last-leg/fleet/quotes/FQ-ONE', { recordVersion: '90071992547409931', details }) }) it('sends stable creation and batch keys without converting business references', async () => { vi.mocked(http.post).mockResolvedValue(ok(quote)); await createFleetQuote('stable-key', details) expect(http.post).toHaveBeenCalledWith('/platform/v1/last-leg/fleet/quotes', { idempotencyKey: 'stable-key', details }) vi.mocked(http.post).mockResolvedValue(ok({ importedRows: 1 })); await importFleetQuotes('batch-key', [details]) expect(http.post).toHaveBeenLastCalledWith('/platform/v1/last-leg/fleet/quotes/import', { idempotencyKey: 'batch-key', rows: [details] }) }) it('rejects lossy money and numeric versions instead of rounding them', () => { expect(() => fleetMoney('99999999999999999.01')).toThrow('精确展示') expect(() => fleetVersion(9007199254740992)).toThrow('版本无效') expect(fleetVersion('90071992547409931')).toBe('90071992547409931') }) it('recognizes deployed A04xx business rejection without treating S0500 or network failure as a definite abort', () => { for (const code of ['A0400', 'A0409', 'A0422']) expect(isFleetCommandRejected(new ApiResponseError('业务拒绝', code, 200))).toBe(true) expect(isFleetCommandRejected(new ApiResponseError('服务器结果不明', 'S0500', 200))).toBe(false) expect(isFleetCommandRejected(new Error('Network error'))).toBe(false) })