improve tests
This commit is contained in:
parent
075a856d09
commit
efea6fe13c
|
@ -29,7 +29,7 @@ export class RouteCompleter extends Completer {
|
||||||
break;
|
break;
|
||||||
case RouteCompleterType.DETAILED:
|
case RouteCompleterType.DETAILED:
|
||||||
const detailedCandidateRoute =
|
const detailedCandidateRoute =
|
||||||
await this.query.routeProvider.getBasic(
|
await this.query.routeProvider.getDetailed(
|
||||||
(candidate.getProps().carpoolSteps as WayStep[]).map(
|
(candidate.getProps().carpoolSteps as WayStep[]).map(
|
||||||
(wayStep: WayStep) => wayStep.point,
|
(wayStep: WayStep) => wayStep.point,
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { RouteProviderPort } from '../core/application/ports/route-provider.port';
|
import { RouteProviderPort } from '../core/application/ports/route-provider.port';
|
||||||
import { GetRouteControllerPort } from '@modules/geography/core/application/ports/get-route-controller.port';
|
import { GetRouteControllerPort } from '@modules/geography/core/application/ports/get-route-controller.port';
|
||||||
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '../ad.di-tokens';
|
import {
|
||||||
|
AD_GET_BASIC_ROUTE_CONTROLLER,
|
||||||
|
AD_GET_DETAILED_ROUTE_CONTROLLER,
|
||||||
|
} from '../ad.di-tokens';
|
||||||
import { Point, Route } from '@modules/geography/core/domain/route.types';
|
import { Point, Route } from '@modules/geography/core/domain/route.types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -9,6 +12,8 @@ export class RouteProvider implements RouteProviderPort {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(AD_GET_BASIC_ROUTE_CONTROLLER)
|
@Inject(AD_GET_BASIC_ROUTE_CONTROLLER)
|
||||||
private readonly getBasicRouteController: GetRouteControllerPort,
|
private readonly getBasicRouteController: GetRouteControllerPort,
|
||||||
|
@Inject(AD_GET_DETAILED_ROUTE_CONTROLLER)
|
||||||
|
private readonly getDetailedRouteController: GetRouteControllerPort,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
getBasic = async (waypoints: Point[]): Promise<Route> =>
|
getBasic = async (waypoints: Point[]): Promise<Route> =>
|
||||||
|
@ -17,7 +22,7 @@ export class RouteProvider implements RouteProviderPort {
|
||||||
});
|
});
|
||||||
|
|
||||||
getDetailed = async (waypoints: Point[]): Promise<Route> =>
|
getDetailed = async (waypoints: Point[]): Promise<Route> =>
|
||||||
await this.getBasicRouteController.get({
|
await this.getDetailedRouteController.get({
|
||||||
waypoints,
|
waypoints,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port';
|
||||||
|
import { RouteProviderPort } from '@modules/ad/core/application/ports/route-provider.port';
|
||||||
|
import {
|
||||||
|
Algorithm,
|
||||||
|
Selector,
|
||||||
|
} from '@modules/ad/core/application/queries/match/algorithm.abstract';
|
||||||
|
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||||
|
import { Waypoint } from '@modules/ad/core/application/types/waypoint.type';
|
||||||
|
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
||||||
|
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||||
|
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
|
||||||
|
|
||||||
|
const originWaypoint: Waypoint = {
|
||||||
|
position: 0,
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
houseNumber: '5',
|
||||||
|
street: 'Avenue Foch',
|
||||||
|
locality: 'Nancy',
|
||||||
|
postalCode: '54000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
const destinationWaypoint: Waypoint = {
|
||||||
|
position: 1,
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
locality: 'Paris',
|
||||||
|
postalCode: '75000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockRouteProvider: RouteProviderPort = {
|
||||||
|
getBasic: jest.fn(),
|
||||||
|
getDetailed: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchQuery = new MatchQuery(
|
||||||
|
{
|
||||||
|
frequency: Frequency.PUNCTUAL,
|
||||||
|
fromDate: '2023-08-28',
|
||||||
|
toDate: '2023-08-28',
|
||||||
|
schedule: [
|
||||||
|
{
|
||||||
|
time: '01:05',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
waypoints: [originWaypoint, destinationWaypoint],
|
||||||
|
},
|
||||||
|
mockRouteProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockAdRepository: AdRepositoryPort = {
|
||||||
|
insertExtra: jest.fn(),
|
||||||
|
findOneById: jest.fn(),
|
||||||
|
findOne: jest.fn(),
|
||||||
|
insert: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
updateWhere: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
count: jest.fn(),
|
||||||
|
healthCheck: jest.fn(),
|
||||||
|
getCandidateAds: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
class SomeSelector extends Selector {
|
||||||
|
select = async (): Promise<CandidateEntity[]> => [
|
||||||
|
CandidateEntity.create({
|
||||||
|
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
|
||||||
|
role: Role.DRIVER,
|
||||||
|
driverWaypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.678454,
|
||||||
|
lon: 6.189745,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.84877,
|
||||||
|
lon: 2.398457,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passengerWaypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.849445,
|
||||||
|
lon: 6.68651,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 47.18746,
|
||||||
|
lon: 2.89742,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
driverDistance: 350145,
|
||||||
|
driverDuration: 13548,
|
||||||
|
spacetimeDetourRatio: {
|
||||||
|
maxDistanceDetourRatio: 0.3,
|
||||||
|
maxDurationDetourRatio: 0.3,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeAlgorithm extends Algorithm {
|
||||||
|
constructor(
|
||||||
|
protected readonly query: MatchQuery,
|
||||||
|
protected readonly repository: AdRepositoryPort,
|
||||||
|
) {
|
||||||
|
super(query, repository);
|
||||||
|
this.selector = new SomeSelector(query, repository);
|
||||||
|
this.processors = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Abstract Algorithm', () => {
|
||||||
|
it('should return matches', async () => {
|
||||||
|
const someAlgorithm = new SomeAlgorithm(matchQuery, mockAdRepository);
|
||||||
|
const matches: MatchEntity[] = await someAlgorithm.match();
|
||||||
|
expect(matches).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,154 @@
|
||||||
|
import {
|
||||||
|
RouteCompleter,
|
||||||
|
RouteCompleterType,
|
||||||
|
} from '@modules/ad/core/application/queries/match/completer/route.completer';
|
||||||
|
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||||
|
import { AlgorithmType } from '@modules/ad/core/application/types/algorithm.types';
|
||||||
|
import { Waypoint } from '@modules/ad/core/application/types/waypoint.type';
|
||||||
|
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
||||||
|
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||||
|
import { Target } from '@modules/ad/core/domain/candidate.types';
|
||||||
|
import { Actor } from '@modules/ad/core/domain/value-objects/actor.value-object';
|
||||||
|
import { Point } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||||
|
|
||||||
|
const originWaypoint: Waypoint = {
|
||||||
|
position: 0,
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
houseNumber: '5',
|
||||||
|
street: 'Avenue Foch',
|
||||||
|
locality: 'Nancy',
|
||||||
|
postalCode: '54000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
const destinationWaypoint: Waypoint = {
|
||||||
|
position: 1,
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
locality: 'Paris',
|
||||||
|
postalCode: '75000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchQuery = new MatchQuery(
|
||||||
|
{
|
||||||
|
algorithmType: AlgorithmType.PASSENGER_ORIENTED,
|
||||||
|
driver: true,
|
||||||
|
passenger: true,
|
||||||
|
frequency: Frequency.PUNCTUAL,
|
||||||
|
fromDate: '2023-08-28',
|
||||||
|
toDate: '2023-08-28',
|
||||||
|
schedule: [
|
||||||
|
{
|
||||||
|
time: '07:05',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
strict: false,
|
||||||
|
waypoints: [originWaypoint, destinationWaypoint],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
getBasic: jest.fn().mockImplementation(() => ({
|
||||||
|
distance: 350101,
|
||||||
|
duration: 14422,
|
||||||
|
fwdAzimuth: 273,
|
||||||
|
backAzimuth: 93,
|
||||||
|
distanceAzimuth: 336544,
|
||||||
|
points: [],
|
||||||
|
})),
|
||||||
|
getDetailed: jest.fn().mockImplementation(() => ({
|
||||||
|
distance: 350102,
|
||||||
|
duration: 14423,
|
||||||
|
fwdAzimuth: 273,
|
||||||
|
backAzimuth: 93,
|
||||||
|
distanceAzimuth: 336544,
|
||||||
|
points: [],
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const candidate: CandidateEntity = CandidateEntity.create({
|
||||||
|
id: 'cc260669-1c6d-441f-80a5-19cd59afb777',
|
||||||
|
role: Role.DRIVER,
|
||||||
|
driverWaypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.678454,
|
||||||
|
lon: 6.189745,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.84877,
|
||||||
|
lon: 2.398457,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
passengerWaypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
driverDistance: 350145,
|
||||||
|
driverDuration: 13548,
|
||||||
|
spacetimeDetourRatio: {
|
||||||
|
maxDistanceDetourRatio: 0.3,
|
||||||
|
maxDurationDetourRatio: 0.3,
|
||||||
|
},
|
||||||
|
}).setCarpoolPath([
|
||||||
|
{
|
||||||
|
point: new Point({
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
}),
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.START,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
point: new Point({
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
}),
|
||||||
|
actors: [
|
||||||
|
new Actor({
|
||||||
|
role: Role.DRIVER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
new Actor({
|
||||||
|
role: Role.PASSENGER,
|
||||||
|
target: Target.FINISH,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
describe('Route completer', () => {
|
||||||
|
it('should complete candidates with basic setting', async () => {
|
||||||
|
const routeCompleter: RouteCompleter = new RouteCompleter(
|
||||||
|
matchQuery,
|
||||||
|
RouteCompleterType.BASIC,
|
||||||
|
);
|
||||||
|
const completedCandidates: CandidateEntity[] =
|
||||||
|
await routeCompleter.complete([candidate]);
|
||||||
|
expect(completedCandidates.length).toBe(1);
|
||||||
|
expect(completedCandidates[0].getProps().distance).toBe(350101);
|
||||||
|
});
|
||||||
|
it('should complete candidates with detailed setting', async () => {
|
||||||
|
const routeCompleter: RouteCompleter = new RouteCompleter(
|
||||||
|
matchQuery,
|
||||||
|
RouteCompleterType.DETAILED,
|
||||||
|
);
|
||||||
|
const completedCandidates: CandidateEntity[] =
|
||||||
|
await routeCompleter.complete([candidate]);
|
||||||
|
expect(completedCandidates.length).toBe(1);
|
||||||
|
expect(completedCandidates[0].getProps().distance).toBe(350102);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,4 +1,7 @@
|
||||||
import { AD_GET_BASIC_ROUTE_CONTROLLER } from '@modules/ad/ad.di-tokens';
|
import {
|
||||||
|
AD_GET_BASIC_ROUTE_CONTROLLER,
|
||||||
|
AD_GET_DETAILED_ROUTE_CONTROLLER,
|
||||||
|
} from '@modules/ad/ad.di-tokens';
|
||||||
import { Point } from '@modules/ad/core/application/types/point.type';
|
import { Point } from '@modules/ad/core/application/types/point.type';
|
||||||
import { RouteProvider } from '@modules/ad/infrastructure/route-provider';
|
import { RouteProvider } from '@modules/ad/infrastructure/route-provider';
|
||||||
import { GetRouteControllerPort } from '@modules/geography/core/application/ports/get-route-controller.port';
|
import { GetRouteControllerPort } from '@modules/geography/core/application/ports/get-route-controller.port';
|
||||||
|
@ -38,6 +41,30 @@ const mockGetBasicRouteController: GetRouteControllerPort = {
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockGetDetailedRouteController: GetRouteControllerPort = {
|
||||||
|
get: jest.fn().mockImplementationOnce(() => ({
|
||||||
|
distance: 350102,
|
||||||
|
duration: 14423,
|
||||||
|
fwdAzimuth: 273,
|
||||||
|
backAzimuth: 93,
|
||||||
|
distanceAzimuth: 336544,
|
||||||
|
points: [
|
||||||
|
{
|
||||||
|
lon: 6.1765102,
|
||||||
|
lat: 48.689445,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 4.984578,
|
||||||
|
lat: 48.725687,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 2.3522,
|
||||||
|
lat: 48.8566,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
describe('Route provider', () => {
|
describe('Route provider', () => {
|
||||||
let routeProvider: RouteProvider;
|
let routeProvider: RouteProvider;
|
||||||
|
|
||||||
|
@ -49,6 +76,10 @@ describe('Route provider', () => {
|
||||||
provide: AD_GET_BASIC_ROUTE_CONTROLLER,
|
provide: AD_GET_BASIC_ROUTE_CONTROLLER,
|
||||||
useValue: mockGetBasicRouteController,
|
useValue: mockGetBasicRouteController,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: AD_GET_DETAILED_ROUTE_CONTROLLER,
|
||||||
|
useValue: mockGetDetailedRouteController,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
@ -67,4 +98,13 @@ describe('Route provider', () => {
|
||||||
expect(route.distance).toBe(350101);
|
expect(route.distance).toBe(350101);
|
||||||
expect(route.duration).toBe(14422);
|
expect(route.duration).toBe(14422);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should provide a detailed route', async () => {
|
||||||
|
const route: Route = await routeProvider.getDetailed([
|
||||||
|
originPoint,
|
||||||
|
destinationPoint,
|
||||||
|
]);
|
||||||
|
expect(route.distance).toBe(350102);
|
||||||
|
expect(route.duration).toBe(14423);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { GetDetailedRouteController } from '@modules/geography/interface/controllers/get-detailed-route.controller';
|
||||||
|
import { RouteMapper } from '@modules/geography/route.mapper';
|
||||||
|
import { QueryBus } from '@nestjs/cqrs';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
|
const mockQueryBus = {
|
||||||
|
execute: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockRouteMapper = {
|
||||||
|
toPersistence: jest.fn(),
|
||||||
|
toDomain: jest.fn(),
|
||||||
|
toResponse: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Get Detailed Route Controller', () => {
|
||||||
|
let getDetailedRouteController: GetDetailedRouteController;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: QueryBus,
|
||||||
|
useValue: mockQueryBus,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: RouteMapper,
|
||||||
|
useValue: mockRouteMapper,
|
||||||
|
},
|
||||||
|
GetDetailedRouteController,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
getDetailedRouteController = module.get<GetDetailedRouteController>(
|
||||||
|
GetDetailedRouteController,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(getDetailedRouteController).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get a route', async () => {
|
||||||
|
jest.spyOn(mockQueryBus, 'execute');
|
||||||
|
await getDetailedRouteController.get({
|
||||||
|
waypoints: [
|
||||||
|
{
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue