mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 15:25:45 +00:00
auto allocation and batch managemnt, tracking the train
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
import { BookingBatchService } from './booking-batch.service';
|
||||
import { Booking } from '../bookings/entities/booking.entity';
|
||||
|
||||
describe('BookingBatchService — PAID reconcile', () => {
|
||||
const scheduleId = 'schedule-1';
|
||||
const bookingId = 'booking-1';
|
||||
|
||||
const paidBooking = {
|
||||
id: bookingId,
|
||||
reference: 'BK-2026-000034',
|
||||
trainScheduleId: scheduleId,
|
||||
status: 'PAID',
|
||||
paymentStatus: 'PAID',
|
||||
isGovernment: false,
|
||||
cargoTotalWeightVgm: 20,
|
||||
bookingContainers: [],
|
||||
} as unknown as Booking;
|
||||
|
||||
let service: BookingBatchService;
|
||||
let bookingsRepository: {
|
||||
findPaidUnlinkedForSchedule: jest.Mock;
|
||||
findBatchPool: jest.Mock;
|
||||
findReservedForSchedule: jest.Mock;
|
||||
update: jest.Mock;
|
||||
};
|
||||
let trainScheduleBookingsRepository: {
|
||||
existsForBooking: jest.Mock;
|
||||
createMany: jest.Mock;
|
||||
};
|
||||
let trainSchedulesRepository: {
|
||||
findByIdWithFullGraph: jest.Mock;
|
||||
findAll: jest.Mock;
|
||||
};
|
||||
let trainSchedulingService: {
|
||||
tryAutoWagonAllocation: jest.Mock;
|
||||
};
|
||||
let dataSource: {
|
||||
getRepository: jest.Mock;
|
||||
transaction: jest.Mock;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
bookingsRepository = {
|
||||
findPaidUnlinkedForSchedule: jest.fn().mockResolvedValue([]),
|
||||
findBatchPool: jest.fn().mockResolvedValue([]),
|
||||
findReservedForSchedule: jest.fn().mockResolvedValue([]),
|
||||
update: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
trainScheduleBookingsRepository = {
|
||||
existsForBooking: jest.fn().mockResolvedValue(false),
|
||||
createMany: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
trainSchedulesRepository = {
|
||||
findByIdWithFullGraph: jest.fn().mockResolvedValue({
|
||||
id: scheduleId,
|
||||
maxWagons: 10,
|
||||
bookingWindowStatus: 'OPEN',
|
||||
trainSet: { locomotive: { maxPullWeightTons: 3500, maxTrainLengthMeters: 760 } },
|
||||
scheduleBookings: [],
|
||||
}),
|
||||
findAll: jest.fn().mockResolvedValue([]),
|
||||
};
|
||||
trainSchedulingService = {
|
||||
tryAutoWagonAllocation: jest.fn().mockResolvedValue({
|
||||
assignedBookingIds: [],
|
||||
deferred: [],
|
||||
issues: [],
|
||||
violations: [],
|
||||
}),
|
||||
};
|
||||
|
||||
const bookingRepo = {
|
||||
findOne: jest.fn().mockResolvedValue(paidBooking),
|
||||
update: jest.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
dataSource = {
|
||||
getRepository: jest.fn().mockReturnValue(bookingRepo),
|
||||
transaction: jest.fn(async (fn: (m: unknown) => Promise<void>) => {
|
||||
const manager = {
|
||||
getRepository: () => bookingRepo,
|
||||
};
|
||||
await fn(manager);
|
||||
}),
|
||||
};
|
||||
|
||||
service = new BookingBatchService(
|
||||
dataSource as never,
|
||||
bookingsRepository as never,
|
||||
trainSchedulesRepository as never,
|
||||
trainScheduleBookingsRepository as never,
|
||||
{ payNow: jest.fn(), secured: jest.fn(), expired: jest.fn() } as never,
|
||||
{ addTimeout: jest.fn(), deleteTimeout: jest.fn(), doesExist: jest.fn() } as never,
|
||||
trainSchedulingService as never,
|
||||
);
|
||||
});
|
||||
|
||||
it('reconcilePaidUnlinked links PAID bookings without a schedule row', async () => {
|
||||
bookingsRepository.findPaidUnlinkedForSchedule.mockResolvedValue([paidBooking]);
|
||||
|
||||
await service.reconcilePaidUnlinked(scheduleId);
|
||||
|
||||
expect(bookingsRepository.findPaidUnlinkedForSchedule).toHaveBeenCalledWith(scheduleId);
|
||||
expect(trainScheduleBookingsRepository.createMany).toHaveBeenCalledWith(
|
||||
[{ trainScheduleId: scheduleId, bookingId }],
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('ensurePaidBookingAllocated links PAID booking when not yet linked', async () => {
|
||||
await service.ensurePaidBookingAllocated(bookingId);
|
||||
|
||||
expect(trainScheduleBookingsRepository.createMany).toHaveBeenCalledTimes(1);
|
||||
expect(trainSchedulingService.tryAutoWagonAllocation).toHaveBeenCalledWith(scheduleId);
|
||||
});
|
||||
|
||||
it('ensurePaidBookingAllocated is idempotent when already linked', async () => {
|
||||
trainScheduleBookingsRepository.existsForBooking.mockResolvedValue(true);
|
||||
|
||||
await service.ensurePaidBookingAllocated(bookingId);
|
||||
await service.ensurePaidBookingAllocated(bookingId);
|
||||
|
||||
expect(trainScheduleBookingsRepository.createMany).not.toHaveBeenCalled();
|
||||
expect(trainSchedulingService.tryAutoWagonAllocation).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('processSchedule reconciles PAID-unlinked before wagon allocation', async () => {
|
||||
const fillSpy = jest.spyOn(service, 'fillSchedule').mockResolvedValue(undefined);
|
||||
const settleSpy = jest.spyOn(service, 'settleDueReservations').mockResolvedValue(undefined);
|
||||
const reconcileSpy = jest.spyOn(service, 'reconcilePaidUnlinked').mockResolvedValue(undefined);
|
||||
|
||||
await service.processSchedule(scheduleId);
|
||||
|
||||
expect(fillSpy).toHaveBeenCalledWith(scheduleId);
|
||||
expect(settleSpy).toHaveBeenCalledWith(scheduleId);
|
||||
expect(reconcileSpy).toHaveBeenCalledWith(scheduleId);
|
||||
expect(trainSchedulingService.tryAutoWagonAllocation).toHaveBeenCalledWith(scheduleId);
|
||||
|
||||
const fillOrder = fillSpy.mock.invocationCallOrder[0];
|
||||
const reconcileOrder = reconcileSpy.mock.invocationCallOrder[0];
|
||||
const wagonOrder = trainSchedulingService.tryAutoWagonAllocation.mock.invocationCallOrder[0];
|
||||
expect(fillOrder).toBeLessThan(reconcileOrder);
|
||||
expect(reconcileOrder).toBeLessThan(wagonOrder);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user