basic match query
This commit is contained in:
		
							parent
							
								
									bca3374255
								
							
						
					
					
						commit
						a98e5b3c83
					
				| 
						 | 
					@ -2,7 +2,6 @@ import { Frequency } from '@modules/ad/core/domain/ad.types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Ad = {
 | 
					export type Ad = {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
  userId: string;
 | 
					 | 
				
			||||||
  driver: boolean;
 | 
					  driver: boolean;
 | 
				
			||||||
  passenger: boolean;
 | 
					  passenger: boolean;
 | 
				
			||||||
  frequency: Frequency;
 | 
					  frequency: Frequency;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					import { QueryBase } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					import { Frequency } from '@modules/matcher/core/domain/match.types';
 | 
				
			||||||
 | 
					import { ScheduleItem } from '../../types/schedule-item';
 | 
				
			||||||
 | 
					import { Waypoint } from '../../types/waypoint';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class MatchQuery extends QueryBase {
 | 
				
			||||||
 | 
					  readonly driver?: boolean;
 | 
				
			||||||
 | 
					  readonly passenger?: boolean;
 | 
				
			||||||
 | 
					  readonly frequency?: Frequency;
 | 
				
			||||||
 | 
					  readonly fromDate: string;
 | 
				
			||||||
 | 
					  readonly toDate: string;
 | 
				
			||||||
 | 
					  readonly schedule: ScheduleItem[];
 | 
				
			||||||
 | 
					  readonly strict?: boolean;
 | 
				
			||||||
 | 
					  readonly waypoints: Waypoint[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(props: MatchQuery) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					    this.driver = props.driver;
 | 
				
			||||||
 | 
					    this.passenger = props.passenger;
 | 
				
			||||||
 | 
					    this.frequency = props.frequency;
 | 
				
			||||||
 | 
					    this.fromDate = props.fromDate;
 | 
				
			||||||
 | 
					    this.toDate = props.toDate;
 | 
				
			||||||
 | 
					    this.schedule = props.schedule;
 | 
				
			||||||
 | 
					    this.strict = props.strict;
 | 
				
			||||||
 | 
					    this.waypoints = props.waypoints;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					import { Coordinates } from './coordinates';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Address = {
 | 
				
			||||||
 | 
					  name?: string;
 | 
				
			||||||
 | 
					  houseNumber?: string;
 | 
				
			||||||
 | 
					  street?: string;
 | 
				
			||||||
 | 
					  locality?: string;
 | 
				
			||||||
 | 
					  postalCode?: string;
 | 
				
			||||||
 | 
					  country: string;
 | 
				
			||||||
 | 
					} & Coordinates;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					export type Coordinates = {
 | 
				
			||||||
 | 
					  lon: number;
 | 
				
			||||||
 | 
					  lat: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					export type ScheduleItem = {
 | 
				
			||||||
 | 
					  day?: number;
 | 
				
			||||||
 | 
					  time: string;
 | 
				
			||||||
 | 
					  margin?: number;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					import { Address } from './address';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type Waypoint = {
 | 
				
			||||||
 | 
					  position?: number;
 | 
				
			||||||
 | 
					} & Address;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					export enum Frequency {
 | 
				
			||||||
 | 
					  PUNCTUAL = 'PUNCTUAL',
 | 
				
			||||||
 | 
					  RECURRENT = 'RECURRENT',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export enum AlgorithmType {
 | 
				
			||||||
 | 
					  CLASSIC = 'CLASSIC',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					import { PaginatedResponseDto } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					import { MatchResponseDto } from './match.response.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class MatchPaginatedResponseDto extends PaginatedResponseDto<MatchResponseDto> {
 | 
				
			||||||
 | 
					  readonly data: readonly MatchResponseDto[];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					export class MatchResponseDto {
 | 
				
			||||||
 | 
					  adId: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					import { IsOptional, IsString } from 'class-validator';
 | 
				
			||||||
 | 
					import { CoordinatesDto as CoordinatesDto } from './coordinates.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class AddressDto extends CoordinatesDto {
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  name?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  houseNumber?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  street?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  locality?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  postalCode?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  country: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					import { IsLatitude, IsLongitude } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CoordinatesDto {
 | 
				
			||||||
 | 
					  @IsLongitude()
 | 
				
			||||||
 | 
					  lon: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsLatitude()
 | 
				
			||||||
 | 
					  lat: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  AlgorithmType,
 | 
				
			||||||
 | 
					  Frequency,
 | 
				
			||||||
 | 
					} from '@modules/matcher/core/domain/match.types';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ArrayMinSize,
 | 
				
			||||||
 | 
					  IsArray,
 | 
				
			||||||
 | 
					  IsBoolean,
 | 
				
			||||||
 | 
					  IsDecimal,
 | 
				
			||||||
 | 
					  IsEnum,
 | 
				
			||||||
 | 
					  IsISO8601,
 | 
				
			||||||
 | 
					  IsInt,
 | 
				
			||||||
 | 
					  IsOptional,
 | 
				
			||||||
 | 
					  Max,
 | 
				
			||||||
 | 
					  Min,
 | 
				
			||||||
 | 
					  ValidateNested,
 | 
				
			||||||
 | 
					} from 'class-validator';
 | 
				
			||||||
 | 
					import { Type } from 'class-transformer';
 | 
				
			||||||
 | 
					import { HasDay } from './validators/decorators/has-day.decorator';
 | 
				
			||||||
 | 
					import { IsAfterOrEqual } from './validators/decorators/is-after-or-equal.decorator';
 | 
				
			||||||
 | 
					import { ScheduleItemDto } from './schedule-item.dto';
 | 
				
			||||||
 | 
					import { WaypointDto } from './waypoint.dto';
 | 
				
			||||||
 | 
					import { HasValidPositionIndexes } from './validators/decorators/has-valid-position-indexes.decorator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class MatchRequestDto {
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  driver?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  passenger?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsEnum(Frequency)
 | 
				
			||||||
 | 
					  @HasDay('schedule', {
 | 
				
			||||||
 | 
					    message: 'At least a day is required for a recurrent ad',
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  frequency: Frequency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsISO8601({
 | 
				
			||||||
 | 
					    strict: true,
 | 
				
			||||||
 | 
					    strictSeparator: true,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  fromDate: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsISO8601({
 | 
				
			||||||
 | 
					    strict: true,
 | 
				
			||||||
 | 
					    strictSeparator: true,
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  @IsAfterOrEqual('fromDate', {
 | 
				
			||||||
 | 
					    message: 'toDate must be after or equal to fromDate',
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  toDate: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Type(() => ScheduleItemDto)
 | 
				
			||||||
 | 
					  @IsArray()
 | 
				
			||||||
 | 
					  @ArrayMinSize(1)
 | 
				
			||||||
 | 
					  @ValidateNested({ each: true })
 | 
				
			||||||
 | 
					  schedule: ScheduleItemDto[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  seatsProposed?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  seatsRequested?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  strict?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Type(() => WaypointDto)
 | 
				
			||||||
 | 
					  @IsArray()
 | 
				
			||||||
 | 
					  @ArrayMinSize(2)
 | 
				
			||||||
 | 
					  @HasValidPositionIndexes()
 | 
				
			||||||
 | 
					  @ValidateNested({ each: true })
 | 
				
			||||||
 | 
					  waypoints: WaypointDto[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsEnum(AlgorithmType)
 | 
				
			||||||
 | 
					  algorithm?: AlgorithmType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  remoteness?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  useProportion?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsDecimal()
 | 
				
			||||||
 | 
					  @Min(0)
 | 
				
			||||||
 | 
					  @Max(1)
 | 
				
			||||||
 | 
					  proportion?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsBoolean()
 | 
				
			||||||
 | 
					  useAzimuth?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(0)
 | 
				
			||||||
 | 
					  @Max(359)
 | 
				
			||||||
 | 
					  azimuthMargin?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsDecimal()
 | 
				
			||||||
 | 
					  @Min(0)
 | 
				
			||||||
 | 
					  @Max(1)
 | 
				
			||||||
 | 
					  maxDetourDistanceRatio?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsDecimal()
 | 
				
			||||||
 | 
					  @Min(0)
 | 
				
			||||||
 | 
					  @Max(1)
 | 
				
			||||||
 | 
					  maxDetourDurationRatio?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					import { IsOptional, IsMilitaryTime, IsInt, Min, Max } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class ScheduleItemDto {
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  @Min(0)
 | 
				
			||||||
 | 
					  @Max(6)
 | 
				
			||||||
 | 
					  day?: number;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsMilitaryTime()
 | 
				
			||||||
 | 
					  time: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  margin?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					import { Frequency } from '@modules/ad/core/domain/ad.types';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  registerDecorator,
 | 
				
			||||||
 | 
					  ValidationOptions,
 | 
				
			||||||
 | 
					  ValidationArguments,
 | 
				
			||||||
 | 
					} from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function HasDay(
 | 
				
			||||||
 | 
					  property: string,
 | 
				
			||||||
 | 
					  validationOptions?: ValidationOptions,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  return function (object: object, propertyName: string) {
 | 
				
			||||||
 | 
					    registerDecorator({
 | 
				
			||||||
 | 
					      name: 'hasDay',
 | 
				
			||||||
 | 
					      target: object.constructor,
 | 
				
			||||||
 | 
					      propertyName: propertyName,
 | 
				
			||||||
 | 
					      constraints: [property],
 | 
				
			||||||
 | 
					      options: validationOptions,
 | 
				
			||||||
 | 
					      validator: {
 | 
				
			||||||
 | 
					        validate(value: any, args: ValidationArguments) {
 | 
				
			||||||
 | 
					          const [relatedPropertyName] = args.constraints;
 | 
				
			||||||
 | 
					          const relatedValue = (args.object as any)[relatedPropertyName];
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            value == Frequency.PUNCTUAL ||
 | 
				
			||||||
 | 
					            (Array.isArray(relatedValue) &&
 | 
				
			||||||
 | 
					              relatedValue.some((scheduleItem) =>
 | 
				
			||||||
 | 
					                scheduleItem.hasOwnProperty('day'),
 | 
				
			||||||
 | 
					              ))
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					import { ValidateBy, ValidationOptions, buildMessage } from 'class-validator';
 | 
				
			||||||
 | 
					import { hasValidPositionIndexes } from '../has-valid-position-indexes.validator';
 | 
				
			||||||
 | 
					import { WaypointDto } from '../../waypoint.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const HasValidPositionIndexes = (
 | 
				
			||||||
 | 
					  validationOptions?: ValidationOptions,
 | 
				
			||||||
 | 
					): PropertyDecorator =>
 | 
				
			||||||
 | 
					  ValidateBy(
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      name: '',
 | 
				
			||||||
 | 
					      constraints: [],
 | 
				
			||||||
 | 
					      validator: {
 | 
				
			||||||
 | 
					        validate: (waypoints: WaypointDto[]): boolean =>
 | 
				
			||||||
 | 
					          hasValidPositionIndexes(waypoints),
 | 
				
			||||||
 | 
					        defaultMessage: buildMessage(
 | 
				
			||||||
 | 
					          () => `invalid waypoints positions`,
 | 
				
			||||||
 | 
					          validationOptions,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    validationOptions,
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  registerDecorator,
 | 
				
			||||||
 | 
					  ValidationOptions,
 | 
				
			||||||
 | 
					  ValidationArguments,
 | 
				
			||||||
 | 
					  isISO8601,
 | 
				
			||||||
 | 
					} from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function IsAfterOrEqual(
 | 
				
			||||||
 | 
					  property: string,
 | 
				
			||||||
 | 
					  validationOptions?: ValidationOptions,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  return function (object: object, propertyName: string) {
 | 
				
			||||||
 | 
					    registerDecorator({
 | 
				
			||||||
 | 
					      name: 'isAfterOrEqual',
 | 
				
			||||||
 | 
					      target: object.constructor,
 | 
				
			||||||
 | 
					      propertyName: propertyName,
 | 
				
			||||||
 | 
					      constraints: [property],
 | 
				
			||||||
 | 
					      options: validationOptions,
 | 
				
			||||||
 | 
					      validator: {
 | 
				
			||||||
 | 
					        validate(value: any, args: ValidationArguments) {
 | 
				
			||||||
 | 
					          const [relatedPropertyName] = args.constraints;
 | 
				
			||||||
 | 
					          const relatedValue = (args.object as any)[relatedPropertyName];
 | 
				
			||||||
 | 
					          if (
 | 
				
			||||||
 | 
					            !(
 | 
				
			||||||
 | 
					              typeof value === 'string' &&
 | 
				
			||||||
 | 
					              typeof relatedValue === 'string' &&
 | 
				
			||||||
 | 
					              isISO8601(value, {
 | 
				
			||||||
 | 
					                strict: true,
 | 
				
			||||||
 | 
					                strictSeparator: true,
 | 
				
			||||||
 | 
					              }) &&
 | 
				
			||||||
 | 
					              isISO8601(relatedValue, {
 | 
				
			||||||
 | 
					                strict: true,
 | 
				
			||||||
 | 
					                strictSeparator: true,
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					          return new Date(value) >= new Date(relatedValue);
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					import { WaypointDto } from '../waypoint.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const hasValidPositionIndexes = (waypoints: WaypointDto[]): boolean => {
 | 
				
			||||||
 | 
					  if (!waypoints) return false;
 | 
				
			||||||
 | 
					  if (waypoints.length == 0) return false;
 | 
				
			||||||
 | 
					  if (waypoints.every((waypoint) => waypoint.position === undefined))
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  if (waypoints.every((waypoint) => typeof waypoint.position === 'number')) {
 | 
				
			||||||
 | 
					    const positions = Array.from(waypoints, (waypoint) => waypoint.position);
 | 
				
			||||||
 | 
					    positions.sort();
 | 
				
			||||||
 | 
					    for (let i = 1; i < positions.length; i++)
 | 
				
			||||||
 | 
					      if (positions[i] != positions[i - 1] + 1) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return false;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					import { IsInt, IsOptional } from 'class-validator';
 | 
				
			||||||
 | 
					import { AddressDto } from './address.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class WaypointDto extends AddressDto {
 | 
				
			||||||
 | 
					  @IsOptional()
 | 
				
			||||||
 | 
					  @IsInt()
 | 
				
			||||||
 | 
					  position?: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					import { Controller, UsePipes } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { GrpcMethod, RpcException } from '@nestjs/microservices';
 | 
				
			||||||
 | 
					import { RpcValidationPipe } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					import { RpcExceptionCode } from '@mobicoop/ddd-library';
 | 
				
			||||||
 | 
					import { MatchPaginatedResponseDto } from '../dtos/match.paginated.response.dto';
 | 
				
			||||||
 | 
					import { QueryBus } from '@nestjs/cqrs';
 | 
				
			||||||
 | 
					import { MatchRequestDto } from './dtos/match.request.dto';
 | 
				
			||||||
 | 
					import { MatchQuery } from '@modules/matcher/core/application/queries/match/match.query';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@UsePipes(
 | 
				
			||||||
 | 
					  new RpcValidationPipe({
 | 
				
			||||||
 | 
					    whitelist: false,
 | 
				
			||||||
 | 
					    forbidUnknownValues: false,
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					@Controller()
 | 
				
			||||||
 | 
					export class MatchGrpcController {
 | 
				
			||||||
 | 
					  constructor(private readonly queryBus: QueryBus) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @GrpcMethod('MatcherService', 'Match')
 | 
				
			||||||
 | 
					  async match(data: MatchRequestDto): Promise<MatchPaginatedResponseDto> {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const matches = await this.queryBus.execute(new MatchQuery(data));
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        data: matches,
 | 
				
			||||||
 | 
					        page: 1,
 | 
				
			||||||
 | 
					        perPage: 5,
 | 
				
			||||||
 | 
					        total: 1,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      throw new RpcException({
 | 
				
			||||||
 | 
					        code: RpcExceptionCode.UNKNOWN,
 | 
				
			||||||
 | 
					        message: e.message,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					import { Frequency } from '@modules/ad/core/domain/ad.types';
 | 
				
			||||||
 | 
					import { ScheduleItemDto } from '@modules/matcher/interface/grpc-controllers/dtos/schedule-item.dto';
 | 
				
			||||||
 | 
					import { HasDay } from '@modules/matcher/interface/grpc-controllers/dtos/validators/decorators/has-day.decorator';
 | 
				
			||||||
 | 
					import { Validator } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Has day decorator', () => {
 | 
				
			||||||
 | 
					  class MyClass {
 | 
				
			||||||
 | 
					    @HasDay('schedule', {
 | 
				
			||||||
 | 
					      message: 'At least a day is required for a recurrent ad',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    frequency: Frequency;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    schedule: ScheduleItemDto[];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should return a property decorator has a function', () => {
 | 
				
			||||||
 | 
					    const hasDay = HasDay('someProperty');
 | 
				
			||||||
 | 
					    expect(typeof hasDay).toBe('function');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should validate a punctual frequency associated with a valid schedule', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.frequency = Frequency.PUNCTUAL;
 | 
				
			||||||
 | 
					    myClassInstance.schedule = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        time: '07:15',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should validate a recurrent frequency associated with a valid schedule', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.frequency = Frequency.RECURRENT;
 | 
				
			||||||
 | 
					    myClassInstance.schedule = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        time: '07:15',
 | 
				
			||||||
 | 
					        day: 1,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should not validate a recurrent frequency associated with an invalid schedule', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.frequency = Frequency.RECURRENT;
 | 
				
			||||||
 | 
					    myClassInstance.schedule = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        time: '07:15',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					import { HasValidPositionIndexes } from '@modules/matcher/interface/grpc-controllers/dtos/validators/decorators/has-valid-position-indexes.decorator';
 | 
				
			||||||
 | 
					import { WaypointDto } from '@modules/matcher/interface/grpc-controllers/dtos/waypoint.dto';
 | 
				
			||||||
 | 
					import { Validator } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('valid position indexes decorator', () => {
 | 
				
			||||||
 | 
					  class MyClass {
 | 
				
			||||||
 | 
					    @HasValidPositionIndexes()
 | 
				
			||||||
 | 
					    waypoints: WaypointDto[];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  it('should return a property decorator has a function', () => {
 | 
				
			||||||
 | 
					    const hasValidPositionIndexes = HasValidPositionIndexes();
 | 
				
			||||||
 | 
					    expect(typeof hasValidPositionIndexes).toBe('function');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  it('should validate an array of waypoints with valid positions', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.waypoints = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        position: 0,
 | 
				
			||||||
 | 
					        lon: 48.8566,
 | 
				
			||||||
 | 
					        lat: 2.3522,
 | 
				
			||||||
 | 
					        locality: 'Paris',
 | 
				
			||||||
 | 
					        postalCode: '75000',
 | 
				
			||||||
 | 
					        country: 'France',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        position: 1,
 | 
				
			||||||
 | 
					        lon: 49.2628,
 | 
				
			||||||
 | 
					        lat: 4.0347,
 | 
				
			||||||
 | 
					        locality: 'Reims',
 | 
				
			||||||
 | 
					        postalCode: '51454',
 | 
				
			||||||
 | 
					        country: 'France',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  it('should not validate an array of waypoints with invalid positions', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.waypoints = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        position: 1,
 | 
				
			||||||
 | 
					        lon: 48.8566,
 | 
				
			||||||
 | 
					        lat: 2.3522,
 | 
				
			||||||
 | 
					        locality: 'Paris',
 | 
				
			||||||
 | 
					        postalCode: '75000',
 | 
				
			||||||
 | 
					        country: 'France',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        position: 1,
 | 
				
			||||||
 | 
					        lon: 49.2628,
 | 
				
			||||||
 | 
					        lat: 4.0347,
 | 
				
			||||||
 | 
					        locality: 'Reims',
 | 
				
			||||||
 | 
					        postalCode: '51454',
 | 
				
			||||||
 | 
					        country: 'France',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,75 @@
 | 
				
			||||||
 | 
					import { hasValidPositionIndexes } from '@modules/matcher/interface/grpc-controllers/dtos/validators/has-valid-position-indexes.validator';
 | 
				
			||||||
 | 
					import { WaypointDto } from '@modules/matcher/interface/grpc-controllers/dtos/waypoint.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('addresses position validator', () => {
 | 
				
			||||||
 | 
					  const mockAddress1: WaypointDto = {
 | 
				
			||||||
 | 
					    lon: 48.689445,
 | 
				
			||||||
 | 
					    lat: 6.17651,
 | 
				
			||||||
 | 
					    houseNumber: '5',
 | 
				
			||||||
 | 
					    street: 'Avenue Foch',
 | 
				
			||||||
 | 
					    locality: 'Nancy',
 | 
				
			||||||
 | 
					    postalCode: '54000',
 | 
				
			||||||
 | 
					    country: 'France',
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  const mockAddress2: WaypointDto = {
 | 
				
			||||||
 | 
					    lon: 48.8566,
 | 
				
			||||||
 | 
					    lat: 2.3522,
 | 
				
			||||||
 | 
					    locality: 'Paris',
 | 
				
			||||||
 | 
					    postalCode: '75000',
 | 
				
			||||||
 | 
					    country: 'France',
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  const mockAddress3: WaypointDto = {
 | 
				
			||||||
 | 
					    lon: 49.2628,
 | 
				
			||||||
 | 
					    lat: 4.0347,
 | 
				
			||||||
 | 
					    locality: 'Reims',
 | 
				
			||||||
 | 
					    postalCode: '51454',
 | 
				
			||||||
 | 
					    country: 'France',
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should not validate if no position is defined', () => {
 | 
				
			||||||
 | 
					    expect(
 | 
				
			||||||
 | 
					      hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]),
 | 
				
			||||||
 | 
					    ).toBeFalsy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  it('should not validate if only one position is defined', () => {
 | 
				
			||||||
 | 
					    mockAddress1.position = 0;
 | 
				
			||||||
 | 
					    expect(
 | 
				
			||||||
 | 
					      hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]),
 | 
				
			||||||
 | 
					    ).toBeFalsy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  it('should not validate if positions are partially defined', () => {
 | 
				
			||||||
 | 
					    mockAddress1.position = 0;
 | 
				
			||||||
 | 
					    mockAddress2.position = null;
 | 
				
			||||||
 | 
					    mockAddress3.position = undefined;
 | 
				
			||||||
 | 
					    expect(
 | 
				
			||||||
 | 
					      hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]),
 | 
				
			||||||
 | 
					    ).toBeFalsy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should not validate if multiple positions have same value', () => {
 | 
				
			||||||
 | 
					    mockAddress1.position = 0;
 | 
				
			||||||
 | 
					    mockAddress2.position = 1;
 | 
				
			||||||
 | 
					    mockAddress3.position = 1;
 | 
				
			||||||
 | 
					    expect(
 | 
				
			||||||
 | 
					      hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]),
 | 
				
			||||||
 | 
					    ).toBeFalsy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  it('should validate if all positions are ordered', () => {
 | 
				
			||||||
 | 
					    mockAddress1.position = 0;
 | 
				
			||||||
 | 
					    mockAddress2.position = 1;
 | 
				
			||||||
 | 
					    mockAddress3.position = 2;
 | 
				
			||||||
 | 
					    expect(
 | 
				
			||||||
 | 
					      hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]),
 | 
				
			||||||
 | 
					    ).toBeTruthy();
 | 
				
			||||||
 | 
					    mockAddress1.position = 1;
 | 
				
			||||||
 | 
					    mockAddress2.position = 2;
 | 
				
			||||||
 | 
					    mockAddress3.position = 3;
 | 
				
			||||||
 | 
					    expect(
 | 
				
			||||||
 | 
					      hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]),
 | 
				
			||||||
 | 
					    ).toBeTruthy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  it('should not validate if no waypoints are defined', () => {
 | 
				
			||||||
 | 
					    expect(hasValidPositionIndexes(undefined)).toBeFalsy();
 | 
				
			||||||
 | 
					    expect(hasValidPositionIndexes([])).toBeFalsy();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					import { IsAfterOrEqual } from '@modules/matcher/interface/grpc-controllers/dtos/validators/decorators/is-after-or-equal.decorator';
 | 
				
			||||||
 | 
					import { Validator } from 'class-validator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Is after or equal decorator', () => {
 | 
				
			||||||
 | 
					  class MyClass {
 | 
				
			||||||
 | 
					    firstDate: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @IsAfterOrEqual('firstDate', {
 | 
				
			||||||
 | 
					      message: 'secondDate must be after or equal to firstDate',
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    secondDate: string;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should return a property decorator has a function', () => {
 | 
				
			||||||
 | 
					    const isAfterOrEqual = IsAfterOrEqual('someProperty');
 | 
				
			||||||
 | 
					    expect(typeof isAfterOrEqual).toBe('function');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should validate a secondDate posterior to firstDate', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.firstDate = '2023-07-20';
 | 
				
			||||||
 | 
					    myClassInstance.secondDate = '2023-07-30';
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should not validate a secondDate prior to firstDate', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.firstDate = '2023-07-20';
 | 
				
			||||||
 | 
					    myClassInstance.secondDate = '2023-07-19';
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should not validate if dates are invalid', async () => {
 | 
				
			||||||
 | 
					    const myClassInstance = new MyClass();
 | 
				
			||||||
 | 
					    myClassInstance.firstDate = '2023-07-40';
 | 
				
			||||||
 | 
					    myClassInstance.secondDate = '2023-07-19';
 | 
				
			||||||
 | 
					    const validator = new Validator();
 | 
				
			||||||
 | 
					    const validation = await validator.validate(myClassInstance);
 | 
				
			||||||
 | 
					    expect(validation.length).toBe(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,82 @@
 | 
				
			||||||
 | 
					import { Frequency } from '@modules/matcher/core/domain/match.types';
 | 
				
			||||||
 | 
					import { MatchRequestDto } from '@modules/matcher/interface/grpc-controllers/dtos/match.request.dto';
 | 
				
			||||||
 | 
					import { WaypointDto } from '@modules/matcher/interface/grpc-controllers/dtos/waypoint.dto';
 | 
				
			||||||
 | 
					import { MatchGrpcController } from '@modules/matcher/interface/grpc-controllers/match.grpc-controller';
 | 
				
			||||||
 | 
					import { QueryBus } from '@nestjs/cqrs';
 | 
				
			||||||
 | 
					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 punctualMatchRequestDto: MatchRequestDto = {
 | 
				
			||||||
 | 
					  passenger: true,
 | 
				
			||||||
 | 
					  frequency: Frequency.PUNCTUAL,
 | 
				
			||||||
 | 
					  fromDate: '2023-08-15',
 | 
				
			||||||
 | 
					  toDate: '2023-08-15',
 | 
				
			||||||
 | 
					  schedule: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      time: '07:00',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  waypoints: [originWaypoint, destinationWaypoint],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mockQueryBus = {
 | 
				
			||||||
 | 
					  execute: jest.fn().mockImplementation(() => [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      adId: 1,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      adId: 2,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ]),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Match Grpc Controller', () => {
 | 
				
			||||||
 | 
					  let matchGrpcController: MatchGrpcController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeAll(async () => {
 | 
				
			||||||
 | 
					    const module: TestingModule = await Test.createTestingModule({
 | 
				
			||||||
 | 
					      providers: [
 | 
				
			||||||
 | 
					        MatchGrpcController,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					          provide: QueryBus,
 | 
				
			||||||
 | 
					          useValue: mockQueryBus,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
 | 
					    }).compile();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    matchGrpcController = module.get<MatchGrpcController>(MatchGrpcController);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  afterEach(async () => {
 | 
				
			||||||
 | 
					    jest.clearAllMocks();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should be defined', () => {
 | 
				
			||||||
 | 
					    expect(matchGrpcController).toBeDefined();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should return matches', async () => {
 | 
				
			||||||
 | 
					    const matchPaginatedResponseDto = await matchGrpcController.match(
 | 
				
			||||||
 | 
					      punctualMatchRequestDto,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    expect(matchPaginatedResponseDto.data).toHaveLength(2);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
		Loading…
	
		Reference in New Issue