import { BookingPricingService } from './booking-pricing.service'; import type { Booking } from './entities/booking.entity'; import type { Rate } from '../rule-engine/entities/rate.entity'; const MOCK_CBE_RATE = 130; describe('BookingPricingService — domestic corridor', () => { const intercityBulkUsd: Rate = { id: 'rate-intercity-bulk-usd', rateType: 'INTERCITY_BULK', currency: 'USD', rateValue: 35, rateUnit: 'PER_TON', status: 'LIVE', containerTypeId: null, } as Rate; const intercityContainerUsd: Rate = { id: 'rate-intercity-container-usd', rateType: 'INTERCITY_CONTAINER', currency: 'USD', rateValue: 400, rateUnit: 'PER_CONTAINER', status: 'LIVE', containerTypeId: null, } as Rate; let service: BookingPricingService; let bookingsRepository: { calculateWagonCount: jest.Mock }; let ratesService: { findLiveRates: jest.Mock }; let exchangeService: { getRate: jest.Mock }; beforeEach(() => { bookingsRepository = { calculateWagonCount: jest.fn().mockResolvedValue(2) }; ratesService = { findLiveRates: jest.fn().mockResolvedValue([intercityBulkUsd, intercityContainerUsd]), }; exchangeService = { getRate: jest.fn().mockResolvedValue(MOCK_CBE_RATE), }; service = new BookingPricingService( bookingsRepository as never, {} as never, {} as never, ratesService as never, exchangeService as never, { validate20ftPairing: jest.fn().mockResolvedValue([]) } as never, ); }); it('prices domestic bulk in ETB using INTERCITY_BULK USD rate × CBE exchange rate', async () => { const booking = { id: 'b-1', freightType: 'BULK', tradeDirection: 'DOMESTIC', paymentCurrency: 'ETB', cargoTotalWeightVgm: 120, bookingContainers: [], } as unknown as Booking; const result = await ( service as unknown as { computeBaseRailLinesWithRates: ( b: Booking, input: { containers: [] }, ) => Promise<{ lineItems: Array<{ amount: number; code: string; currency: string }> }>; } ).computeBaseRailLinesWithRates(booking, { containers: [] }); expect(result.lineItems).toHaveLength(1); expect(result.lineItems[0].code).toBe('INTERCITY_BULK'); expect(result.lineItems[0].currency).toBe('ETB'); expect(result.lineItems[0].amount).toBe(Math.round(35 * 120 * MOCK_CBE_RATE)); }); it('prices domestic bulk in USD using INTERCITY_BULK USD rate directly', async () => { const booking = { id: 'b-1-usd', freightType: 'BULK', tradeDirection: 'DOMESTIC', paymentCurrency: 'USD', cargoTotalWeightVgm: 120, bookingContainers: [], } as unknown as Booking; const result = await ( service as unknown as { computeBaseRailLinesWithRates: ( b: Booking, input: { containers: [] }, ) => Promise<{ lineItems: Array<{ amount: number; code: string; currency: string }> }>; } ).computeBaseRailLinesWithRates(booking, { containers: [] }); expect(result.lineItems).toHaveLength(1); expect(result.lineItems[0].code).toBe('INTERCITY_BULK'); expect(result.lineItems[0].currency).toBe('USD'); expect(result.lineItems[0].amount).toBe(35 * 120); }); it('prices domestic container in ETB using INTERCITY_CONTAINER USD fallback × CBE rate', async () => { const booking = { id: 'b-2', freightType: 'CONTAINER', tradeDirection: 'DOMESTIC', paymentCurrency: 'ETB', cargoTotalWeightVgm: 50, bookingContainers: [], } as unknown as Booking; const result = await ( service as unknown as { computeBaseRailLinesWithRates: ( b: Booking, input: { containers: Array<{ containerTypeId: string; quantity: number }>; }, ) => Promise<{ lineItems: Array<{ amount: number; code: string; currency: string }> }>; } ).computeBaseRailLinesWithRates(booking, { containers: [{ containerTypeId: 'ct-20', quantity: 3 }], }); expect(result.lineItems.some((l) => l.code === 'INTERCITY_CONTAINER')).toBe(true); const line = result.lineItems.find((l) => l.code === 'INTERCITY_CONTAINER')!; expect(line.currency).toBe('ETB'); }); });