Files
edr-platform/apps/edr-freight-api/src/modules/bookings/bookings.repository.spec.ts

145 lines
5.4 KiB
TypeScript

import { DataSource, Repository } from 'typeorm';
import { Booking } from './entities/booking.entity';
import { BookingsRepository } from './bookings.repository';
function mockQueryBuilder() {
const qb = {
leftJoinAndSelect: jest.fn().mockReturnThis(),
leftJoin: jest.fn().mockReturnThis(),
addSelect: jest.fn().mockReturnThis(),
where: jest.fn().mockReturnThis(),
andWhere: jest.fn().mockReturnThis(),
orderBy: jest.fn().mockReturnThis(),
addOrderBy: jest.fn().mockReturnThis(),
skip: jest.fn().mockReturnThis(),
take: jest.fn().mockReturnThis(),
getMany: jest.fn(),
getManyAndCount: jest.fn().mockResolvedValue([[], 0]),
getCount: jest.fn().mockResolvedValue(0),
getRawAndEntities: jest.fn().mockResolvedValue({ entities: [], raw: [] }),
};
return qb;
}
describe('BookingsRepository', () => {
let repository: jest.Mocked<Repository<Booking>>;
let dataSource: { getRepository: jest.Mock };
let bookingsRepository: BookingsRepository;
beforeEach(() => {
repository = {
createQueryBuilder: jest.fn(),
} as unknown as jest.Mocked<Repository<Booking>>;
dataSource = { getRepository: jest.fn() };
bookingsRepository = new BookingsRepository(repository, dataSource as unknown as DataSource);
});
it('findEligibleForScheduling does not filter by schedule date', async () => {
const qb = mockQueryBuilder();
const bookings = [
{ id: 'b1', scheduledDate: new Date('2026-06-20T08:00:00.000Z') },
{ id: 'b2', scheduledDate: new Date('2026-06-21T14:00:00.000Z') },
];
qb.getMany.mockResolvedValue(bookings);
repository.createQueryBuilder.mockReturnValue(qb as never);
const result = await bookingsRepository.findEligibleForScheduling({
originStationId: 'yard-origin',
destinationStationId: 'yard-destination',
freightType: 'CONTAINER',
});
expect(result).toHaveLength(2);
const dateFilters = qb.andWhere.mock.calls.filter(([clause]) =>
String(clause).includes('scheduled_date'),
);
expect(dateFilters).toHaveLength(0);
});
it('applyListFilters excludes assigned bookings when assignedToSchedule is false', async () => {
const qb = mockQueryBuilder();
repository.createQueryBuilder.mockReturnValue(qb as never);
dataSource.getRepository.mockReturnValue({ find: jest.fn().mockResolvedValue([]) });
await bookingsRepository.findAllPaginated({
page: 1,
pageSize: 10,
assignedToSchedule: 'false',
});
expect(qb.andWhere).toHaveBeenCalledWith(expect.stringContaining('NOT EXISTS'));
});
it('findManualConsolidationCandidates offers an odd-20ft partner booked on another day', async () => {
const qb = mockQueryBuilder();
// Same route/direction, odd 20ft count, but sitting on a different
// scheduled_date than the booking being completed. GL completes both halves
// onto the date chosen on the form, so this is still a legal partner.
qb.getMany.mockResolvedValue([
{
id: 'partner',
reference: 'BK-2026-000303',
scheduledDate: new Date('2026-09-02T08:00:00.000Z'),
bookingContainers: [{ quantity: 1, containerType: { sizeFt: 20 } }],
},
]);
repository.createQueryBuilder.mockReturnValue(qb as never);
const result = await bookingsRepository.findManualConsolidationCandidates({
id: 'own',
originYardId: 'yard-a',
destinationYardId: 'yard-b',
tradeDirection: 'IMPORT',
scheduledDate: new Date('2026-09-01T08:00:00.000Z'),
} as Booking);
expect(result.map((r) => r.booking.reference)).toEqual(['BK-2026-000303']);
expect(result[0].ft20Quantity).toBe(1);
// The booking day must not narrow this list at all.
const dateFilters = qb.andWhere.mock.calls.filter(([clause]) =>
String(clause).includes('scheduled_date'),
);
expect(dateFilters).toHaveLength(0);
});
it('findManualConsolidationCandidates falls back to the requested lines before cargo is persisted', async () => {
const qb = mockQueryBuilder();
// The ordinary state of a CLEARANCE_READY customs booking: container lines
// are written by completion, so there are none yet and the accepted booking
// request is the only statement of what it will carry.
qb.getMany.mockResolvedValue([
{ id: 'b-odd', reference: 'BK-2026-001116', bookingContainers: [] },
{ id: 'b-even', reference: 'BK-EVEN', bookingContainers: [] },
// No request at all — count unknown, so not offerable.
{ id: 'b-unknown', reference: 'BK-UNKNOWN', bookingContainers: [] },
]);
repository.createQueryBuilder.mockReturnValue(qb as never);
dataSource.getRepository.mockReturnValue({
createQueryBuilder: () => ({
where: jest.fn().mockReturnThis(),
getMany: jest.fn().mockResolvedValue([
{
createdBookingId: 'b-odd',
requestedLines: { containers: [{ containerSize: '20ft', quantity: 1 }] },
},
{
createdBookingId: 'b-even',
requestedLines: { containers: [{ containerSize: '20ft', quantity: 2 }] },
},
]),
}),
});
const result = await bookingsRepository.findManualConsolidationCandidates({
id: 'own',
originYardId: 'yard-a',
destinationYardId: 'yard-b',
tradeDirection: 'EXPORT',
} as Booking);
expect(result.map((r) => r.booking.reference)).toEqual(['BK-2026-001116']);
expect(result[0].ft20Quantity).toBe(1);
});
});