import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query'; import { PassengerOrientedSelector } from '@modules/ad/core/application/queries/match/selector/passenger-oriented.selector'; import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types'; import { AdEntity } from '@modules/ad/core/domain/ad.entity'; import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types'; import { ScheduleItemProps } from '@modules/ad/core/domain/value-objects/schedule-item.value-object'; import { AdRepository } from '@modules/ad/infrastructure/ad.repository'; import { PrismaService } from '@modules/ad/infrastructure/prisma.service'; import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto'; import { bareMockGeorouter } from '../unit/georouter.mock'; import { Marseille, Nice, SaintRaphael, Toulon, driverNiceMarseille, monday, passengerToulonMarseille, thursday, wednesday, } from './ad.fixtures'; import { integrationTestingModule } from './integration.setup'; function baseMatchQuery( frequency: Frequency, dates: [string, string], scheduleItems: ScheduleItemProps[], waypoints: WaypointDto[], ): MatchQuery { return new MatchQuery( { algorithmType: AlgorithmType.PASSENGER_ORIENTED, driver: false, passenger: false, frequency: frequency, fromDate: dates[0], toDate: dates[1], useAzimuth: false, useProportion: false, remoteness: 15000, schedule: scheduleItems, strict: false, waypoints: waypoints, }, bareMockGeorouter, ); } function passengerQueryToulonMarseille( frequency: Frequency, dates: [string, string], scheduleItems: ScheduleItemProps[], ): MatchQuery { const matchQuery = baseMatchQuery(frequency, dates, scheduleItems, [ { position: 0, ...Toulon }, { position: 1, ...Marseille }, ]); matchQuery.passenger = true; matchQuery.passengerRoute = { distance: 64000, duration: 2460, points: [Toulon, Marseille], // Not used by this query fwdAzimuth: 0, backAzimuth: 0, distanceAzimuth: 0, }; return matchQuery; } function driverQueryNiceMarseille( frequency: Frequency, dates: [string, string], scheduleItems: ScheduleItemProps[], ): MatchQuery { const matchQuery = baseMatchQuery(frequency, dates, scheduleItems, [ { position: 0, ...Nice }, { position: 1, ...Marseille }, ]); matchQuery.driver = true; matchQuery.driverRoute = { distance: 199000, duration: 7668, points: [Nice, SaintRaphael, Toulon, Marseille], // Not used by this query fwdAzimuth: 0, backAzimuth: 0, distanceAzimuth: 0, }; return matchQuery; } describe('PassengerOriented selector', () => { let prismaService: PrismaService; let adRepository: AdRepository; const insertAd = async (adProps: CreateAdProps): Promise => { const ad = AdEntity.create(adProps); return adRepository.insertExtra(ad, 'ad'); }; beforeAll(async () => { ({ prismaService, adRepository } = await integrationTestingModule()); }); afterAll(async () => { await prismaService.$disconnect(); }); beforeEach(async () => { await prismaService.ad.deleteMany(); }); describe('select', () => { it('should find a driver that departs on the same day', async () => { await insertAd( driverNiceMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('08:30')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( passengerQueryToulonMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('10:00')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); it('should find a passenger that departs on the same day', async () => { await insertAd( passengerToulonMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('10:00')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( driverQueryNiceMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('08:30')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); it('should find a driver that departs the day before', async () => { await insertAd( driverNiceMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('23:45')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( passengerQueryToulonMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('01:15')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); it('should find a passenger that departs the day after', async () => { await insertAd( passengerToulonMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('01:15')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( driverQueryNiceMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('23:45')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); it('should find a driver that departs shortly after midnight', async () => { await insertAd( driverNiceMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], //01:30 in Nice is 00:30 in UTC [thursday('01:30')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( passengerQueryToulonMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('03:00')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); it('should find a passenger that departs shortly after midnight', async () => { await insertAd( passengerToulonMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('03:00')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( driverQueryNiceMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('01:30')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); it('should NOT find a driver that departs the day after', async () => { await insertAd( driverNiceMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('08:30')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( passengerQueryToulonMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('10:00')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(0); }); it('should NOT find a passenger that departs the day before', async () => { await insertAd( passengerToulonMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('10:00')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( driverQueryNiceMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('08:30')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(0); }); it('should find a recurring driver that interesects', async () => { await Promise.all([ insertAd( driverNiceMarseille( Frequency.RECURRENT, ['2023-02-01', '2023-02-28'], [wednesday('08:30')], ), ), insertAd( driverNiceMarseille( Frequency.RECURRENT, ['2023-02-01', '2023-02-18'], [wednesday('08:30')], ), ), insertAd( driverNiceMarseille( Frequency.RECURRENT, ['2023-02-12', '2023-02-28'], [wednesday('08:30')], ), ), insertAd( driverNiceMarseille( Frequency.RECURRENT, ['2023-02-12', '2023-02-18'], [wednesday('08:30')], ), ), ]); const passengerOrientedSelector = new PassengerOrientedSelector( passengerQueryToulonMarseille( Frequency.RECURRENT, ['2023-02-10', '2023-02-20'], [wednesday('10:00')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(4); }); it("should NOT find a recurring driver that doesn't interesect", async () => { await Promise.all([ insertAd( driverNiceMarseille( Frequency.RECURRENT, ['2023-02-01', '2023-02-10'], [wednesday('08:30')], ), ), insertAd( driverNiceMarseille( Frequency.RECURRENT, ['2023-02-20', '2023-02-28'], [wednesday('08:30')], ), ), ]); const passengerOrientedSelector = new PassengerOrientedSelector( passengerQueryToulonMarseille( Frequency.RECURRENT, ['2023-02-12', '2023-02-18'], [wednesday('10:00')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(0); }); it('should find a recurring passenger that interesects', async () => { await Promise.all([ insertAd( passengerToulonMarseille( Frequency.RECURRENT, ['2023-02-01', '2023-02-28'], [wednesday('10:00')], ), ), insertAd( passengerToulonMarseille( Frequency.RECURRENT, ['2023-02-01', '2023-02-18'], [wednesday('10:00')], ), ), insertAd( passengerToulonMarseille( Frequency.RECURRENT, ['2023-02-12', '2023-02-28'], [wednesday('10:00')], ), ), insertAd( passengerToulonMarseille( Frequency.RECURRENT, ['2023-02-12', '2023-02-18'], [wednesday('10:00')], ), ), ]); const passengerOrientedSelector = new PassengerOrientedSelector( driverQueryNiceMarseille( Frequency.RECURRENT, ['2023-02-10', '2023-02-20'], [wednesday('08:30')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(4); }); it("should NOT find a recurring passenger that doesn't interesect", async () => { await Promise.all([ insertAd( passengerToulonMarseille( Frequency.RECURRENT, ['2023-02-01', '2023-02-10'], [wednesday('10:00')], ), ), insertAd( passengerToulonMarseille( Frequency.RECURRENT, ['2023-02-20', '2023-02-28'], [wednesday('10:00')], ), ), ]); const passengerOrientedSelector = new PassengerOrientedSelector( driverQueryNiceMarseille( Frequency.RECURRENT, ['2023-02-12', '2023-02-18'], [wednesday('08:30')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(0); }); it('should find a borderline driver that departs the day before a recurring query', async () => { await insertAd( driverNiceMarseille( Frequency.PUNCTUAL, ['2023-02-01', '2023-02-01'], [wednesday('23:45')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( passengerQueryToulonMarseille( Frequency.RECURRENT, ['2023-02-02', '2023-02-28'], [monday('13:45'), thursday('01:15')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); it('should find a borderline passenger that departs the day after a recurring query', async () => { await insertAd( passengerToulonMarseille( Frequency.PUNCTUAL, ['2023-02-02', '2023-02-02'], [thursday('01:15')], ), ); const passengerOrientedSelector = new PassengerOrientedSelector( driverQueryNiceMarseille( Frequency.RECURRENT, ['2023-01-01', '2023-02-01'], [monday('13:45'), wednesday('23:45')], ), adRepository, ); const candidates = await passengerOrientedSelector.select(); expect(candidates.length).toBe(1); }); }); });