import { RpcExceptionCode } from '@mobicoop/ddd-library'; import { AD_ROUTE_PROVIDER } from '@modules/ad/ad.di-tokens'; import { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port'; import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler'; import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types'; import { Frequency, Role } from '@modules/ad/core/domain/ad.types'; import { Target } from '@modules/ad/core/domain/candidate.types'; import { MatchEntity } from '@modules/ad/core/domain/match.entity'; import { ActorTime } from '@modules/ad/core/domain/value-objects/actor-time.value-object'; import { JourneyItem } from '@modules/ad/core/domain/value-objects/journey-item.value-object'; import { MatchingPaginatedResponseDto } from '@modules/ad/interface/dtos/matching.paginated.response.dto'; import { MatchRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/match.request.dto'; import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto'; import { MatchGrpcController } from '@modules/ad/interface/grpc-controllers/match.grpc-controller'; import { MatchMapper } from '@modules/ad/match.mapper'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { QueryBus } from '@nestjs/cqrs'; import { RpcException } from '@nestjs/microservices'; import { Test, TestingModule } from '@nestjs/testing'; const originWaypoint: WaypointDto = { position: 0, lat: 48.689445, lon: 6.17651, houseNumber: '5', street: 'Avenue Foch', locality: 'Nancy', postalCode: '54000', country: 'France', }; const destinationWaypoint: WaypointDto = { position: 1, lat: 48.8566, lon: 2.3522, locality: 'Paris', postalCode: '75000', country: 'France', }; const recurrentMatchRequestDto: MatchRequestDto = { driver: false, passenger: true, frequency: Frequency.RECURRENT, fromDate: '2023-08-15', toDate: '2024-09-30', schedule: [ { time: '07:00', day: 5, margin: 900, }, ], waypoints: [originWaypoint, destinationWaypoint], strict: false, algorithmType: AlgorithmType.PASSENGER_ORIENTED, }; const mockQueryBus = { execute: jest .fn() .mockImplementationOnce( () => { id: '43c83ae2-f4b0-4ac6-b8bf-8071801924d4', page: 1, perPage: 10, matches: [ MatchEntity.create({ adId: '53a0bf71-4132-4f7b-a4cc-88c796b6bdf1', role: Role.DRIVER, frequency: Frequency.RECURRENT, distance: 356041, duration: 12647, initialDistance: 349251, initialDuration: 12103, journeys: [ { firstDate: new Date('2023-09-01'), lastDate: new Date('2024-08-30'), journeyItems: [ new JourneyItem({ lat: 48.689445, lon: 6.17651, duration: 0, distance: 0, actorTimes: [ new ActorTime({ role: Role.DRIVER, target: Target.START, firstDatetime: new Date('2023-09-01 07:00'), firstMinDatetime: new Date('2023-09-01 06:45'), firstMaxDatetime: new Date('2023-09-01 07:15'), lastDatetime: new Date('2024-08-30 07:00'), lastMinDatetime: new Date('2024-08-30 06:45'), lastMaxDatetime: new Date('2024-08-30 07:15'), }), ], }), new JourneyItem({ lat: 48.369445, lon: 6.67487, duration: 2100, distance: 56878, actorTimes: [ new ActorTime({ role: Role.DRIVER, target: Target.NEUTRAL, firstDatetime: new Date('2023-09-01 07:35'), firstMinDatetime: new Date('2023-09-01 07:20'), firstMaxDatetime: new Date('2023-09-01 07:50'), lastDatetime: new Date('2024-08-30 07:35'), lastMinDatetime: new Date('2024-08-30 07:20'), lastMaxDatetime: new Date('2024-08-30 07:50'), }), new ActorTime({ role: Role.PASSENGER, target: Target.START, firstDatetime: new Date('2023-09-01 07:32'), firstMinDatetime: new Date('2023-09-01 07:17'), firstMaxDatetime: new Date('2023-09-01 07:47'), lastDatetime: new Date('2024-08-30 07:32'), lastMinDatetime: new Date('2024-08-30 07:17'), lastMaxDatetime: new Date('2024-08-30 07:47'), }), ], }), new JourneyItem({ lat: 47.98487, lon: 6.9427, duration: 3840, distance: 76491, actorTimes: [ new ActorTime({ role: Role.DRIVER, target: Target.NEUTRAL, firstDatetime: new Date('2023-09-01 08:04'), firstMinDatetime: new Date('2023-09-01 07:51'), firstMaxDatetime: new Date('2023-09-01 08:19'), lastDatetime: new Date('2024-08-30 08:04'), lastMinDatetime: new Date('2024-08-30 07:51'), lastMaxDatetime: new Date('2024-08-30 08:19'), }), new ActorTime({ role: Role.PASSENGER, target: Target.FINISH, firstDatetime: new Date('2023-09-01 08:01'), firstMinDatetime: new Date('2023-09-01 07:46'), firstMaxDatetime: new Date('2023-09-01 08:16'), lastDatetime: new Date('2024-08-30 08:01'), lastMinDatetime: new Date('2024-08-30 07:46'), lastMaxDatetime: new Date('2024-08-30 08:16'), }), ], }), new JourneyItem({ lat: 47.365987, lon: 7.02154, duration: 4980, distance: 96475, actorTimes: [ new ActorTime({ role: Role.DRIVER, target: Target.FINISH, firstDatetime: new Date('2023-09-01 08:23'), firstMinDatetime: new Date('2023-09-01 08:08'), firstMaxDatetime: new Date('2023-09-01 08:38'), lastDatetime: new Date('2024-08-30 08:23'), lastMinDatetime: new Date('2024-08-30 08:08'), lastMaxDatetime: new Date('2024-08-30 08:38'), }), ], }), ], }, ], }), ], total: 1, }, ) .mockImplementationOnce(() => { throw new Error(); }), }; const mockRouteProvider: RouteProviderPort = { getBasic: jest.fn(), getDetailed: jest.fn(), }; const mockMatchMapper = { toResponse: jest.fn().mockImplementation(() => ({ adId: '53a0bf71-4132-4f7b-a4cc-88c796b6bdf1', role: 'DRIVER', frequency: 'RECURRENT', distance: 356041, duration: 12647, initialDistance: 349251, initialDuration: 12103, distanceDetour: 6790, durationDetour: 544, distanceDetourPercentage: 4.1, durationDetourPercentage: 3.8, journeys: [ { day: 5, firstDate: '2023-09-01', lastDate: '2024-08-30', journeyItems: [ { lat: 48.689445, lon: 6.17651, duration: 0, distance: 0, time: '07:00', actorTimes: [ { role: 'DRIVER', target: 'START', }, ], }, { lat: 48.369445, lon: 6.67487, duration: 2100, distance: 56878, time: '07:35', actorTimes: [ { role: 'DRIVER', target: 'NEUTRAL', }, { role: 'PASSENGER', target: 'START', }, ], }, { lat: 47.98487, lon: 6.9427, duration: 3840, distance: 76491, time: '08:04', actorTimes: [ { role: 'DRIVER', target: 'NEUTRAL', }, { role: 'PASSENGER', target: 'FINISH', }, ], }, { lat: 47.365987, lon: 7.02154, duration: 4980, distance: 96475, time: '08:23', actorTimes: [ { role: 'DRIVER', target: 'FINISH', }, ], }, ], }, ], })), }; describe('Match Grpc Controller', () => { let matchGrpcController: MatchGrpcController; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ MatchGrpcController, { provide: QueryBus, useValue: mockQueryBus, }, { provide: AD_ROUTE_PROVIDER, useValue: mockRouteProvider, }, { provide: MatchMapper, useValue: mockMatchMapper, }, { provide: CACHE_MANAGER, useValue: {}, }, ], }).compile(); matchGrpcController = module.get(MatchGrpcController); }); afterEach(async () => { jest.clearAllMocks(); }); it('should be defined', () => { expect(matchGrpcController).toBeDefined(); }); it('should return a matching', async () => { jest.spyOn(mockQueryBus, 'execute'); const matchingPaginatedResponseDto: MatchingPaginatedResponseDto = await matchGrpcController.match(recurrentMatchRequestDto); expect(matchingPaginatedResponseDto.id).toBe( '43c83ae2-f4b0-4ac6-b8bf-8071801924d4', ); expect(matchingPaginatedResponseDto.data).toHaveLength(1); expect(matchingPaginatedResponseDto.page).toBe(1); expect(matchingPaginatedResponseDto.perPage).toBe(10); expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); }); it('should throw a generic RpcException', async () => { jest.spyOn(mockQueryBus, 'execute'); expect.assertions(3); try { await matchGrpcController.match(recurrentMatchRequestDto); } catch (e: any) { expect(e).toBeInstanceOf(RpcException); expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); } expect(mockQueryBus.execute).toHaveBeenCalledTimes(1); }); });