12 Commits

Author SHA1 Message Date
Romain Thouvenin
104559d03d Add a test for punctual search matching a recurrent driver schedule 2024-04-05 11:44:10 +02:00
Romain Thouvenin
173e5ebba5 Rename schedule fixtures for better readability of tests 2024-04-05 11:43:30 +02:00
Romain Thouvenin
0dc01da2b0 Combine journey creation and filtering to avoid missing results 2024-04-04 17:24:23 +02:00
Sylvain Briat
945ce80840 improve documentation 2024-04-03 13:39:09 +02:00
Sylvain Briat
16ebe8d543 handle excluded ad in query and selector 2024-04-03 13:02:31 +02:00
Sylvain Briat
0c29e522ed add excludedAdId to proto and dto 2024-04-03 13:01:12 +02:00
Romain Thouvenin
c51c368d83 Remove the controller-level cache from MatchGrpcController 2024-04-03 07:52:37 +00:00
Sylvain Briat
0446d267ef improve creation of schedules 2024-04-02 11:14:05 +00:00
Sylvain Briat
100fb3487d add possibility to have more than one driver target in carpool path item (eg. when a user adds 2 same waypoints with a different target) 2024-04-02 11:14:05 +00:00
Romain Thouvenin
e501bef249 Move test job to a common template 2024-04-02 12:23:03 +02:00
Sylvain Briat
71ac97410a return frequency in match response 2024-04-02 10:06:34 +00:00
Romain Thouvenin
5696ac57bd Generic test job that also runs on release merge requests 2024-04-02 12:01:38 +02:00
13 changed files with 203 additions and 194 deletions

View File

@@ -8,25 +8,6 @@ include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
- project: mobicoop/v3/gitlab-templates
file: /ci/release.build-job.yml
##############
# TEST STAGE #
##############
test:
stage: test
image: docker/compose:latest
variables:
DOCKER_TLS_CERTDIR: ''
services:
- docker:dind
script:
- docker-compose -f docker-compose.ci.tools.yml -p matcher-tools --env-file ci/.env.ci up -d
- sh ci/wait-up.sh
- docker-compose -f docker-compose.ci.service.yml -p matcher-service --env-file ci/.env.ci up -d
- docker exec -t v3-matcher-api sh -c "npm run test:integration:ci"
coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_MESSAGE =~ /--check/ || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: always
file:
- /ci/release.build-job.yml
- /ci/service.test-job.yml

View File

@@ -167,6 +167,7 @@ The app exposes the following [gRPC](https://grpc.io/) services :
- **seatsProposed** (integer, optional): number of seats proposed as driver (_default : 3_)
- **seatsRequested** (integer, optional): number of seats requested as passenger (_default : 1_)
- **waypoints**: an array of addresses that represent the waypoints of the journey (only first and last waypoints are used for passenger ads). Note that positions are **required** and **must** be consecutives
- **excludedAdId** (optional): the id of an ad to be excluded from the results (useful to avoid self-matchings)
- **algorithmType** (optional): the type of algorithm to use (as of 2023-09-28, only the default `PASSENGER_ORIENTED` is accepted)
- **remoteness** (integer, optional): an integer to indicate the maximum flying distance (in metres) between the driver route and the passenger pick-up / drop-off points (_default : 15000_)
- **useProportion** (boolean, optional): a boolean to indicate if the matching algorithm will compare the distance of the passenger route against the distance of the driver route (_default : 1_). Works in combination with **proportion** parameter
@@ -218,10 +219,6 @@ If the matching is successful, you will get a result, containing :
Matching is a time-consuming process, so the results of a matching request are stored in cache before being paginated and returned to the requester.
An id is attributed to the overall results of a request : on further requests (for example to query for different pages of results), the requester can provide this id and get in return the cached data, avoiding another longer process of computing the results from scratch. Obviously, new computing must be done periodically to get fresh new results !
There's also a basic cache to store the results of the _same_ request sent multiple times successively.
Cache TTLs are customizable in the `.env` file.
## Tests / ESLint / Prettier
Tests are run outside the container for ease of use (switching between different environments inside containers using prisma is complicated and error prone).

View File

@@ -26,6 +26,7 @@ export class MatchQuery extends QueryBase {
seatsRequested?: number;
strict?: boolean;
readonly waypoints: Waypoint[];
excludedAdId?: string;
algorithmType?: AlgorithmType;
remoteness?: number;
useProportion?: boolean;
@@ -56,6 +57,7 @@ export class MatchQuery extends QueryBase {
this.seatsRequested = props.seatsRequested;
this.strict = props.strict;
this.waypoints = props.waypoints;
this.excludedAdId = props.excludedAdId;
this.algorithmType = props.algorithmType;
this.remoteness = props.remoteness;
this.useProportion = props.useProportion;

View File

@@ -136,6 +136,7 @@ export class PassengerOrientedSelector extends Selector {
this._whereStrict(),
this._whereDate(),
this._whereSchedule(role),
this._whereExcludedAd(),
this._whereAzimuth(),
this._whereProportion(role),
this._whereRemoteness(role),
@@ -206,6 +207,9 @@ export class PassengerOrientedSelector extends Selector {
return '';
};
private _whereExcludedAd = (): string =>
this.query.excludedAdId ? `ad.uuid <> '${this.query.excludedAdId}'` : '';
private _wherePassengerSchedule = (
date: Date,
scheduleItem: ScheduleItem,

View File

@@ -1,28 +1,28 @@
import {
AggregateRoot,
AggregateID,
AggregateRoot,
ArgumentInvalidException,
} from '@mobicoop/ddd-library';
import { Role } from './ad.types';
import { CalendarTools } from './calendar-tools.service';
import {
CandidateProps,
CreateCandidateProps,
Target,
} from './candidate.types';
import { ActorTime } from './value-objects/actor-time.value-object';
import { Actor } from './value-objects/actor.value-object';
import {
CarpoolPathItem,
CarpoolPathItemProps,
} from './value-objects/carpool-path-item.value-object';
import { Step, StepProps } from './value-objects/step.value-object';
import { JourneyItem } from './value-objects/journey-item.value-object';
import { Journey, JourneyProps } from './value-objects/journey.value-object';
import {
ScheduleItem,
ScheduleItemProps,
} from './value-objects/schedule-item.value-object';
import { Journey } from './value-objects/journey.value-object';
import { CalendarTools } from './calendar-tools.service';
import { JourneyItem } from './value-objects/journey-item.value-object';
import { Actor } from './value-objects/actor.value-object';
import { ActorTime } from './value-objects/actor-time.value-object';
import { Role } from './ad.types';
import { Step, StepProps } from './value-objects/step.value-object';
export class CandidateEntity extends AggregateRoot<CandidateProps> {
protected readonly _id: AggregateID;
@@ -63,18 +63,23 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
// driver and passenger schedules are eventually mandatory
if (!this.props.driverSchedule) this._createDriverSchedule();
if (!this.props.passengerSchedule) this._createPassengerSchedule();
try {
this.props.journeys = (this.props.driverSchedule as ScheduleItemProps[])
// first we create the journeys
.map((driverScheduleItem: ScheduleItem) =>
this._createJourney(driverScheduleItem),
)
// then we filter the ones with invalid pickups
.filter((journey: Journey) => journey.hasValidPickUp());
} catch (e) {
// irrelevant journeys fall here
// eg. no available day for the given date range
}
this.props.journeys = this.props.driverSchedule!.reduce(
(accJourneys: JourneyProps[], driverScheduleItem: ScheduleItem) => {
try {
// first we create the journeys
const journey = this._createJourney(driverScheduleItem);
// then we filter the ones with invalid pickups
if (journey.hasValidPickUp()) {
accJourneys.push(journey);
}
} catch (e) {
// irrelevant journeys fall here
// eg. no available day for the given date range
}
return accJourneys;
},
new Array<JourneyProps>(),
);
return this;
};
@@ -96,40 +101,50 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
* Create the driver schedule based on the passenger schedule
*/
private _createDriverSchedule = (): void => {
let driverSchedule: ScheduleItemProps[] = this.props.passengerSchedule!.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
);
let driverSchedule: Array<ScheduleItemProps | undefined> =
this.props.passengerSchedule!.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
);
// adjust the driver theoretical schedule :
// we guess the ideal driver departure time based on the duration to
// reach the passenger starting point from the driver starting point
driverSchedule = driverSchedule.map(
(scheduleItemProps: ScheduleItemProps) => {
const driverDate: Date = CalendarTools.firstDate(
scheduleItemProps.day,
this.props.dateInterval,
);
const driverStartDatetime: Date = CalendarTools.datetimeWithSeconds(
driverDate,
scheduleItemProps.time,
-this._passengerStartDuration(),
);
return {
day: driverDate.getUTCDay(),
margin: scheduleItemProps.margin,
time: `${driverStartDatetime
.getUTCHours()
.toString()
.padStart(2, '0')}:${driverStartDatetime
.getUTCMinutes()
.toString()
.padStart(2, '0')}`,
};
},
);
driverSchedule = driverSchedule
.map((scheduleItemProps: ScheduleItemProps) => {
try {
const driverDate: Date = CalendarTools.firstDate(
scheduleItemProps.day,
this.props.dateInterval,
);
const driverStartDatetime: Date = CalendarTools.datetimeWithSeconds(
driverDate,
scheduleItemProps.time,
-this._passengerStartDuration(),
);
return <ScheduleItemProps>{
day: driverDate.getUTCDay(),
margin: scheduleItemProps.margin,
time: `${driverStartDatetime
.getUTCHours()
.toString()
.padStart(2, '0')}:${driverStartDatetime
.getUTCMinutes()
.toString()
.padStart(2, '0')}`,
};
} catch (e) {
// no possible driver date or time
// TODO : find a test case !
return undefined;
}
})
.filter(
(scheduleItemProps: ScheduleItemProps | undefined) =>
scheduleItemProps !== undefined,
);
this.props.driverSchedule = driverSchedule.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
@@ -159,40 +174,50 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
* Create the passenger schedule based on the driver schedule
*/
private _createPassengerSchedule = (): void => {
let passengerSchedule: ScheduleItemProps[] = this.props.driverSchedule!.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
);
let passengerSchedule: Array<ScheduleItemProps | undefined> =
this.props.driverSchedule!.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,
time: scheduleItemProps.time,
margin: scheduleItemProps.margin,
}),
);
// adjust the passenger theoretical schedule :
// we guess the ideal passenger departure time based on the duration to
// reach the passenger starting point from the driver starting point
passengerSchedule = passengerSchedule.map(
(scheduleItemProps: ScheduleItemProps) => {
const passengerDate: Date = CalendarTools.firstDate(
scheduleItemProps.day,
this.props.dateInterval,
);
const passengeStartDatetime: Date = CalendarTools.datetimeWithSeconds(
passengerDate,
scheduleItemProps.time,
this._passengerStartDuration(),
);
return {
day: passengerDate.getUTCDay(),
margin: scheduleItemProps.margin,
time: `${passengeStartDatetime
.getUTCHours()
.toString()
.padStart(2, '0')}:${passengeStartDatetime
.getUTCMinutes()
.toString()
.padStart(2, '0')}`,
};
},
);
passengerSchedule = passengerSchedule
.map((scheduleItemProps: ScheduleItemProps) => {
try {
const passengerDate: Date = CalendarTools.firstDate(
scheduleItemProps.day,
this.props.dateInterval,
);
const passengeStartDatetime: Date = CalendarTools.datetimeWithSeconds(
passengerDate,
scheduleItemProps.time,
this._passengerStartDuration(),
);
return {
day: passengerDate.getUTCDay(),
margin: scheduleItemProps.margin,
time: `${passengeStartDatetime
.getUTCHours()
.toString()
.padStart(2, '0')}:${passengeStartDatetime
.getUTCMinutes()
.toString()
.padStart(2, '0')}`,
};
} catch (e) {
// no possible passenger date or time
// TODO : find a test case !
return undefined;
}
})
.filter(
(scheduleItemProps: ScheduleItemProps | undefined) =>
scheduleItemProps !== undefined,
);
this.props.passengerSchedule = passengerSchedule.map(
(scheduleItemProps: ScheduleItemProps) => ({
day: scheduleItemProps.day,

View File

@@ -2,8 +2,7 @@ import {
ArgumentOutOfRangeException,
ValueObject,
} from '@mobicoop/ddd-library';
import { Actor, ActorProps } from './actor.value-object';
import { Role } from '../ad.types';
import { ActorProps } from './actor.value-object';
import { Point, PointProps } from './point.value-object';
/** Note:
@@ -36,12 +35,5 @@ export class CarpoolPathItem extends ValueObject<CarpoolPathItemProps> {
});
if (props.actors.length <= 0)
throw new ArgumentOutOfRangeException('at least one actor is required');
if (
props.actors.filter((actor: Actor) => actor.role == Role.DRIVER).length >
1
)
throw new ArgumentOutOfRangeException(
'a carpoolStep can contain only one driver',
);
}
}

View File

@@ -1,10 +1,11 @@
import { ResponseBase } from '@mobicoop/ddd-library';
import { JourneyResponseDto } from './journey.response.dto';
import { Frequency } from '@modules/ad/core/domain/ad.types';
export class MatchResponseDto extends ResponseBase {
adId: string;
role: string;
frequency: string;
frequency: Frequency;
distance: number;
duration: number;
initialDistance: number;

View File

@@ -81,6 +81,10 @@ export class MatchRequestDto {
@ValidateNested({ each: true })
waypoints: WaypointDto[];
@IsUUID()
@IsOptional()
excludedAdId?: string;
@IsOptional()
@IsEnum(AlgorithmType)
algorithmType?: AlgorithmType;

View File

@@ -5,14 +5,7 @@ import { MatchQuery } from '@modules/ad/core/application/queries/match/match.que
import { MatchingResult } from '@modules/ad/core/application/queries/match/match.query-handler';
import { MatchEntity } from '@modules/ad/core/domain/match.entity';
import { MatchMapper } from '@modules/ad/match.mapper';
import { CacheInterceptor, CacheKey } from '@nestjs/cache-manager';
import {
Controller,
Inject,
UseFilters,
UseInterceptors,
UsePipes,
} from '@nestjs/common';
import { Controller, Inject, UseFilters, UsePipes } from '@nestjs/common';
import { QueryBus } from '@nestjs/cqrs';
import { GrpcMethod } from '@nestjs/microservices';
import { LogCauseExceptionFilter } from '@src/log-cause.exception-filter';
@@ -35,8 +28,6 @@ export class MatchGrpcController {
private readonly matchMapper: MatchMapper,
) {}
@CacheKey('MatcherServiceMatch')
@UseInterceptors(CacheInterceptor)
@GrpcMethod('MatcherService', 'Match')
async match(data: MatchRequestDto): Promise<MatchingPaginatedResponseDto> {
const matchingResult: MatchingResult = await this.queryBus.execute(

View File

@@ -16,16 +16,17 @@ message MatchRequest {
repeated ScheduleItem schedule = 7;
bool strict = 8;
repeated Waypoint waypoints = 9;
AlgorithmType algorithmType = 10;
int32 remoteness = 11;
bool useProportion = 12;
float proportion = 13;
bool useAzimuth = 14;
int32 azimuthMargin = 15;
float maxDetourDistanceRatio = 16;
float maxDetourDurationRatio = 17;
optional int32 page = 18;
optional int32 perPage = 19;
string excludedAdId = 10;
AlgorithmType algorithmType = 11;
int32 remoteness = 12;
bool useProportion = 13;
float proportion = 14;
bool useAzimuth = 15;
int32 azimuthMargin = 16;
float maxDetourDistanceRatio = 17;
float maxDetourDurationRatio = 18;
optional int32 page = 19;
optional int32 perPage = 20;
}
message ScheduleItem {
@@ -58,15 +59,16 @@ enum AlgorithmType {
message Match {
string adId = 1;
string role = 2;
int32 distance = 3;
int32 duration = 4;
int32 initialDistance = 5;
int32 initialDuration = 6;
int32 distanceDetour = 7;
int32 durationDetour = 8;
double distanceDetourPercentage = 9;
double durationDetourPercentage = 10;
repeated Journey journeys = 11;
Frequency frequency = 3;
int32 distance = 4;
int32 duration = 5;
int32 initialDistance = 6;
int32 initialDuration = 7;
int32 distanceDetour = 8;
int32 durationDetour = 9;
double distanceDetourPercentage = 10;
double durationDetourPercentage = 11;
repeated Journey journeys = 12;
}
message Journey {

View File

@@ -37,7 +37,7 @@ const waypointsSet2: PointProps[] = [
},
];
const schedule1: ScheduleItemProps[] = [
const mondayAt7: ScheduleItemProps[] = [
{
day: 1,
time: '07:00',
@@ -45,7 +45,7 @@ const schedule1: ScheduleItemProps[] = [
},
];
const schedule2: ScheduleItemProps[] = [
const mondayAt710: ScheduleItemProps[] = [
{
day: 1,
time: '07:10',
@@ -53,7 +53,7 @@ const schedule2: ScheduleItemProps[] = [
},
];
const schedule3: ScheduleItemProps[] = [
const weekdayMornings: ScheduleItemProps[] = [
{
day: 1,
time: '06:30',
@@ -81,7 +81,7 @@ const schedule3: ScheduleItemProps[] = [
},
];
const schedule4: ScheduleItemProps[] = [
const schooldayMornings: ScheduleItemProps[] = [
{
day: 1,
time: '06:50',
@@ -104,7 +104,7 @@ const schedule4: ScheduleItemProps[] = [
},
];
const schedule5: ScheduleItemProps[] = [
const saturdayNightAndMondayMorning: ScheduleItemProps[] = [
{
day: 0,
time: '00:02',
@@ -117,7 +117,7 @@ const schedule5: ScheduleItemProps[] = [
},
];
const schedule6: ScheduleItemProps[] = [
const mondayAndSaturdayNights: ScheduleItemProps[] = [
{
day: 1,
time: '23:10',
@@ -130,7 +130,7 @@ const schedule6: ScheduleItemProps[] = [
},
];
const schedule7: ScheduleItemProps[] = [
const thursdayEvening: ScheduleItemProps[] = [
{
day: 4,
time: '19:00',
@@ -266,8 +266,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule2,
driverSchedule: mondayAt7,
passengerSchedule: mondayAt710,
spacetimeDetourRatio,
});
expect(candidateEntity.id.length).toBe(36);
@@ -286,8 +286,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule2,
driverSchedule: mondayAt7,
passengerSchedule: mondayAt710,
spacetimeDetourRatio,
}).setCarpoolPath(carpoolPath1);
expect(candidateEntity.getProps().carpoolPath).toHaveLength(2);
@@ -306,8 +306,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule2,
driverSchedule: mondayAt7,
passengerSchedule: mondayAt710,
spacetimeDetourRatio,
}).setMetrics(352688, 14587);
expect(candidateEntity.getProps().distance).toBe(352688);
@@ -328,8 +328,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule2,
driverSchedule: mondayAt7,
passengerSchedule: mondayAt710,
spacetimeDetourRatio,
}).setMetrics(458690, 13980);
expect(candidateEntity.isDetourValid()).toBeFalsy();
@@ -347,8 +347,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule2,
driverSchedule: mondayAt7,
passengerSchedule: mondayAt710,
spacetimeDetourRatio,
}).setMetrics(352368, 18314);
expect(candidateEntity.isDetourValid()).toBeFalsy();
@@ -369,8 +369,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule2,
driverSchedule: mondayAt7,
passengerSchedule: mondayAt710,
spacetimeDetourRatio,
})
.setCarpoolPath(carpoolPath2)
@@ -392,7 +392,7 @@ describe('Candidate entity', () => {
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: undefined,
passengerSchedule: schedule2,
passengerSchedule: mondayAt710,
spacetimeDetourRatio,
})
.setCarpoolPath(carpoolPath2)
@@ -424,7 +424,7 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
driverSchedule: mondayAt7,
passengerSchedule: undefined,
spacetimeDetourRatio,
})
@@ -480,8 +480,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule3,
passengerSchedule: schedule4,
driverSchedule: weekdayMornings,
passengerSchedule: schooldayMornings,
spacetimeDetourRatio,
})
.setCarpoolPath(carpoolPath2)
@@ -517,8 +517,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule5,
passengerSchedule: schedule6,
driverSchedule: saturdayNightAndMondayMorning,
passengerSchedule: mondayAndSaturdayNights,
spacetimeDetourRatio,
})
.setCarpoolPath(carpoolPath2)
@@ -550,6 +550,33 @@ describe('Candidate entity', () => {
)[0].journeyItems[1].actorTimes[1].firstMinDatetime.getUTCMinutes(),
).toBe(42);
});
it('should create a journey for a punctual search from a recurrent driver schedule', () => {
const candidateEntity: CandidateEntity = CandidateEntity.create({
id: '5600ccfb-ab69-4d03-aa30-0fbe84fcedc0',
role: Role.PASSENGER,
frequency: Frequency.PUNCTUAL,
dateInterval: {
lowerDate: '2024-04-01', //This is a Monday
higherDate: '2024-04-01',
},
driverWaypoints: waypointsSet1,
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: weekdayMornings,
passengerSchedule: mondayAt7,
spacetimeDetourRatio,
})
.setCarpoolPath(carpoolPath2)
.setSteps(steps)
.createJourneys();
expect(candidateEntity.getProps().journeys).toHaveLength(1);
expect(
(
candidateEntity.getProps().journeys as Journey[]
)[0].firstDate.getDate(),
).toBe(1);
});
it('should not create journeys if dates does not match', () => {
const candidateEntity: CandidateEntity = CandidateEntity.create({
@@ -564,8 +591,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule7,
driverSchedule: mondayAt7,
passengerSchedule: thursdayEvening,
spacetimeDetourRatio,
})
.setCarpoolPath(carpoolPath2)
@@ -588,8 +615,8 @@ describe('Candidate entity', () => {
passengerWaypoints: waypointsSet2,
driverDistance: 350145,
driverDuration: 13548,
driverSchedule: schedule1,
passengerSchedule: schedule7,
driverSchedule: mondayAt7,
passengerSchedule: thursdayEvening,
spacetimeDetourRatio,
})
.setCarpoolPath(carpoolPath2)

View File

@@ -33,22 +33,4 @@ describe('Carpool Path Item value object', () => {
});
}).toThrow(ArgumentOutOfRangeException);
});
it('should throw an exception if actors contains more than one driver', () => {
expect(() => {
new CarpoolPathItem({
lat: 48.689445,
lon: 6.17651,
actors: [
new Actor({
role: Role.DRIVER,
target: Target.NEUTRAL,
}),
new Actor({
role: Role.DRIVER,
target: Target.START,
}),
],
});
}).toThrow(ArgumentOutOfRangeException);
});
});

View File

@@ -47,6 +47,7 @@ const matchQuery = new MatchQuery(
],
strict: false,
waypoints: [originWaypoint, destinationWaypoint],
excludedAdId: '758618c6-dd82-4199-a548-0205161b04d7',
},
bareMockGeorouter,
);