import { beforeEach, describe, expect, it, vi } from 'vitest' import { ApiResponseError, http } from '@/shared/api/http' import { adjustPublishedInboundFee, createFeeAdjustmentIdempotencyKey, getLatestInboundFee, } from './api' vi.mock('@/shared/api/http', async () => { const actual = await vi.importActual('@/shared/api/http') return { ...actual, http: { get: vi.fn(), put: vi.fn() } } }) const snapshot = { feeId: '9007199254740993', feeNo: 'FBAF-88-2', feeVersion: 2, feeStatus: 'SENT', inboundId: '88', totalAmount: 1280, } describe('inbound fee API', () => { beforeEach(() => vi.clearAllMocks()) it.each(['', ' ', 'null', 'undefined'])('rejects invalid inboundId %j without issuing HTTP', async (inboundId) => { await expect(getLatestInboundFee('10001', inboundId)).rejects.toThrow('inboundId is required') expect(http.get).not.toHaveBeenCalled() }) it('loads the latest immutable fee snapshot with string IDs', async () => { vi.mocked(http.get).mockResolvedValue({ code: '200', message: 'success', data: snapshot }) await expect(getLatestInboundFee('10001', '88')).resolves.toEqual(snapshot) expect(http.get).toHaveBeenCalledWith('/platform/v1/inbounds/88/fees/latest', { params: { tenantId: '10001' }, }) }) it('adjusts a published snapshot with the required idempotency key', async () => { vi.mocked(http.put).mockResolvedValue({ code: '200', message: 'success', data: { ...snapshot, feeVersion: 3 } }) const payload = { expectedFeeVersion: 2, chargeableQuantity: 140, unitPrice: 8.2, additionalAmount: 80, reason: '补充已确认的附加费' } await expect(adjustPublishedInboundFee('10001', '88', payload, 'fee-adjust-1')) .resolves.toMatchObject({ feeVersion: 3 }) expect(http.put).toHaveBeenCalledWith( '/platform/v1/inbounds/88/fees/published', payload, { params: { tenantId: '10001' }, headers: { 'Idempotency-Key': 'fee-adjust-1' } }, ) }) it('preserves backend conflict metadata for stale-version recovery', async () => { vi.mocked(http.put).mockResolvedValue({ code: 'A0409', message: '费用已被其他操作员修改,请刷新后重试', data: null, requestId: 'request-fee-409', traceId: 'trace-fee-409', }) const error = await adjustPublishedInboundFee( '10001', '88', { expectedFeeVersion: 2, chargeableQuantity: 140, unitPrice: 8.2, additionalAmount: 80, reason: '价格修正' }, 'fee-adjust-2', ).catch((reason: unknown) => reason) expect(error).toBeInstanceOf(ApiResponseError) expect(error).toMatchObject({ code: 'A0409', requestId: 'request-fee-409', traceId: 'trace-fee-409' }) }) it('creates bounded unique keys for adjustment retries', () => { const first = createFeeAdjustmentIdempotencyKey() const second = createFeeAdjustmentIdempotencyKey() expect(first).toMatch(/^fee-adjust-/) expect(first.length).toBeLessThanOrEqual(128) expect(second).not.toBe(first) }) })