Merge branch 'fix-7812' into 'release-1.8'
Fix for Redmine#7812 See merge request mobicoop/v3/service/matcher!44
This commit is contained in:
commit
60df1b978a
|
@ -1,11 +1,21 @@
|
|||
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
||||
import { Selector } from '../algorithm.abstract';
|
||||
import { Waypoint } from '../../../types/waypoint.type';
|
||||
import { Point } from '../../../types/point.type';
|
||||
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { ScheduleItem } from '@modules/ad/core/domain/value-objects/schedule-item.value-object';
|
||||
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
|
||||
import { CandidateEntity } from '@modules/ad/core/domain/candidate.entity';
|
||||
import { DateInterval } from '../../../../domain/candidate.types';
|
||||
import { Point } from '../../../types/point.type';
|
||||
import { Waypoint } from '../../../types/waypoint.type';
|
||||
import { Selector } from '../algorithm.abstract';
|
||||
import { ScheduleItem } from '../match.query';
|
||||
|
||||
/**
|
||||
* This class complements the AdRepository prisma service by turning a match query object into a SQL query,
|
||||
* with the assumption that the query is passenger-oriented (i.e. it is up to the driver to go out of his way to pick up the passenger).
|
||||
* The idea is to make a rough filter of the ads in DB to limit the number of ads to be processed more precisely by the application code.
|
||||
* TODO: Converting the query object into a SQL query is a job for the repository implementation
|
||||
* (or anything behind the repository interface),
|
||||
* any logic related to being passenger-oriented should be in the domain layer.
|
||||
* (though it might be difficult to describe generically the search criteria with a query object)
|
||||
*/
|
||||
export class PassengerOrientedSelector extends Selector {
|
||||
select = async (): Promise<CandidateEntity[]> => {
|
||||
const queryStringRoles: QueryStringRole[] = [];
|
||||
|
@ -19,6 +29,7 @@ export class PassengerOrientedSelector extends Selector {
|
|||
query: this._createQueryString(Role.PASSENGER),
|
||||
role: Role.PASSENGER,
|
||||
});
|
||||
|
||||
return (
|
||||
await Promise.all(
|
||||
queryStringRoles.map<Promise<AdsRole>>(
|
||||
|
@ -36,7 +47,7 @@ export class PassengerOrientedSelector extends Selector {
|
|||
id: adEntity.id,
|
||||
role: adsRole.role == Role.DRIVER ? Role.PASSENGER : Role.DRIVER,
|
||||
frequency: adEntity.getProps().frequency,
|
||||
dateInterval: {
|
||||
dateInterval: this._fixDateInterval({
|
||||
lowerDate: this._maxDateString(
|
||||
this.query.fromDate,
|
||||
adEntity.getProps().fromDate,
|
||||
|
@ -45,7 +56,7 @@ export class PassengerOrientedSelector extends Selector {
|
|||
this.query.toDate,
|
||||
adEntity.getProps().toDate,
|
||||
),
|
||||
},
|
||||
}),
|
||||
driverWaypoints:
|
||||
adsRole.role == Role.PASSENGER
|
||||
? adEntity.getProps().waypoints
|
||||
|
@ -134,8 +145,7 @@ export class PassengerOrientedSelector extends Selector {
|
|||
[
|
||||
this._whereRole(role),
|
||||
this._whereStrict(),
|
||||
this._whereDate(),
|
||||
this._whereSchedule(role),
|
||||
this._whereDate(role),
|
||||
this._whereExcludedAd(),
|
||||
this._whereAzimuth(),
|
||||
this._whereProportion(role),
|
||||
|
@ -154,110 +164,58 @@ export class PassengerOrientedSelector extends Selector {
|
|||
: `frequency='${Frequency.RECURRENT}'`
|
||||
: '';
|
||||
|
||||
private _whereDate = (): string =>
|
||||
this.query.frequency == Frequency.PUNCTUAL
|
||||
? `("fromDate" <= '${this.query.fromDate}' AND "toDate" >= '${this.query.fromDate}')`
|
||||
: `(\
|
||||
(\
|
||||
"fromDate" <= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" >= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
) OR (\
|
||||
"fromDate" >= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" <= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
) OR (\
|
||||
"fromDate" <= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" <= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
) OR (\
|
||||
"fromDate" >= '${this.query.fromDate}' AND "fromDate" <= '${this.query.toDate}' AND\
|
||||
"toDate" >= '${this.query.toDate}' AND "toDate" >= '${this.query.fromDate}'\
|
||||
)\
|
||||
)`;
|
||||
/**
|
||||
* Generates the WHERE clause checking that the date range of the query intersects with the range of the ad.
|
||||
* Note that driver dates might not be comparable with passenger dates when the trip is by night or very long.
|
||||
* For this reason, the pickup date is adjusted with the driver duration,
|
||||
* so as to compare with the maximum / minimum driver date that could make sense for the passenger.
|
||||
* This may return more ads than necessary, but they will be filtered out in further processing.
|
||||
*/
|
||||
private _whereDate = (role: Role): string => {
|
||||
const maxFromDate = this._maxFromDate(role);
|
||||
const minToDate = this._minToDate(role);
|
||||
return `("fromDate" <= ${maxFromDate} AND "toDate" >= ${minToDate})`;
|
||||
};
|
||||
|
||||
private _whereSchedule = (role: Role): string => {
|
||||
// no schedule filtering if schedule is not set
|
||||
if (this.query.schedule === undefined) return '';
|
||||
const schedule: string[] = [];
|
||||
// we need full dates to compare times, because margins can lead to compare on previous or next day
|
||||
// - first we establish a base calendar (up to a week)
|
||||
const scheduleDates: Date[] = this._datesBetweenBoundaries(
|
||||
this.query.fromDate,
|
||||
this.query.toDate,
|
||||
private _maxFromDate = (role: Role): string => {
|
||||
if (role == Role.DRIVER) {
|
||||
//When looking for a passenger, we add the duration of the driver route to the latest toDate
|
||||
//to compute the maximum sensible passenger fromDate, in case the pickup date could be on the next day
|
||||
const querySchedule = this.query.schedule;
|
||||
// When there is no schedule (search whole day), we consider the driver accepts to depart until 23:59
|
||||
const maxScheduleTime =
|
||||
querySchedule === undefined
|
||||
? '23:59'
|
||||
: querySchedule.reduce(
|
||||
(max, s) => (s.time > max ? s.time : max),
|
||||
'00:00',
|
||||
);
|
||||
// - then we compare each resulting day of the schedule with each day of calendar,
|
||||
// adding / removing margin depending on the role
|
||||
scheduleDates.map((date: Date) => {
|
||||
(this.query.schedule as ScheduleItem[])
|
||||
.filter(
|
||||
(scheduleItem: ScheduleItem) => date.getUTCDay() == scheduleItem.day,
|
||||
)
|
||||
.map((scheduleItem: ScheduleItem) => {
|
||||
switch (role) {
|
||||
case Role.PASSENGER:
|
||||
schedule.push(this._wherePassengerSchedule(date, scheduleItem));
|
||||
break;
|
||||
case Role.DRIVER:
|
||||
schedule.push(this._whereDriverSchedule(date, scheduleItem));
|
||||
break;
|
||||
const [h, m] = maxScheduleTime.split(':');
|
||||
const maxFromDate = new Date(this.query.toDate);
|
||||
maxFromDate.setHours(parseInt(h));
|
||||
maxFromDate.setMinutes(parseInt(m));
|
||||
maxFromDate.setSeconds(this.query.driverRoute!.duration);
|
||||
return `'${maxFromDate.getUTCFullYear()}-${maxFromDate.getUTCMonth() + 1}-${maxFromDate.getUTCDate()}'`;
|
||||
} else {
|
||||
return `'${this.query.toDate}'`;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (schedule.length > 0) {
|
||||
return ['(', schedule.join(' OR '), ')'].join('');
|
||||
};
|
||||
|
||||
private _minToDate = (role: Role): string => {
|
||||
if (role == Role.PASSENGER) {
|
||||
// When looking for a driver, we look for a toDate that is one day before the fromDate of the query
|
||||
// so that the driver will be able to pick up the passenger even during a long trip that starts the day before
|
||||
const oneDayBeforeFromDate = new Date(this.query.fromDate);
|
||||
oneDayBeforeFromDate.setDate(oneDayBeforeFromDate.getDate() - 1);
|
||||
return `'${oneDayBeforeFromDate.getUTCFullYear()}-${oneDayBeforeFromDate.getUTCMonth() + 1}-${oneDayBeforeFromDate.getUTCDate()}'`;
|
||||
} else {
|
||||
return `'${this.query.fromDate}'`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
private _whereExcludedAd = (): string =>
|
||||
this.query.excludedAdId ? `ad.uuid <> '${this.query.excludedAdId}'` : '';
|
||||
|
||||
private _wherePassengerSchedule = (
|
||||
date: Date,
|
||||
scheduleItem: ScheduleItem,
|
||||
): string => {
|
||||
let maxDepartureDatetime: Date = new Date(date);
|
||||
maxDepartureDatetime.setHours(parseInt(scheduleItem.time.split(':')[0]));
|
||||
maxDepartureDatetime.setMinutes(parseInt(scheduleItem.time.split(':')[1]));
|
||||
maxDepartureDatetime = this._addMargin(
|
||||
maxDepartureDatetime,
|
||||
scheduleItem.margin as number,
|
||||
);
|
||||
// we want the min departure time of the driver to be before the max departure time of the passenger
|
||||
return `make_timestamp(\
|
||||
${maxDepartureDatetime.getUTCFullYear()},\
|
||||
${maxDepartureDatetime.getUTCMonth() + 1},\
|
||||
${maxDepartureDatetime.getUTCDate()},\
|
||||
CAST(EXTRACT(hour from time) as integer),\
|
||||
CAST(EXTRACT(minute from time) as integer),0) - interval '1 second' * margin <=\
|
||||
make_timestamp(\
|
||||
${maxDepartureDatetime.getUTCFullYear()},\
|
||||
${maxDepartureDatetime.getUTCMonth() + 1},\
|
||||
${maxDepartureDatetime.getUTCDate()},${maxDepartureDatetime.getUTCHours()},${maxDepartureDatetime.getUTCMinutes()},0)`;
|
||||
};
|
||||
|
||||
private _whereDriverSchedule = (
|
||||
date: Date,
|
||||
scheduleItem: ScheduleItem,
|
||||
): string => {
|
||||
let minDepartureDatetime: Date = new Date(date);
|
||||
minDepartureDatetime.setHours(parseInt(scheduleItem.time.split(':')[0]));
|
||||
minDepartureDatetime.setMinutes(parseInt(scheduleItem.time.split(':')[1]));
|
||||
minDepartureDatetime = this._addMargin(
|
||||
minDepartureDatetime,
|
||||
-(scheduleItem.margin as number),
|
||||
);
|
||||
// we want the max departure time of the passenger to be after the min departure time of the driver
|
||||
return `make_timestamp(\
|
||||
${minDepartureDatetime.getUTCFullYear()},
|
||||
${minDepartureDatetime.getUTCMonth() + 1},
|
||||
${minDepartureDatetime.getUTCDate()},\
|
||||
CAST(EXTRACT(hour from time) as integer),\
|
||||
CAST(EXTRACT(minute from time) as integer),0) + interval '1 second' * margin >=\
|
||||
make_timestamp(\
|
||||
${minDepartureDatetime.getUTCFullYear()},
|
||||
${minDepartureDatetime.getUTCMonth() + 1},
|
||||
${minDepartureDatetime.getUTCDate()},${minDepartureDatetime.getUTCHours()},${minDepartureDatetime.getUTCMinutes()},0)`;
|
||||
};
|
||||
|
||||
private _whereAzimuth = (): string => {
|
||||
if (!this.query.useAzimuth) return '';
|
||||
const { minAzimuth, maxAzimuth } = this._azimuthRange(
|
||||
|
@ -317,37 +275,6 @@ export class PassengerOrientedSelector extends Selector {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array of dates containing all the dates (limited to 7 by default) between 2 boundary dates.
|
||||
*
|
||||
* The array length can be limited to a _max_ number of dates (default: 7)
|
||||
*/
|
||||
private _datesBetweenBoundaries = (
|
||||
firstDate: string,
|
||||
lastDate: string,
|
||||
max = 7,
|
||||
): Date[] => {
|
||||
const fromDate: Date = new Date(firstDate);
|
||||
const toDate: Date = new Date(lastDate);
|
||||
const dates: Date[] = [];
|
||||
let count = 0;
|
||||
for (
|
||||
let date = fromDate;
|
||||
date <= toDate;
|
||||
date.setUTCDate(date.getUTCDate() + 1)
|
||||
) {
|
||||
dates.push(new Date(date));
|
||||
count++;
|
||||
if (count == max) break;
|
||||
}
|
||||
return dates;
|
||||
};
|
||||
|
||||
private _addMargin = (date: Date, marginInSeconds: number): Date => {
|
||||
date.setUTCSeconds(marginInSeconds);
|
||||
return date;
|
||||
};
|
||||
|
||||
private _azimuthRange = (
|
||||
azimuth: number,
|
||||
margin: number,
|
||||
|
@ -358,11 +285,26 @@ export class PassengerOrientedSelector extends Selector {
|
|||
azimuth + margin > 360 ? azimuth + margin - 360 : azimuth + margin,
|
||||
});
|
||||
|
||||
//TODO If the dates are always formatted with '%Y-%m-%d', no conversion to Date is needed
|
||||
private _maxDateString = (date1: string, date2: string): string =>
|
||||
new Date(date1) > new Date(date2) ? date1 : date2;
|
||||
|
||||
private _minDateString = (date1: string, date2: string): string =>
|
||||
new Date(date1) < new Date(date2) ? date1 : date2;
|
||||
|
||||
/**
|
||||
* When a punctual ad matches a punctual query, it may be on a different date than the query
|
||||
* (for routes by night), and the range produced by _minDateString and _maxDateString is not correct.
|
||||
* This function fixes that by inverting the dates if necessary.
|
||||
*/
|
||||
private _fixDateInterval(interval: DateInterval): DateInterval {
|
||||
if (interval.lowerDate > interval.higherDate) {
|
||||
const tmp = interval.lowerDate;
|
||||
interval.lowerDate = interval.higherDate;
|
||||
interval.higherDate = tmp;
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
}
|
||||
|
||||
export type QueryStringRole = {
|
||||
|
|
|
@ -323,7 +323,7 @@ export class CandidateEntity extends AggregateRoot<CandidateProps> {
|
|||
}
|
||||
|
||||
//TODO Use this class as part of the CandidateEntity aggregate
|
||||
class Schedule extends ValueObject<{
|
||||
export class Schedule extends ValueObject<{
|
||||
items: ScheduleItemProps[];
|
||||
dateInterval: DateInterval;
|
||||
}> {
|
||||
|
@ -353,7 +353,7 @@ class Schedule extends ValueObject<{
|
|||
duration,
|
||||
);
|
||||
acc.push({
|
||||
day: itemDate.getUTCDay(),
|
||||
day: driverStartDatetime.getUTCDay(),
|
||||
margin: scheduleItemProps.margin,
|
||||
time: this._formatTime(driverStartDatetime),
|
||||
});
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||
import { ScheduleItemProps } from '@modules/ad/core/domain/value-objects/schedule-item.value-object';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export const Nice: PointProps = {
|
||||
lat: 43.7102,
|
||||
lon: 7.262,
|
||||
};
|
||||
|
||||
export const Marseille: PointProps = {
|
||||
lat: 43.2965,
|
||||
lon: 5.3698,
|
||||
};
|
||||
|
||||
export const SaintRaphael: PointProps = {
|
||||
lat: 43.4268,
|
||||
lon: 6.769,
|
||||
};
|
||||
|
||||
export const Toulon: PointProps = {
|
||||
lat: 43.1167,
|
||||
lon: 5.95,
|
||||
};
|
||||
|
||||
export function monday(time: string): ScheduleItemProps {
|
||||
return { day: 1, time: time, margin: 900 };
|
||||
}
|
||||
|
||||
export function wednesday(time: string): ScheduleItemProps {
|
||||
return { day: 3, time: time, margin: 900 };
|
||||
}
|
||||
export function thursday(time: string): ScheduleItemProps {
|
||||
return { day: 4, time: time, margin: 900 };
|
||||
}
|
||||
|
||||
export function weekdays(time: string): ScheduleItemProps[] {
|
||||
return [1, 2, 3, 4, 5].map<ScheduleItemProps>((day) => ({
|
||||
day: day,
|
||||
time: time,
|
||||
margin: 900,
|
||||
}));
|
||||
}
|
||||
|
||||
function createAdPropsDefaults(): CreateAdProps {
|
||||
return {
|
||||
id: uuidv4(),
|
||||
driver: false,
|
||||
passenger: false,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '',
|
||||
toDate: '',
|
||||
schedule: [],
|
||||
seatsProposed: 1,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [],
|
||||
points: [],
|
||||
driverDuration: 0,
|
||||
driverDistance: 0,
|
||||
passengerDuration: 0,
|
||||
passengerDistance: 0,
|
||||
fwdAzimuth: 0,
|
||||
backAzimuth: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export function driverNiceMarseille(
|
||||
frequency: Frequency,
|
||||
dates: string[],
|
||||
schedule: ScheduleItemProps[],
|
||||
): CreateAdProps {
|
||||
return {
|
||||
...createAdPropsDefaults(),
|
||||
driver: true,
|
||||
frequency: frequency,
|
||||
fromDate: dates[0],
|
||||
toDate: dates[1],
|
||||
schedule: schedule,
|
||||
waypoints: [Nice, Marseille],
|
||||
points: [Nice, SaintRaphael, Toulon, Marseille],
|
||||
driverDuration: 7668,
|
||||
driverDistance: 199000,
|
||||
passengerDuration: 7668,
|
||||
passengerDistance: 199000,
|
||||
fwdAzimuth: 273,
|
||||
backAzimuth: 93,
|
||||
};
|
||||
}
|
||||
|
||||
export function passengerToulonMarseille(
|
||||
frequency: Frequency,
|
||||
dates: string[],
|
||||
schedule: ScheduleItemProps[],
|
||||
): CreateAdProps {
|
||||
return {
|
||||
...createAdPropsDefaults(),
|
||||
passenger: true,
|
||||
frequency: frequency,
|
||||
fromDate: dates[0],
|
||||
toDate: dates[1],
|
||||
schedule: schedule,
|
||||
waypoints: [Toulon, Marseille],
|
||||
points: [Toulon, Marseille],
|
||||
driverDuration: 2460,
|
||||
driverDistance: 64000,
|
||||
passengerDuration: 2460,
|
||||
passengerDistance: 64000,
|
||||
};
|
||||
}
|
|
@ -1,61 +1,16 @@
|
|||
import {
|
||||
AD_DIRECTION_ENCODER,
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { driverNiceMarseille, wednesday, weekdays } from './ad.fixtures';
|
||||
import { integrationTestingModule } from './integration.setup';
|
||||
|
||||
describe('Ad Repository', () => {
|
||||
let prismaService: PrismaService;
|
||||
let adRepository: AdRepository;
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockLogger = {
|
||||
log: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
EventEmitterModule.forRoot(),
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
],
|
||||
providers: [
|
||||
PrismaService,
|
||||
AdMapper,
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useClass: AdRepository,
|
||||
},
|
||||
{
|
||||
provide: AD_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
{
|
||||
provide: AD_DIRECTION_ENCODER,
|
||||
useClass: PostgresDirectionEncoder,
|
||||
},
|
||||
],
|
||||
})
|
||||
// disable logging
|
||||
.setLogger(mockLogger)
|
||||
.compile();
|
||||
|
||||
prismaService = module.get<PrismaService>(PrismaService);
|
||||
adRepository = module.get<AdRepository>(AD_REPOSITORY);
|
||||
({ prismaService, adRepository } = await integrationTestingModule());
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
@ -70,60 +25,12 @@ describe('Ad Repository', () => {
|
|||
it('should create a punctual ad', async () => {
|
||||
const beforeCount = await prismaService.ad.count();
|
||||
|
||||
const createAdProps: CreateAdProps = {
|
||||
id: 'b4b56444-f8d3-4110-917c-e37bba77f383',
|
||||
driver: true,
|
||||
passenger: false,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-02-01',
|
||||
toDate: '2023-02-01',
|
||||
schedule: [
|
||||
{
|
||||
day: 3,
|
||||
time: '12:05',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [
|
||||
{
|
||||
lon: 43.7102,
|
||||
lat: 7.262,
|
||||
},
|
||||
{
|
||||
lon: 43.2965,
|
||||
lat: 5.3698,
|
||||
},
|
||||
],
|
||||
points: [
|
||||
{
|
||||
lon: 7.262,
|
||||
lat: 43.7102,
|
||||
},
|
||||
{
|
||||
lon: 6.797838,
|
||||
lat: 43.547031,
|
||||
},
|
||||
{
|
||||
lon: 6.18535,
|
||||
lat: 43.407517,
|
||||
},
|
||||
{
|
||||
lon: 5.3698,
|
||||
lat: 43.2965,
|
||||
},
|
||||
],
|
||||
driverDuration: 7668,
|
||||
driverDistance: 199000,
|
||||
passengerDuration: 7668,
|
||||
passengerDistance: 199000,
|
||||
fwdAzimuth: 273,
|
||||
backAzimuth: 93,
|
||||
};
|
||||
|
||||
const adToCreate: AdEntity = AdEntity.create(createAdProps);
|
||||
const createAdProps = driverNiceMarseille(
|
||||
Frequency.PUNCTUAL,
|
||||
['2023-02-01', '2023-02-01'],
|
||||
[wednesday('08:30')],
|
||||
);
|
||||
const adToCreate = AdEntity.create(createAdProps);
|
||||
await adRepository.insertExtra(adToCreate, 'ad');
|
||||
|
||||
const afterCount = await prismaService.ad.count();
|
||||
|
@ -134,80 +41,13 @@ describe('Ad Repository', () => {
|
|||
it('should create a recurrent ad', async () => {
|
||||
const beforeCount = await prismaService.ad.count();
|
||||
|
||||
const createAdProps: CreateAdProps = {
|
||||
id: 'b4b56444-f8d3-4110-917c-e37bba77f383',
|
||||
driver: true,
|
||||
passenger: false,
|
||||
frequency: Frequency.RECURRENT,
|
||||
fromDate: '2023-02-01',
|
||||
toDate: '2024-01-31',
|
||||
schedule: [
|
||||
{
|
||||
day: 1,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 2,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 3,
|
||||
time: '09:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 4,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 5,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [
|
||||
{
|
||||
lon: 43.7102,
|
||||
lat: 7.262,
|
||||
},
|
||||
{
|
||||
lon: 43.2965,
|
||||
lat: 5.3698,
|
||||
},
|
||||
],
|
||||
points: [
|
||||
{
|
||||
lon: 7.262,
|
||||
lat: 43.7102,
|
||||
},
|
||||
{
|
||||
lon: 6.797838,
|
||||
lat: 43.547031,
|
||||
},
|
||||
{
|
||||
lon: 6.18535,
|
||||
lat: 43.407517,
|
||||
},
|
||||
{
|
||||
lon: 5.3698,
|
||||
lat: 43.2965,
|
||||
},
|
||||
],
|
||||
driverDuration: 7668,
|
||||
driverDistance: 199000,
|
||||
passengerDuration: 7668,
|
||||
passengerDistance: 199000,
|
||||
fwdAzimuth: 273,
|
||||
backAzimuth: 93,
|
||||
};
|
||||
const createAdProps = driverNiceMarseille(
|
||||
Frequency.RECURRENT,
|
||||
['2023-02-01', '2024-01-31'],
|
||||
weekdays('08:30'),
|
||||
);
|
||||
|
||||
const adToCreate: AdEntity = AdEntity.create(createAdProps);
|
||||
const adToCreate = AdEntity.create(createAdProps);
|
||||
await adRepository.insertExtra(adToCreate, 'ad');
|
||||
|
||||
const afterCount = await prismaService.ad.count();
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
AD_DIRECTION_ENCODER,
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
export async function integrationTestingModule(): Promise<{
|
||||
prismaService: PrismaService;
|
||||
adRepository: AdRepository;
|
||||
}> {
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockLogger = {
|
||||
log: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
EventEmitterModule.forRoot(),
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
],
|
||||
providers: [
|
||||
PrismaService,
|
||||
AdMapper,
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useClass: AdRepository,
|
||||
},
|
||||
{
|
||||
provide: AD_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
{
|
||||
provide: AD_DIRECTION_ENCODER,
|
||||
useClass: PostgresDirectionEncoder,
|
||||
},
|
||||
],
|
||||
})
|
||||
.setLogger(mockLogger)
|
||||
.compile();
|
||||
|
||||
return {
|
||||
prismaService: module.get<PrismaService>(PrismaService),
|
||||
adRepository: module.get<AdRepository>(AD_REPOSITORY),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,467 @@
|
|||
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<void> => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,8 +1,8 @@
|
|||
import {
|
||||
Domain,
|
||||
KeyType,
|
||||
Configurator,
|
||||
Domain,
|
||||
GetConfigurationRepositoryPort,
|
||||
KeyType,
|
||||
} from '@mobicoop/configuration-module';
|
||||
import {
|
||||
CARPOOL_CONFIG_DEPARTURE_TIME_MARGIN,
|
||||
|
@ -16,8 +16,9 @@ import {
|
|||
AD_REPOSITORY,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
MATCHING_REPOSITORY,
|
||||
TIMEZONE_FINDER,
|
||||
TIME_CONVERTER,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { MatchingRepositoryPort } from '@modules/ad/core/application/ports/matching.repository.port';
|
||||
import { MatchQuery } from '@modules/ad/core/application/queries/match/match.query';
|
||||
import {
|
||||
|
@ -30,6 +31,9 @@ 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 { MatchingEntity } from '@modules/ad/core/domain/matching.entity';
|
||||
import { InputDateTimeTransformer } from '@modules/ad/infrastructure/input-datetime-transformer';
|
||||
import { TimeConverter } from '@modules/ad/infrastructure/time-converter';
|
||||
import { TimezoneFinder } from '@modules/ad/infrastructure/timezone-finder';
|
||||
import {
|
||||
MATCH_CONFIG_ALGORITHM,
|
||||
MATCH_CONFIG_AZIMUTH_MARGIN,
|
||||
|
@ -344,13 +348,6 @@ const mockConfigurationRepository: GetConfigurationRepositoryPort = {
|
|||
),
|
||||
};
|
||||
|
||||
const mockInputDateTimeTransformer: DateTimeTransformerPort = {
|
||||
fromDate: jest.fn(),
|
||||
toDate: jest.fn(),
|
||||
day: jest.fn(),
|
||||
time: jest.fn(),
|
||||
};
|
||||
|
||||
const mockRouteProvider = simpleMockGeorouter;
|
||||
|
||||
describe('Match Query Handler', () => {
|
||||
|
@ -372,9 +369,17 @@ describe('Match Query Handler', () => {
|
|||
provide: AD_CONFIGURATION_REPOSITORY,
|
||||
useValue: mockConfigurationRepository,
|
||||
},
|
||||
{
|
||||
provide: TIMEZONE_FINDER,
|
||||
useClass: TimezoneFinder,
|
||||
},
|
||||
{
|
||||
provide: TIME_CONVERTER,
|
||||
useClass: TimeConverter,
|
||||
},
|
||||
{
|
||||
provide: INPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockInputDateTimeTransformer,
|
||||
useClass: InputDateTimeTransformer,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
|
|
@ -72,27 +72,7 @@ matchQuery.driverRoute = {
|
|||
},
|
||||
],
|
||||
};
|
||||
matchQuery.passengerRoute = {
|
||||
distance: 150120,
|
||||
duration: 6540,
|
||||
fwdAzimuth: 276,
|
||||
backAzimuth: 96,
|
||||
distanceAzimuth: 148321,
|
||||
points: [
|
||||
{
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
{
|
||||
lat: 48.7566,
|
||||
lon: 4.3522,
|
||||
},
|
||||
{
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
],
|
||||
};
|
||||
matchQuery.passengerRoute = { ...matchQuery.driverRoute };
|
||||
|
||||
const mockMatcherRepository: AdRepositoryPort = {
|
||||
insertExtra: jest.fn(),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
"exclude": ["node_modules", "tests", "dist", "**/*spec.ts"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue