test find-by-id query

This commit is contained in:
sbriat 2023-06-23 15:35:37 +02:00
parent 0409670eec
commit b7bb656f10
9 changed files with 165 additions and 70 deletions

View File

@ -48,30 +48,34 @@ npm run migrate
The app exposes the following [gRPC](https://grpc.io/) services :
- **FindByUuid** : find an ad by its uuid
- **FindById** : find an ad by its id
```json
{
"uuid": "80126a61-d128-4f96-afdb-92e33c75a3e1"
"id": "80126a61-d128-4f96-afdb-92e33c75a3e1"
}
```
- **Create** : create an ad (note that uuid is optional, a uuid will be automatically attributed if it is not provided)
- **Create** : create an ad (note that id is optional, an id (as a uuid) will be automatically attributed if it is not provided)
Punctual driver ad :
```json
{
"userUuid": "80c9bb02-0931-4a1d-bea6-22d358992245",
"userId": "80c9bb02-0931-4a1d-bea6-22d358992245",
"driver": true,
"seatsDriver": 3,
"seatsProposed": 3,
"frequency": "PUNCTUAL",
"departure": "2023-01-15 09:00",
"addresses": [
"fromDate": "2023-01-15",
"toDate": "2023-01-15",
"schedule": {
"thu": "09:00"
},
"waypoints": [
{
"position": 0,
"lon": 48.68944505415954,
"lat": 6.176510296462267,
"lon": 48.689445,
"lat": 6.17651,
"houseNumber": "5",
"street": "Avenue Foch",
"locality": "Nancy",
@ -94,14 +98,18 @@ The app exposes the following [gRPC](https://grpc.io/) services :
```json
{
"userUuid": "80c9bb02-0931-4a1d-bea6-22d358992245",
"userId": "80c9bb02-0931-4a1d-bea6-22d358992245",
"driver": true,
"pasenger": true,
"seatsDriver": 3,
"seatsPassenger": 1,
"seatsProposed": 3,
"seatsRequested": 1,
"frequency": "PUNCTUAL",
"departure": "2023-01-15 09:00",
"addresses": [
"fromDate": "2023-01-15",
"toDate": "2023-01-15",
"schedule": {
"thu": "09:00"
},
"waypoints": [
{
"position": 0,
"lon": 48.68944505415954,
@ -139,11 +147,11 @@ The app exposes the following [gRPC](https://grpc.io/) services :
"tue": "07:05",
"fri": "07:10"
},
"addresses": [
"waypoints": [
{
"position": 0,
"lon": 48.68944505415954,
"lat": 6.176510296462267,
"lon": 48.689445,
"lat": 6.17651,
"houseNumber": "5",
"street": "Avenue Foch",
"locality": "Nancy",
@ -164,15 +172,14 @@ The app exposes the following [gRPC](https://grpc.io/) services :
The list of possible options when creating an ad :
- uuid (optional): the uuid of the ad
- userUuid: the user uuid
- id (optional): the id of the ad (as a uuid)
- userId: the user id (as a uuid)
- driver (boolean, optional): if the ad is a driver ad
- passenger (boolean, optional): if the ad is a passenger ad
- frequency: `PUNCTUAL` or `RECURRENT`
- departure (required if punctual): departure date and hour/minute for a punctual ad
- fromDate (required if recurrent): start date for recurrent ad
- toDate (required if recurrent): end date for recurrent ad
- schedule (required if recurrent): an object with the departure time for each carpooled day in the week
- fromDate: start date for recurrent ad, carpool date for punctual ad
- toDate: end date for recurrent ad, same as fromDate for punctual ad
- schedule: an object with the departure time for each carpooled day in the week
- marginDurations (optional): an object with the margin duration (in seconds) for each carpooled day in the week, eg:
{
@ -181,10 +188,10 @@ The app exposes the following [gRPC](https://grpc.io/) services :
"fri": 950
}
- seatsDriver (optional): number of seats proposed as driver;
- seatsPassenger (optional): number of seats requested as passenger;
- seatsProposed (optional): number of seats proposed as driver
- seatsRequested (optional): number of seats requested as passenger
- strict (boolean, optional): if set to true, allow matching only with similar frequency ads
- addresses: an array of adresses that represent the waypoints of the journey (only first and last waypoints are used for passenger ads)
- 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 **must** be consecutives
Default values must be set in `.env` file.

View File

@ -2,8 +2,6 @@ import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
// import { HealthModule } from './modules/health/health.module';
import { AdModule } from './modules/ad/ad.module';
import { AutomapperModule } from '@automapper/nestjs';
import { classes } from '@automapper/classes';
import {
MessageBrokerModule,
MessageBrokerModuleOptions,
@ -21,7 +19,6 @@ import { HealthModule } from '@modules/health/health.module';
ConfigModule.forRoot({ isGlobal: true }),
EventEmitterModule.forRoot(),
RequestContextModule,
AutomapperModule.forRoot({ strategyInitializer: classes() }),
MessageBrokerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],

View File

@ -1,33 +1,26 @@
import { AutoMap } from '@automapper/classes';
import { IsOptional, IsString } from 'class-validator';
import { CoordinatesDto as CoordinatesDto } from './coordinates.dto';
export class AddressDto extends CoordinatesDto {
@IsOptional()
@AutoMap()
name?: string;
@IsOptional()
@IsString()
@AutoMap()
houseNumber?: string;
@IsOptional()
@IsString()
@AutoMap()
street?: string;
@IsOptional()
@IsString()
@AutoMap()
locality?: string;
@IsOptional()
@IsString()
@AutoMap()
postalCode?: string;
@IsString()
@AutoMap()
country: string;
}

View File

@ -1,4 +1,3 @@
import { AutoMap } from '@automapper/classes';
import { Transform } from 'class-transformer';
import { IsLatitude, IsLongitude } from 'class-validator';
import { toPrecision } from './transformers/to-precision';
@ -8,13 +7,11 @@ export class CoordinatesDto {
toClassOnly: true,
})
@IsLongitude()
@AutoMap()
lon: number;
@Transform(({ value }) => toPrecision(value, 6), {
toClassOnly: true,
})
@IsLatitude()
@AutoMap()
lat: number;
}

View File

@ -1,4 +1,3 @@
import { AutoMap } from '@automapper/classes';
import {
IsOptional,
IsBoolean,
@ -21,65 +20,54 @@ import { Frequency } from '@modules/ad/core/ad.types';
export class CreateAdRequestDto {
@IsUUID(4)
@AutoMap()
userId: string;
@IsOptional()
@IsBoolean()
@AutoMap()
driver?: boolean;
@IsOptional()
@IsBoolean()
@AutoMap()
passenger?: boolean;
@Transform(({ value }) => intToFrequency(value), {
toClassOnly: true,
})
@IsEnum(Frequency)
@AutoMap()
frequency: Frequency;
@IsISO8601({
strict: true,
strictSeparator: true,
})
@AutoMap()
fromDate: string;
@IsISO8601({
strict: true,
strictSeparator: true,
})
@AutoMap()
toDate: string;
@Type(() => ScheduleDto)
@IsSchedule()
@ValidateNested({ each: true })
@AutoMap()
schedule: ScheduleDto;
@IsOptional()
@Type(() => MarginDurationsDto)
@ValidateNested({ each: true })
@AutoMap()
marginDurations?: MarginDurationsDto;
@IsOptional()
@IsInt()
@AutoMap()
seatsProposed?: number;
@IsOptional()
@IsInt()
@AutoMap()
seatsRequested?: number;
@IsOptional()
@IsBoolean()
@AutoMap()
strict?: boolean;
@Type(() => WaypointDto)
@ -87,6 +75,5 @@ export class CreateAdRequestDto {
@ArrayMinSize(2)
@HasValidPositionIndexes()
@ValidateNested({ each: true })
@AutoMap()
waypoints: WaypointDto[];
}

View File

@ -1,39 +1,31 @@
import { AutoMap } from '@automapper/classes';
import { IsInt, IsOptional } from 'class-validator';
export class MarginDurationsDto {
@IsOptional()
@IsInt()
@AutoMap()
mon?: number;
@IsOptional()
@IsInt()
@AutoMap()
tue?: number;
@IsOptional()
@IsInt()
@AutoMap()
wed?: number;
@IsOptional()
@IsInt()
@AutoMap()
thu?: number;
@IsOptional()
@IsInt()
@AutoMap()
fri?: number;
@IsOptional()
@IsInt()
@AutoMap()
sat?: number;
@IsOptional()
@IsInt()
@AutoMap()
sun?: number;
}

View File

@ -1,39 +1,31 @@
import { AutoMap } from '@automapper/classes';
import { IsOptional, IsMilitaryTime } from 'class-validator';
export class ScheduleDto {
@IsOptional()
@IsMilitaryTime()
@AutoMap()
mon?: string;
@IsOptional()
@IsMilitaryTime()
@AutoMap()
tue?: string;
@IsOptional()
@IsMilitaryTime()
@AutoMap()
wed?: string;
@IsOptional()
@IsMilitaryTime()
@AutoMap()
thu?: string;
@IsOptional()
@IsMilitaryTime()
@AutoMap()
fri?: string;
@IsOptional()
@IsMilitaryTime()
@AutoMap()
sat?: string;
@IsOptional()
@IsMilitaryTime()
@AutoMap()
sun?: string;
}

View File

@ -1,10 +1,8 @@
import { AutoMap } from '@automapper/classes';
import { IsInt, IsOptional } from 'class-validator';
import { AddressDto } from './address.dto';
export class WaypointDto extends AddressDto {
@IsOptional()
@IsInt()
@AutoMap()
position?: number;
}

View File

@ -0,0 +1,132 @@
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
import { AdEntity } from '@modules/ad/core/ad.entity';
import {
CreateAdProps,
DefaultAdProps,
Frequency,
} from '@modules/ad/core/ad.types';
import { FindAdByIdQuery } from '@modules/ad/core/queries/find-ad-by-id/find-ad-by-id.query';
import { FindAdByIdQueryHandler } from '@modules/ad/core/queries/find-ad-by-id/find-ad-by-id.query-handler';
import { MarginDurationsProps } from '@modules/ad/core/value-objects/margin-durations.value-object';
import { WaypointProps } from '@modules/ad/core/value-objects/waypoint.value-object';
import { Test, TestingModule } from '@nestjs/testing';
const originWaypointProps: WaypointProps = {
position: 0,
address: {
houseNumber: '5',
street: 'Avenue Foch',
locality: 'Nancy',
postalCode: '54000',
country: 'France',
coordinates: {
lon: 48.68944505415954,
lat: 6.176510296462267,
},
},
};
const destinationWaypointProps: WaypointProps = {
position: 1,
address: {
locality: 'Paris',
postalCode: '75000',
country: 'France',
coordinates: {
lon: 48.8566,
lat: 2.3522,
},
},
};
const marginDurationsProps: MarginDurationsProps = {
mon: 600,
tue: 600,
wed: 600,
thu: 600,
fri: 600,
sat: 600,
sun: 600,
};
const baseCreateAdProps = {
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
seatsProposed: 3,
seatsRequested: 1,
strict: false,
waypoints: [originWaypointProps, destinationWaypointProps],
};
const punctualCreateAdProps = {
fromDate: '2023-06-22',
toDate: '2023-06-22',
schedule: {
wed: '08:30',
},
frequency: Frequency.PUNCTUAL,
};
const punctualPassengerCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: false,
passenger: true,
};
const defaultAdProps: DefaultAdProps = {
driver: false,
passenger: true,
marginDurations: {
mon: 900,
tue: 900,
wed: 900,
thu: 900,
fri: 900,
sat: 900,
sun: 900,
},
seatsProposed: 3,
seatsRequested: 1,
strict: false,
};
const ad: AdEntity = AdEntity.create(
punctualPassengerCreateAdProps,
defaultAdProps,
);
const mockAdRepository = {
findOneById: jest.fn().mockImplementation(() => ad),
};
describe('find-ad-by-id.query-handler', () => {
let findAdByIdQueryHandler: FindAdByIdQueryHandler;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: AD_REPOSITORY,
useValue: mockAdRepository,
},
FindAdByIdQueryHandler,
],
}).compile();
findAdByIdQueryHandler = module.get<FindAdByIdQueryHandler>(
FindAdByIdQueryHandler,
);
});
it('should be defined', () => {
expect(findAdByIdQueryHandler).toBeDefined();
});
describe('execution', () => {
it('should return an ad', async () => {
const findAdbyIdQuery = new FindAdByIdQuery(
'dd264806-13b4-4226-9b18-87adf0ad5dd1',
);
const ad: AdEntity = await findAdByIdQueryHandler.execute(
findAdbyIdQuery,
);
expect(ad.getProps().fromDate).toBe('2023-06-22');
});
});
});