import { BookingSplitService } from './booking-split.service'; import { Booking } from '../bookings/entities/booking.entity'; import { BookingContainer } from '../bookings/entities/booking-container.entity'; import { BookingContainerUnit } from '../bookings/entities/booking-container-unit.entity'; import { Contract } from '../contracts/entities/contract.entity'; import { BookingBatchOffer } from './entities/booking-batch-offer.entity'; /** * applySplit promotion behaviour: a ONE_TIME contract must be flipped to GENERAL * (both the parent contract row and the booking's denormalized copy) so the split * remainder can be rebooked. A GENERAL booking is left untouched. */ describe('BookingSplitService — applySplit ONE_TIME promotion', () => { const bookingId = 'bk-1'; const contractId = 'ct-1'; const offerId = 'of-1'; const buildService = (bookingContractKind: 'ONE_TIME' | 'GENERAL') => { const offer = { id: offerId, bookingId, status: 'OFFERED', offeredWagons: 3, totalWagons: 5, offeredWeightTons: 30, offeredAmount: 300, offeredPricingBreakdown: {}, offeredLines: null, } as unknown as BookingBatchOffer; const bookingRepo = { update: jest.fn().mockResolvedValue(undefined), findOne: jest.fn().mockResolvedValue({ id: bookingId, contractId, contractKind: bookingContractKind, }), find: jest.fn().mockResolvedValue([]), softDelete: jest.fn().mockResolvedValue(undefined), }; const contractRepo = { update: jest.fn().mockResolvedValue(undefined) }; const offerRepo = { findOne: jest.fn().mockResolvedValue(offer), update: jest.fn().mockResolvedValue(undefined), }; const containerRepo = { find: jest.fn().mockResolvedValue([]), update: jest.fn(), softDelete: jest.fn(), }; const unitRepo = { find: jest.fn().mockResolvedValue([]), softDelete: jest.fn() }; const repoFor = (entity: unknown) => { if (entity === Booking) return bookingRepo; if (entity === Contract) return contractRepo; if (entity === BookingBatchOffer) return offerRepo; if (entity === BookingContainer) return containerRepo; if (entity === BookingContainerUnit) return unitRepo; return { find: jest.fn().mockResolvedValue([]), update: jest.fn() }; }; const dataSource = { getRepository: jest.fn(repoFor), transaction: jest.fn(async (fn: (m: unknown) => Promise) => { await fn({ getRepository: repoFor }); }), }; const service = new BookingSplitService( dataSource as never, {} as never, {} as never, { expirePayable: jest.fn() } as never, { payNowPartial: jest.fn() } as never, ); return { service, bookingRepo, contractRepo }; }; it('promotes a ONE_TIME booking + parent contract to GENERAL', async () => { const { service, bookingRepo, contractRepo } = buildService('ONE_TIME'); await service.applySplit(bookingId); expect(bookingRepo.update).toHaveBeenCalledWith( bookingId, expect.objectContaining({ contractKind: 'GENERAL' }), ); expect(contractRepo.update).toHaveBeenCalledWith( contractId, expect.objectContaining({ contractKind: 'GENERAL' }), ); }); it('leaves a GENERAL booking untouched (no contract promotion)', async () => { const { service, contractRepo } = buildService('GENERAL'); await service.applySplit(bookingId); expect(contractRepo.update).not.toHaveBeenCalled(); }); });