import { BookingJourneyService } from './booking-journey.service'; /** * autoPlaceOnFreedWagons: intercity cargo boards the wagons freed by earlier * unloads. Exercised directly with a stubbed EntityManager — the surrounding * loadBooking flow is integration-tested through the running app. */ describe('BookingJourneyService.autoPlaceOnFreedWagons', () => { const service = new BookingJourneyService( {} as never, // dataSource {} as never, // yardFacilities {} as never, // facilityHandling { emit: jest.fn() } as never, // events ); const schedule = { id: 'sched-1', trainSetId: 'ts-1' }; const booking = { id: 'booking-1', reference: 'BK-1', cargoTotalWeightVgm: 50, freightType: 'CONTAINER', }; const makeManager = (slots: unknown[], existingAllocs: unknown[] = []) => { const savedAllocs: Array> = []; const savedItems: Array> = []; const allocQb = { innerJoinAndSelect: jest.fn().mockReturnThis(), innerJoin: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue(existingAllocs), }; const slotQb = { leftJoinAndSelect: jest.fn().mockReturnThis(), innerJoin: jest.fn().mockReturnThis(), where: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), getMany: jest.fn().mockResolvedValue(slots), }; let allocId = 0; const manager = { getRepository: jest.fn((entity: { name?: string }) => { const name = entity?.name; if (name === 'WagonBookingAllocation') { return { createQueryBuilder: jest.fn(() => allocQb), create: jest.fn((v: Record) => v), save: jest.fn(async (v: Record) => { const row = { ...v, id: `alloc-${++allocId}` }; savedAllocs.push(row); return row; }), update: jest.fn(), }; } if (name === 'TrainSetWagon') { return { createQueryBuilder: jest.fn(() => slotQb) }; } if (name === 'BookingContainer') { return { find: jest.fn().mockResolvedValue([ { id: 'line-1', containerNumber: 'LINE-001', containerTypeId: 'ct-20', units: [{ containerNumber: 'UNIT-001' }, { containerNumber: 'UNIT-002' }], }, ]), }; } if (name === 'WagonAllocationContainerItem') { return { create: jest.fn((v: Record) => v), save: jest.fn(async (v: Record) => { savedItems.push(v); return v; }), }; } throw new Error(`Unexpected repository ${name}`); }), }; return { manager, savedAllocs, savedItems }; }; const call = (manager: unknown) => (service as never as { autoPlaceOnFreedWagons: (m: unknown, s: unknown, b: unknown) => Promise; }).autoPlaceOnFreedWagons(manager, schedule, booking); it('places the booking on freed slots in consist order, with container items', async () => { const slots = [ // Active cargo still riding — NOT freed. { id: 'slot-1', sequenceNo: 1, capacityTons: 60, allocations: [{ status: 'LOADED' }] }, // Freed by an earlier unload. { id: 'slot-2', sequenceNo: 2, capacityTons: 60, allocations: [{ status: 'DEPARTED' }] }, { id: 'slot-3', sequenceNo: 3, capacityTons: 60, allocations: [] }, ]; const { manager, savedAllocs, savedItems } = makeManager(slots); await call(manager); // 50 t fits on the first freed slot alone. expect(savedAllocs).toHaveLength(1); expect(savedAllocs[0]).toMatchObject({ trainSetWagonId: 'slot-2', bookingId: 'booking-1', allocatedWeightTons: 50, status: 'LOADED', }); // One item per physical unit, on the first allocation. expect(savedItems.map((i) => i.containerNumber)).toEqual(['UNIT-001', 'UNIT-002']); expect(savedItems.every((i) => i.wagonBookingAllocationId === 'alloc-1')).toBe(true); }); it('spills over onto the next freed slot when one is not enough', async () => { const slots = [ { id: 'slot-2', sequenceNo: 2, capacityTons: 30, allocations: [{ status: 'DEPARTED' }] }, { id: 'slot-3', sequenceNo: 3, capacityTons: 30, allocations: [] }, ]; const { manager, savedAllocs } = makeManager(slots); await call(manager); expect(savedAllocs.map((a) => [a.trainSetWagonId, a.allocatedWeightTons])).toEqual([ ['slot-2', 30], ['slot-3', 20], ]); }); it('does nothing when the booking already has allocations', async () => { const { manager, savedAllocs } = makeManager([], [{ id: 'existing' }]); await call(manager); expect(savedAllocs).toHaveLength(0); }); it('loads without allocation when no wagon is free', async () => { const slots = [ { id: 'slot-1', sequenceNo: 1, capacityTons: 60, allocations: [{ status: 'LOADED' }] }, ]; const { manager, savedAllocs } = makeManager(slots); await expect(call(manager)).resolves.toBeUndefined(); expect(savedAllocs).toHaveLength(0); }); });