divide admodel to support different read-write models

This commit is contained in:
sbriat 2023-06-21 16:19:41 +02:00
parent 1989ff6e67
commit 65f9ed4172
9 changed files with 853 additions and 740 deletions

View File

@ -12,20 +12,22 @@ import { ConflictException, DatabaseErrorException } from '@libs/exceptions';
export abstract class PrismaRepositoryBase< export abstract class PrismaRepositoryBase<
Aggregate extends AggregateRoot<any>, Aggregate extends AggregateRoot<any>,
DbModel extends ObjectLiteral, DbReadModel extends ObjectLiteral,
DbWriteModel extends ObjectLiteral,
> implements RepositoryPort<Aggregate> > implements RepositoryPort<Aggregate>
{ {
protected constructor( protected constructor(
protected readonly prisma: PrismaRepositoryPort<Aggregate> | any, protected readonly prisma: PrismaRepositoryPort<Aggregate> | any,
protected readonly prismaRaw: PrismaRawRepositoryPort, protected readonly prismaRaw: PrismaRawRepositoryPort,
protected readonly mapper: Mapper<Aggregate, DbModel>, protected readonly mapper: Mapper<Aggregate, DbReadModel, DbWriteModel>,
protected readonly eventEmitter: EventEmitter2, protected readonly eventEmitter: EventEmitter2,
protected readonly logger: LoggerPort, protected readonly logger: LoggerPort,
) {} ) {}
async findOneById(id: string): Promise<Option<Aggregate>> { async findOneById(id: string, include?: any): Promise<Option<Aggregate>> {
const entity = await this.prisma.findUnique({ const entity = await this.prisma.findUnique({
where: { uuid: id }, where: { uuid: id },
include,
}); });
return entity ? Some(this.mapper.toDomain(entity)) : None; return entity ? Some(this.mapper.toDomain(entity)) : None;
} }
@ -47,7 +49,7 @@ export abstract class PrismaRepositoryBase<
async healthCheck(): Promise<boolean> { async healthCheck(): Promise<boolean> {
try { try {
await this.prismaRaw.$queryRaw`SELECT 1`; await this.prisma.$queryRaw`SELECT 1`;
return true; return true;
} catch (e) { } catch (e) {
console.log(e); console.log(e);

View File

@ -2,10 +2,11 @@ import { Entity } from './entity.base';
export interface Mapper< export interface Mapper<
DomainEntity extends Entity<any>, DomainEntity extends Entity<any>,
DbRecord, DbReadRecord,
DbWriteRecord,
Response = any, Response = any,
> { > {
toPersistence(entity: DomainEntity): DbRecord; toPersistence(entity: DomainEntity): DbWriteRecord;
toDomain(record: any): DomainEntity; toDomain(record: DbReadRecord): DomainEntity;
toResponse(entity: DomainEntity): Response; toResponse(entity: DomainEntity): Response;
} }

View File

@ -2,7 +2,11 @@ import { Mapper } from '@libs/ddd';
import { AdResponseDto } from './interface/dtos/ad.response.dto'; import { AdResponseDto } from './interface/dtos/ad.response.dto';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { AdEntity } from './core/ad.entity'; import { AdEntity } from './core/ad.entity';
import { AdModel, WaypointModel } from './infrastructure/ad.repository'; import {
AdWriteModel,
AdReadModel,
WaypointModel,
} from './infrastructure/ad.repository';
import { Frequency } from './core/ad.types'; import { Frequency } from './core/ad.types';
import { WaypointProps } from './core/value-objects/waypoint.value-object'; import { WaypointProps } from './core/value-objects/waypoint.value-object';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
@ -21,7 +25,9 @@ import { DateTime, TimeZone } from 'timezonecomplete';
*/ */
@Injectable() @Injectable()
export class AdMapper implements Mapper<AdEntity, AdModel, AdResponseDto> { export class AdMapper
implements Mapper<AdEntity, AdReadModel, AdWriteModel, AdResponseDto>
{
private timezone: string; private timezone: string;
private readonly defaultParams: DefaultParams; private readonly defaultParams: DefaultParams;
@ -34,11 +40,11 @@ export class AdMapper implements Mapper<AdEntity, AdModel, AdResponseDto> {
this.defaultParams = defaultParamsProvider.getParams(); this.defaultParams = defaultParamsProvider.getParams();
} }
toPersistence = (entity: AdEntity): AdModel => { toPersistence = (entity: AdEntity): AdWriteModel => {
const copy = entity.getProps(); const copy = entity.getProps();
const timezone = this.getTimezone(copy.waypoints[0].address.coordinates); const timezone = this.getTimezone(copy.waypoints[0].address.coordinates);
const now = new Date(); const now = new Date();
const record: AdModel = { const record: AdWriteModel = {
uuid: copy.id, uuid: copy.id,
userUuid: copy.userId, userUuid: copy.userId,
driver: copy.driver, driver: copy.driver,
@ -127,7 +133,7 @@ export class AdMapper implements Mapper<AdEntity, AdModel, AdResponseDto> {
return record; return record;
}; };
toDomain = (record: AdModel): AdEntity => { toDomain = (record: AdReadModel): AdEntity => {
const entity = new AdEntity({ const entity = new AdEntity({
id: record.uuid, id: record.uuid,
createdAt: new Date(record.createdAt), createdAt: new Date(record.createdAt),
@ -140,13 +146,13 @@ export class AdMapper implements Mapper<AdEntity, AdModel, AdResponseDto> {
fromDate: record.fromDate.toISOString(), fromDate: record.fromDate.toISOString(),
toDate: record.toDate.toISOString(), toDate: record.toDate.toISOString(),
schedule: { schedule: {
mon: record.monTime.toISOString(), mon: record.monTime?.toISOString(),
tue: record.tueTime.toISOString(), tue: record.tueTime?.toISOString(),
wed: record.wedTime.toISOString(), wed: record.wedTime?.toISOString(),
thu: record.thuTime.toISOString(), thu: record.thuTime?.toISOString(),
fri: record.friTime.toISOString(), fri: record.friTime?.toISOString(),
sat: record.satTime.toISOString(), sat: record.satTime?.toISOString(),
sun: record.sunTime.toISOString(), sun: record.sunTime?.toISOString(),
}, },
marginDurations: { marginDurations: {
mon: record.monMargin, mon: record.monMargin,
@ -160,7 +166,7 @@ export class AdMapper implements Mapper<AdEntity, AdModel, AdResponseDto> {
seatsProposed: record.seatsProposed, seatsProposed: record.seatsProposed,
seatsRequested: record.seatsRequested, seatsRequested: record.seatsRequested,
strict: record.strict, strict: record.strict,
waypoints: record.waypoints.create.map((waypoint: WaypointModel) => ({ waypoints: record.waypoints.map((waypoint: WaypointModel) => ({
position: waypoint.position, position: waypoint.position,
address: { address: {
name: waypoint.name, name: waypoint.name,

View File

@ -6,7 +6,7 @@ import { PrismaService } from '@libs/db/prisma.service';
import { AdMapper } from '../ad.mapper'; import { AdMapper } from '../ad.mapper';
import { PrismaRepositoryBase } from '@libs/db/prisma-repository.base'; import { PrismaRepositoryBase } from '@libs/db/prisma-repository.base';
export type AdModel = { export type AdBaseModel = {
uuid: string; uuid: string;
userUuid: string; userUuid: string;
driver: boolean; driver: boolean;
@ -31,11 +31,18 @@ export type AdModel = {
seatsProposed: number; seatsProposed: number;
seatsRequested: number; seatsRequested: number;
strict: boolean; strict: boolean;
createdAt: Date;
updatedAt: Date;
};
export type AdReadModel = AdBaseModel & {
waypoints: WaypointModel[];
};
export type AdWriteModel = AdBaseModel & {
waypoints: { waypoints: {
create: WaypointModel[]; create: WaypointModel[];
}; };
createdAt: Date;
updatedAt: Date;
}; };
export type WaypointModel = { export type WaypointModel = {
@ -58,7 +65,7 @@ export type WaypointModel = {
* */ * */
@Injectable() @Injectable()
export class AdRepository export class AdRepository
extends PrismaRepositoryBase<AdEntity, AdModel> extends PrismaRepositoryBase<AdEntity, AdReadModel, AdWriteModel>
implements AdRepositoryPort implements AdRepositoryPort
{ {
constructor( constructor(

View File

@ -1,295 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { CreateAdUseCase } from '../../../core/usecases/create-ad.usecase';
import { CreateAdRequestDTO } from '../../../interface/commands/create-ad.request.dto';
import { AdsRepository } from '../../../adapters/secondaries/ads.repository';
import { CreateAdCommand } from '../../../interface/commands/create-ad.command';
import { AutomapperModule } from '@automapper/nestjs';
import { classes } from '@automapper/classes';
import { Frequency } from '../../../interface/commands/frequency.enum';
import { Ad } from '../../../core/entities/ad';
import { AdProfile } from '../../../mappers/ad.profile';
import { Waypoint } from '../../../interface/commands/waypoint';
import { AdDTO } from '../../../core/dtos/ad.dto';
import { PARAMS_PROVIDER } from '../../../ad.di-tokens';
import { MESSAGE_PUBLISHER } from '../../../../../app.constants';
const originWaypoint: Waypoint = {
position: 0,
lon: 48.68944505415954,
lat: 6.176510296462267,
houseNumber: '5',
street: 'Avenue Foch',
locality: 'Nancy',
postalCode: '54000',
country: 'France',
};
const destinationWaypoint: Waypoint = {
position: 1,
lon: 48.8566,
lat: 2.3522,
locality: 'Paris',
postalCode: '75000',
country: 'France',
};
const originWaypointWithoutPosition: Waypoint = {
lon: 43.2965,
lat: 5.3698,
locality: 'Marseille',
postalCode: '13000',
country: 'France',
};
const destinationWaypointWithoutPosition: Waypoint = {
lon: 43.7102,
lat: 7.262,
locality: 'Nice',
postalCode: '06000',
country: 'France',
};
const newAdRequest: CreateAdRequestDTO = {
userUuid: '113e0000-0000-4000-a000-000000000000',
driver: true,
passenger: false,
frequency: Frequency.RECURRENT,
fromDate: new Date('2023-05-01'),
toDate: new Date('2023-08-20'),
schedule: {
tue: '08:00',
wed: '08:30',
},
marginDurations: {
mon: undefined,
tue: undefined,
wed: undefined,
thu: undefined,
fri: undefined,
sat: undefined,
sun: undefined,
},
seatsProposed: 2,
waypoints: [originWaypoint, destinationWaypoint],
};
const mockMessagePublisher = {
publish: jest.fn().mockImplementation(),
};
const mockDefaultParamsProvider = {
getParams: () => {
return {
MON_MARGIN: 900,
TUE_MARGIN: 900,
WED_MARGIN: 900,
THU_MARGIN: 900,
FRI_MARGIN: 900,
SAT_MARGIN: 900,
SUN_MARGIN: 900,
DRIVER: false,
SEATS_PROPOSED: 3,
PASSENGER: true,
SEATS_REQUESTED: 1,
STRICT: false,
};
},
};
const mockAdRepository = {
create: jest
.fn()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.mockImplementationOnce((command?: CreateAdCommand) => {
return Promise.resolve({
...newAdRequest,
uuid: 'ad000000-0000-4000-a000-000000000000',
createdAt: new Date('2023-05-01'),
});
})
.mockImplementationOnce(() => {
throw new Error('Already exists');
})
.mockImplementation(),
};
describe('CreateAdUseCase', () => {
let createAdUseCase: CreateAdUseCase;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
providers: [
{
provide: AdsRepository,
useValue: mockAdRepository,
},
{
provide: MESSAGE_PUBLISHER,
useValue: mockMessagePublisher,
},
CreateAdUseCase,
AdProfile,
{
provide: PARAMS_PROVIDER,
useValue: mockDefaultParamsProvider,
},
],
}).compile();
createAdUseCase = module.get<CreateAdUseCase>(CreateAdUseCase);
});
it('should be defined', () => {
expect(createAdUseCase).toBeDefined();
});
describe('execution', () => {
const newAdCommand = new CreateAdCommand(newAdRequest);
it('should create a new ad', async () => {
const newAd: Ad = await createAdUseCase.execute(newAdCommand);
expect(newAd.userUuid).toBe('113e0000-0000-4000-a000-000000000000');
expect(newAd.uuid).toBe('ad000000-0000-4000-a000-000000000000');
});
it('should throw an error if the ad already exists', async () => {
await expect(
createAdUseCase.execute(newAdCommand),
).rejects.toBeInstanceOf(Error);
});
});
describe('Ad parameter default setting ', () => {
beforeEach(() => {
mockAdRepository.create.mockClear();
});
it('should define minimal recurrent ad as passenger', async () => {
const minimalRecurrentAdRequest: CreateAdRequestDTO = {
userUuid: '224e0000-0000-4000-a000-000000000000',
frequency: Frequency.RECURRENT,
fromDate: new Date('2023-05-01'),
toDate: new Date('2024-05-01'),
schedule: {
mon: '08:00',
},
waypoints: [originWaypoint, destinationWaypoint],
};
const newAdCommand = new CreateAdCommand(minimalRecurrentAdRequest);
const expectedAdCreation: AdDTO = {
userUuid: '224e0000-0000-4000-a000-000000000000',
frequency: Frequency.RECURRENT,
fromDate: new Date('2023-05-01'),
toDate: new Date('2024-05-01'),
monTime: '08:00',
tueTime: undefined,
wedTime: undefined,
thuTime: undefined,
friTime: undefined,
satTime: undefined,
sunTime: undefined,
monMargin: 900,
tueMargin: 900,
wedMargin: 900,
thuMargin: 900,
friMargin: 900,
satMargin: 900,
sunMargin: 900,
driver: false,
seatsProposed: 3,
passenger: true,
seatsRequested: 1,
strict: false,
waypoints: {
create: [
{
uuid: undefined,
adUuid: undefined,
position: 0,
lon: 48.68944505415954,
lat: 6.176510296462267,
houseNumber: '5',
street: 'Avenue Foch',
locality: 'Nancy',
postalCode: '54000',
country: 'France',
},
{
uuid: undefined,
adUuid: undefined,
position: 1,
lon: 48.8566,
lat: 2.3522,
locality: 'Paris',
postalCode: '75000',
country: 'France',
},
],
},
createdAt: undefined,
};
await createAdUseCase.execute(newAdCommand);
expect(mockAdRepository.create).toBeCalledWith(expectedAdCreation, {
waypoints: true,
});
});
it('should create a passenger ad with waypoints without position', async () => {
const newPunctualPassengerAdRequest: CreateAdRequestDTO = {
userUuid: '113e0000-0000-4000-a000-000000000000',
passenger: true,
frequency: Frequency.PUNCTUAL,
departureDate: new Date('2023-05-22 09:36'),
seatsRequested: 1,
waypoints: [
originWaypointWithoutPosition,
destinationWaypointWithoutPosition,
],
schedule: {},
};
const newAdCommand = new CreateAdCommand(newPunctualPassengerAdRequest);
const expectedAdCreation: AdDTO = {
userUuid: '113e0000-0000-4000-a000-000000000000',
frequency: Frequency.PUNCTUAL,
fromDate: new Date('2023-05-22'),
toDate: new Date('2023-05-22'),
monTime: '09:36',
tueTime: undefined,
wedTime: undefined,
thuTime: undefined,
friTime: undefined,
satTime: undefined,
sunTime: undefined,
monMargin: 900,
tueMargin: 900,
wedMargin: 900,
thuMargin: 900,
friMargin: 900,
satMargin: 900,
sunMargin: 900,
driver: false,
seatsProposed: 3,
passenger: true,
seatsRequested: 1,
strict: false,
waypoints: {
create: [
{
uuid: undefined,
adUuid: undefined,
position: 0,
lon: 43.2965,
lat: 5.3698,
locality: 'Marseille',
postalCode: '13000',
country: 'France',
},
{
uuid: undefined,
adUuid: undefined,
position: 1,
lon: 43.7102,
lat: 7.262,
locality: 'Nice',
postalCode: '06000',
country: 'France',
},
],
},
createdAt: undefined,
};
await createAdUseCase.execute(newAdCommand);
expect(mockAdRepository.create).toBeCalledWith(expectedAdCreation, {
waypoints: true,
});
});
});
});

View File

@ -1,71 +0,0 @@
import { NotFoundException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { FindAdByUuidQuery } from '../../../queries/find-ad-by-uuid.query';
import { AdsRepository } from '../../../adapters/secondaries/ads.repository';
import { FindAdByIdUseCase } from '../../../core/usecases/find-ad-by-uuid.usecase';
import { FindAdByIdRequestDTO } from '../../../interface/queries/find-ad-by-id/dtos/find-ad-by-id.request.dto';
import { MESSAGE_PUBLISHER } from '../../../../../app.constants';
const mockAd = {
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
};
const mockAdRepository = {
findOneByUuid: jest
.fn()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.mockImplementationOnce((query?: FindAdByUuidQuery) => {
return Promise.resolve(mockAd);
})
.mockImplementation(() => {
return Promise.resolve(null);
}),
};
const mockMessagePublisher = {
publish: jest.fn().mockImplementation(),
};
describe('FindAdByUuidUseCase', () => {
let findAdByUuidUseCase: FindAdByIdUseCase;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
providers: [
{
provide: AdsRepository,
useValue: mockAdRepository,
},
{
provide: MESSAGE_PUBLISHER,
useValue: mockMessagePublisher,
},
FindAdByIdUseCase,
],
}).compile();
findAdByUuidUseCase = module.get<FindAdByIdUseCase>(FindAdByIdUseCase);
});
it('should be defined', () => {
expect(findAdByUuidUseCase).toBeDefined();
});
describe('execute', () => {
it('should return an ad', async () => {
const findAdByUuidRequest: FindAdByIdRequestDTO =
new FindAdByIdRequestDTO();
findAdByUuidRequest.id = 'bb281075-1b98-4456-89d6-c643d3044a91';
const ad = await findAdByUuidUseCase.execute(
new FindAdByUuidQuery(findAdByUuidRequest),
);
expect(ad).toBe(mockAd);
});
it('should throw an error if ad does not exist', async () => {
const findAdByUuidRequest: FindAdByIdRequestDTO =
new FindAdByIdRequestDTO();
findAdByUuidRequest.id = 'bb281075-1b98-4456-89d6-c643d3044a90';
await expect(
findAdByUuidUseCase.execute(new FindAdByUuidQuery(findAdByUuidRequest)),
).rejects.toBeInstanceOf(NotFoundException);
});
});
});

View File

@ -1,14 +1,20 @@
import { PrismaService } from '@libs/db/prisma.service';
import {
AD_REPOSITORY,
PARAMS_PROVIDER,
TIMEZONE_FINDER,
} from '@modules/ad/ad.di-tokens';
import { AdMapper } from '@modules/ad/ad.mapper';
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
import { DefaultParamsProvider } from '@modules/ad/infrastructure/default-params-provider';
import { TimezoneFinder } from '@modules/ad/infrastructure/timezone-finder';
import { ConfigModule } from '@nestjs/config';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { PrismaService } from '../../../database/adapters/secondaries/prisma-service';
import { AdsRepository } from '../../adapters/secondaries/ads.repository';
import { DatabaseModule } from '../../../database/database.module';
import { Frequency } from '../../interface/commands/frequency.enum';
import { AdDTO } from '../../core/dtos/ad.dto';
import { Waypoint } from '../../core/entities/waypoint';
describe('Ad Repository', () => { describe('Ad Repository', () => {
let prismaService: PrismaService; let prismaService: PrismaService;
let adsRepository: AdsRepository; let adRepository: AdRepository;
const executeInsertCommand = async (table: string, object: any) => { const executeInsertCommand = async (table: string, object: any) => {
const command = `INSERT INTO ${table} ("${Object.keys(object).join( const command = `INSERT INTO ${table} ("${Object.keys(object).join(
@ -20,13 +26,14 @@ describe('Ad Repository', () => {
const getSeed = (index: number, uuid: string): string => { const getSeed = (index: number, uuid: string): string => {
return `'${uuid.slice(0, -2)}${index.toString(16).padStart(2, '0')}'`; return `'${uuid.slice(0, -2)}${index.toString(16).padStart(2, '0')}'`;
}; };
const baseUuid = { const baseUuid = {
uuid: 'be459a29-7a41-4c0b-b371-abe90bfb6f00', uuid: 'be459a29-7a41-4c0b-b371-abe90bfb6f00',
}; };
const baseAdress0Uuid = { const baseOriginWaypointUuid = {
uuid: 'bad5e786-3b15-4e51-a8fc-926fa9327ff1', uuid: 'bad5e786-3b15-4e51-a8fc-926fa9327ff1',
}; };
const baseAdress1Uuid = { const baseDestinationWaypointUuid = {
uuid: '4d200eb6-7389-487f-a1ca-dbc0e40381c9', uuid: '4d200eb6-7389-487f-a1ca-dbc0e40381c9',
}; };
const baseUserUuid = { const baseUserUuid = {
@ -35,24 +42,24 @@ describe('Ad Repository', () => {
const driverAd = { const driverAd = {
driver: 'true', driver: 'true',
passenger: 'false', passenger: 'false',
seatsDriver: 3, seatsProposed: 3,
seatsPassenger: 0, seatsRequested: 0,
strict: 'false',
};
const passengerAd = {
driver: 'false',
passenger: 'true',
seatsDriver: 0,
seatsPassenger: 1,
strict: 'false',
};
const driverAndPassengerAd = {
driver: 'true',
passenger: 'true',
seatsDriver: 3,
seatsPassenger: 1,
strict: 'false', strict: 'false',
}; };
// const passengerAd = {
// driver: 'false',
// passenger: 'true',
// seatsProposed: 0,
// seatsRequested: 1,
// strict: 'false',
// };
// const driverAndPassengerAd = {
// driver: 'true',
// passenger: 'true',
// seatsProposed: 3,
// seatsRequested: 1,
// strict: 'false',
// };
const punctualAd = { const punctualAd = {
frequency: `'PUNCTUAL'`, frequency: `'PUNCTUAL'`,
fromDate: `'2023-01-01'`, fromDate: `'2023-01-01'`,
@ -63,7 +70,7 @@ describe('Ad Repository', () => {
thuTime: 'NULL', thuTime: 'NULL',
friTime: 'NULL', friTime: 'NULL',
satTime: 'NULL', satTime: 'NULL',
sunTime: `'07:00'`, sunTime: `'2023-01-01T07:00:00Z'`,
monMargin: 900, monMargin: 900,
tueMargin: 900, tueMargin: 900,
wedMargin: 900, wedMargin: 900,
@ -72,27 +79,26 @@ describe('Ad Repository', () => {
satMargin: 900, satMargin: 900,
sunMargin: 900, sunMargin: 900,
}; };
const recurrentAd = { // const recurrentAd = {
frequency: `'RECURRENT'`, // frequency: `'RECURRENT'`,
fromDate: `'2023-01-01'`, // fromDate: `'2023-01-01'`,
toDate: `'2023-12-31'`, // toDate: `'2023-12-31'`,
monTime: `'07:00'`, // monTime: `'2023-01-01T07:15:00Z'`,
tueTime: `'07:00'`, // tueTime: `'2023-01-01T07:15:00Z'`,
wedTime: `'07:00'`, // wedTime: `'2023-01-01T07:05:00Z'`,
thuTime: `'07:00'`, // thuTime: `'2023-01-01T07:15:00Z'`,
friTime: `'07:00'`, // friTime: `'2023-01-01T07:15:00Z'`,
satTime: 'NULL', // satTime: 'NULL',
sunTime: 'NULL', // sunTime: 'NULL',
monMargin: 900, // monMargin: 900,
tueMargin: 900, // tueMargin: 900,
wedMargin: 900, // wedMargin: 900,
thuMargin: 900, // thuMargin: 900,
friMargin: 900, // friMargin: 900,
satMargin: 900, // satMargin: 900,
sunMargin: 900, // sunMargin: 900,
}; // };
const originWaypoint = {
const address0 = {
position: 0, position: 0,
lon: 43.7102, lon: 43.7102,
lat: 7.262, lat: 7.262,
@ -100,7 +106,7 @@ describe('Ad Repository', () => {
postalCode: "'06000'", postalCode: "'06000'",
country: "'France'", country: "'France'",
}; };
const address1 = { const destinationWaypoint = {
position: 1, position: 1,
lon: 43.2965, lon: 43.2965,
lat: 5.3698, lat: 5.3698,
@ -108,6 +114,7 @@ describe('Ad Repository', () => {
postalCode: "'13000'", postalCode: "'13000'",
country: "'France'", country: "'France'",
}; };
const createPunctualDriverAds = async (nbToCreate = 10) => { const createPunctualDriverAds = async (nbToCreate = 10) => {
const adToCreate = { const adToCreate = {
...baseUuid, ...baseUuid,
@ -118,139 +125,159 @@ describe('Ad Repository', () => {
for (let i = 0; i < nbToCreate; i++) { for (let i = 0; i < nbToCreate; i++) {
adToCreate.uuid = getSeed(i, baseUuid.uuid); adToCreate.uuid = getSeed(i, baseUuid.uuid);
await executeInsertCommand('ad', adToCreate); await executeInsertCommand('ad', adToCreate);
await executeInsertCommand('address', { await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress0Uuid.uuid), uuid: getSeed(i, baseOriginWaypointUuid.uuid),
adUuid: adToCreate.uuid, adUuid: adToCreate.uuid,
...address0, ...originWaypoint,
}); });
await executeInsertCommand('address', { await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress1Uuid.uuid), uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
adUuid: adToCreate.uuid, adUuid: adToCreate.uuid,
...address1, ...destinationWaypoint,
});
}
};
const createRecurrentDriverAds = async (nbToCreate = 10) => {
const adToCreate = {
...baseUuid,
...baseUserUuid,
...driverAd,
...punctualAd,
};
for (let i = 0; i < nbToCreate; i++) {
adToCreate.uuid = getSeed(i, baseUuid.uuid);
await executeInsertCommand('ad', adToCreate);
await executeInsertCommand('address', {
uuid: getSeed(i, baseAdress0Uuid.uuid),
adUuid: adToCreate.uuid,
...address0,
});
await executeInsertCommand('address', {
uuid: getSeed(i, baseAdress1Uuid.uuid),
adUuid: adToCreate.uuid,
...address1,
}); });
} }
}; };
const createPunctualPassengerAds = async (nbToCreate = 10) => { // const createRecurrentDriverAds = async (nbToCreate = 10) => {
const adToCreate = { // const adToCreate = {
...baseUuid, // ...baseUuid,
...baseUserUuid, // ...baseUserUuid,
...passengerAd, // ...driverAd,
...punctualAd, // ...punctualAd,
}; // };
for (let i = 0; i < nbToCreate; i++) { // for (let i = 0; i < nbToCreate; i++) {
adToCreate.uuid = getSeed(i, baseUuid.uuid); // adToCreate.uuid = getSeed(i, baseUuid.uuid);
await executeInsertCommand('ad', adToCreate); // await executeInsertCommand('ad', adToCreate);
await executeInsertCommand('address', { // await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress0Uuid.uuid), // uuid: getSeed(i, baseOriginWaypointUuid.uuid),
adUuid: adToCreate.uuid, // adUuid: adToCreate.uuid,
...address0, // ...originWaypoint,
}); // });
await executeInsertCommand('address', { // await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress1Uuid.uuid), // uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
adUuid: adToCreate.uuid, // adUuid: adToCreate.uuid,
...address1, // ...destinationWaypoint,
}); // });
} // }
}; // };
const createRecurrentPassengerAds = async (nbToCreate = 10) => { // const createPunctualPassengerAds = async (nbToCreate = 10) => {
const adToCreate = { // const adToCreate = {
...baseUuid, // ...baseUuid,
...baseUserUuid, // ...baseUserUuid,
...passengerAd, // ...passengerAd,
...recurrentAd, // ...punctualAd,
}; // };
for (let i = 0; i < nbToCreate; i++) { // for (let i = 0; i < nbToCreate; i++) {
adToCreate.uuid = getSeed(i, baseUuid.uuid); // adToCreate.uuid = getSeed(i, baseUuid.uuid);
await executeInsertCommand('ad', adToCreate); // await executeInsertCommand('ad', adToCreate);
await executeInsertCommand('address', { // await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress0Uuid.uuid), // uuid: getSeed(i, baseOriginWaypointUuid.uuid),
adUuid: adToCreate.uuid, // adUuid: adToCreate.uuid,
...address0, // ...originWaypoint,
}); // });
await executeInsertCommand('address', { // await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress1Uuid.uuid), // uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
adUuid: adToCreate.uuid, // adUuid: adToCreate.uuid,
...address1, // ...destinationWaypoint,
}); // });
} // }
}; // };
const createPunctualDriverPassengerAds = async (nbToCreate = 10) => { // const createRecurrentPassengerAds = async (nbToCreate = 10) => {
const adToCreate = { // const adToCreate = {
...baseUuid, // ...baseUuid,
...baseUserUuid, // ...baseUserUuid,
...driverAndPassengerAd, // ...passengerAd,
...punctualAd, // ...recurrentAd,
}; // };
for (let i = 0; i < nbToCreate; i++) { // for (let i = 0; i < nbToCreate; i++) {
adToCreate.uuid = getSeed(i, baseUuid.uuid); // adToCreate.uuid = getSeed(i, baseUuid.uuid);
await executeInsertCommand('ad', adToCreate); // await executeInsertCommand('ad', adToCreate);
await executeInsertCommand('address', { // await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress0Uuid.uuid), // uuid: getSeed(i, baseOriginWaypointUuid.uuid),
adUuid: adToCreate.uuid, // adUuid: adToCreate.uuid,
...address0, // ...originWaypoint,
}); // });
await executeInsertCommand('address', { // await executeInsertCommand('waypoint', {
uuid: getSeed(i, baseAdress1Uuid.uuid), // uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
adUuid: adToCreate.uuid, // adUuid: adToCreate.uuid,
...address1, // ...destinationWaypoint,
}); // });
} // }
}; // };
// const createPunctualDriverPassengerAds = async (nbToCreate = 10) => {
// const adToCreate = {
// ...baseUuid,
// ...baseUserUuid,
// ...driverAndPassengerAd,
// ...punctualAd,
// };
// for (let i = 0; i < nbToCreate; i++) {
// adToCreate.uuid = getSeed(i, baseUuid.uuid);
// await executeInsertCommand('ad', adToCreate);
// await executeInsertCommand('waypoint', {
// uuid: getSeed(i, baseOriginWaypointUuid.uuid),
// adUuid: adToCreate.uuid,
// ...originWaypoint,
// });
// await executeInsertCommand('waypoint', {
// uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
// adUuid: adToCreate.uuid,
// ...destinationWaypoint,
// });
// }
// };
// const createRecurrentDriverPassengerAds = async (nbToCreate = 10) => {
// const adToCreate = {
// ...baseUuid,
// ...baseUserUuid,
// ...driverAndPassengerAd,
// ...recurrentAd,
// };
// for (let i = 0; i < nbToCreate; i++) {
// adToCreate.uuid = getSeed(i, baseUuid.uuid);
// await executeInsertCommand('ad', adToCreate);
// await executeInsertCommand('waypoint', {
// uuid: getSeed(i, baseOriginWaypointUuid.uuid),
// adUuid: adToCreate.uuid,
// ...originWaypoint,
// });
// await executeInsertCommand('waypoint', {
// uuid: getSeed(i, baseDestinationWaypointUuid.uuid),
// adUuid: adToCreate.uuid,
// ...destinationWaypoint,
// });
// }
// };
const createRecurrentDriverPassengerAds = async (nbToCreate = 10) => {
const adToCreate = {
...baseUuid,
...baseUserUuid,
...driverAndPassengerAd,
...recurrentAd,
};
for (let i = 0; i < nbToCreate; i++) {
adToCreate.uuid = getSeed(i, baseUuid.uuid);
await executeInsertCommand('ad', adToCreate);
await executeInsertCommand('address', {
uuid: getSeed(i, baseAdress0Uuid.uuid),
adUuid: adToCreate.uuid,
...address0,
});
await executeInsertCommand('address', {
uuid: getSeed(i, baseAdress1Uuid.uuid),
adUuid: adToCreate.uuid,
...address1,
});
}
};
beforeAll(async () => { beforeAll(async () => {
const module = await Test.createTestingModule({ const module = await Test.createTestingModule({
imports: [DatabaseModule], imports: [
providers: [PrismaService, AdsRepository], EventEmitterModule.forRoot(),
ConfigModule.forRoot({ isGlobal: true }),
],
providers: [
PrismaService,
AdMapper,
{
provide: AD_REPOSITORY,
useClass: AdRepository,
},
{
provide: PARAMS_PROVIDER,
useClass: DefaultParamsProvider,
},
{
provide: TIMEZONE_FINDER,
useClass: TimezoneFinder,
},
],
}).compile(); }).compile();
prismaService = module.get<PrismaService>(PrismaService); prismaService = module.get<PrismaService>(PrismaService);
adsRepository = module.get<AdsRepository>(AdsRepository); adRepository = module.get<AdRepository>(AD_REPOSITORY);
}); });
afterAll(async () => { afterAll(async () => {
await prismaService.$disconnect(); await prismaService.$disconnect();
@ -259,210 +286,212 @@ describe('Ad Repository', () => {
beforeEach(async () => { beforeEach(async () => {
await prismaService.ad.deleteMany(); await prismaService.ad.deleteMany();
}); });
describe('findAll', () => { // describe('findAll', () => {
it('should return an empty data array', async () => { // it('should return an empty data array', async () => {
const res = await adsRepository.findAll(); // const res = await adRepository.findAll();
expect(res).toEqual({ // expect(res).toEqual({
data: [], // data: [],
total: 0, // total: 0,
}); // });
}); // });
describe('drivers', () => { // describe('drivers', () => {
it('should return a data array with 8 punctual driver ads', async () => { // it('should return a data array with 8 punctual driver ads', async () => {
await createPunctualDriverAds(8); // await createPunctualDriverAds(8);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(8); // expect(ads.data.length).toBe(8);
expect(ads.total).toBe(8); // expect(ads.total).toBe(8);
expect(ads.data[0].driver).toBeTruthy(); // expect(ads.data[0].driver).toBeTruthy();
expect(ads.data[0].passenger).toBeFalsy(); // expect(ads.data[0].passenger).toBeFalsy();
}); // });
it('should return a data array limited to 10 punctual driver ads', async () => { // it('should return a data array limited to 10 punctual driver ads', async () => {
await createPunctualDriverAds(20); // await createPunctualDriverAds(20);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(10); // expect(ads.data.length).toBe(10);
expect(ads.total).toBe(20); // expect(ads.total).toBe(20);
expect(ads.data[1].driver).toBeTruthy(); // expect(ads.data[1].driver).toBeTruthy();
expect(ads.data[1].passenger).toBeFalsy(); // expect(ads.data[1].passenger).toBeFalsy();
}); // });
it('should return a data array with 8 recurrent driver ads', async () => { // it('should return a data array with 8 recurrent driver ads', async () => {
await createRecurrentDriverAds(8); // await createRecurrentDriverAds(8);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(8); // expect(ads.data.length).toBe(8);
expect(ads.total).toBe(8); // expect(ads.total).toBe(8);
expect(ads.data[2].driver).toBeTruthy(); // expect(ads.data[2].driver).toBeTruthy();
expect(ads.data[2].passenger).toBeFalsy(); // expect(ads.data[2].passenger).toBeFalsy();
}); // });
it('should return a data array limited to 10 recurrent driver ads', async () => { // it('should return a data array limited to 10 recurrent driver ads', async () => {
await createRecurrentDriverAds(20); // await createRecurrentDriverAds(20);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(10); // expect(ads.data.length).toBe(10);
expect(ads.total).toBe(20); // expect(ads.total).toBe(20);
expect(ads.data[3].driver).toBeTruthy(); // expect(ads.data[3].driver).toBeTruthy();
expect(ads.data[3].passenger).toBeFalsy(); // expect(ads.data[3].passenger).toBeFalsy();
}); // });
}); // });
describe('passengers', () => { // describe('passengers', () => {
it('should return a data array with 7 punctual passenger ads', async () => { // it('should return a data array with 7 punctual passenger ads', async () => {
await createPunctualPassengerAds(7); // await createPunctualPassengerAds(7);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(7); // expect(ads.data.length).toBe(7);
expect(ads.total).toBe(7); // expect(ads.total).toBe(7);
expect(ads.data[0].passenger).toBeTruthy(); // expect(ads.data[0].passenger).toBeTruthy();
expect(ads.data[0].driver).toBeFalsy(); // expect(ads.data[0].driver).toBeFalsy();
}); // });
it('should return a data array limited to 10 punctual passenger ads', async () => { // it('should return a data array limited to 10 punctual passenger ads', async () => {
await createPunctualPassengerAds(15); // await createPunctualPassengerAds(15);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(10); // expect(ads.data.length).toBe(10);
expect(ads.total).toBe(15); // expect(ads.total).toBe(15);
expect(ads.data[1].passenger).toBeTruthy(); // expect(ads.data[1].passenger).toBeTruthy();
expect(ads.data[1].driver).toBeFalsy(); // expect(ads.data[1].driver).toBeFalsy();
}); // });
it('should return a data array with 7 recurrent passenger ads', async () => { // it('should return a data array with 7 recurrent passenger ads', async () => {
await createRecurrentPassengerAds(7); // await createRecurrentPassengerAds(7);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(7); // expect(ads.data.length).toBe(7);
expect(ads.total).toBe(7); // expect(ads.total).toBe(7);
expect(ads.data[2].passenger).toBeTruthy(); // expect(ads.data[2].passenger).toBeTruthy();
expect(ads.data[2].driver).toBeFalsy(); // expect(ads.data[2].driver).toBeFalsy();
}); // });
it('should return a data array limited to 10 recurrent passenger ads', async () => { // it('should return a data array limited to 10 recurrent passenger ads', async () => {
await createRecurrentPassengerAds(15); // await createRecurrentPassengerAds(15);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(10); // expect(ads.data.length).toBe(10);
expect(ads.total).toBe(15); // expect(ads.total).toBe(15);
expect(ads.data[3].passenger).toBeTruthy(); // expect(ads.data[3].passenger).toBeTruthy();
expect(ads.data[3].driver).toBeFalsy(); // expect(ads.data[3].driver).toBeFalsy();
}); // });
}); // });
describe('drivers and passengers', () => { // describe('drivers and passengers', () => {
it('should return a data array with 6 punctual driver and passenger ads', async () => { // it('should return a data array with 6 punctual driver and passenger ads', async () => {
await createPunctualDriverPassengerAds(6); // await createPunctualDriverPassengerAds(6);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(6); // expect(ads.data.length).toBe(6);
expect(ads.total).toBe(6); // expect(ads.total).toBe(6);
expect(ads.data[0].passenger).toBeTruthy(); // expect(ads.data[0].passenger).toBeTruthy();
expect(ads.data[0].driver).toBeTruthy(); // expect(ads.data[0].driver).toBeTruthy();
}); // });
it('should return a data array limited to 10 punctual driver and passenger ads', async () => { // it('should return a data array limited to 10 punctual driver and passenger ads', async () => {
await createPunctualDriverPassengerAds(16); // await createPunctualDriverPassengerAds(16);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(10); // expect(ads.data.length).toBe(10);
expect(ads.total).toBe(16); // expect(ads.total).toBe(16);
expect(ads.data[1].passenger).toBeTruthy(); // expect(ads.data[1].passenger).toBeTruthy();
expect(ads.data[1].driver).toBeTruthy(); // expect(ads.data[1].driver).toBeTruthy();
}); // });
it('should return a data array with 6 recurrent driver and passenger ads', async () => { // it('should return a data array with 6 recurrent driver and passenger ads', async () => {
await createRecurrentDriverPassengerAds(6); // await createRecurrentDriverPassengerAds(6);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(6); // expect(ads.data.length).toBe(6);
expect(ads.total).toBe(6); // expect(ads.total).toBe(6);
expect(ads.data[2].passenger).toBeTruthy(); // expect(ads.data[2].passenger).toBeTruthy();
expect(ads.data[2].driver).toBeTruthy(); // expect(ads.data[2].driver).toBeTruthy();
}); // });
it('should return a data array limited to 10 recurrent driver and passenger ads', async () => { // it('should return a data array limited to 10 recurrent driver and passenger ads', async () => {
await createRecurrentDriverPassengerAds(16); // await createRecurrentDriverPassengerAds(16);
const ads = await adsRepository.findAll(); // const ads = await adRepository.findAll();
expect(ads.data.length).toBe(10); // expect(ads.data.length).toBe(10);
expect(ads.total).toBe(16); // expect(ads.total).toBe(16);
expect(ads.data[3].passenger).toBeTruthy(); // expect(ads.data[3].passenger).toBeTruthy();
expect(ads.data[3].driver).toBeTruthy(); // expect(ads.data[3].driver).toBeTruthy();
}); // });
}); // });
}); // });
describe('findOneByUuid', () => { describe('findOneById', () => {
it('should return an ad', async () => { it('should return an ad', async () => {
await createPunctualDriverAds(1); await createPunctualDriverAds(1);
const ad = await adsRepository.findOneByUuid(baseUuid.uuid); const result = await adRepository.findOneById(baseUuid.uuid, {
waypoints: true,
expect(ad.uuid).toBe(baseUuid.uuid);
}); });
it('should return null', async () => { expect(result.unwrap().id).toBe(baseUuid.uuid);
const ad = await adsRepository.findOneByUuid(
'544572be-11fb-4244-8235-587221fc9104',
);
expect(ad).toBeNull();
}); });
// it('should return null', async () => {
// const ad = await adRepository.findOneById(
// '544572be-11fb-4244-8235-587221fc9104',
// );
// expect(ad).toBeNull();
// });
}); });
describe('create', () => { // describe('create', () => {
it('should create an punctual ad', async () => { // it('should create a punctual ad', async () => {
const beforeCount = await prismaService.ad.count(); // const beforeCount = await prismaService.ad.count();
const adToCreate: AdDTO = new AdDTO(); // const adToCreate: AdDTO = new AdDTO();
adToCreate.uuid = 'be459a29-7a41-4c0b-b371-abe90bfb6f00'; // adToCreate.uuid = 'be459a29-7a41-4c0b-b371-abe90bfb6f00';
adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200'; // adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200';
adToCreate.driver = true; // adToCreate.driver = true;
adToCreate.passenger = false; // adToCreate.passenger = false;
adToCreate.frequency = Frequency.PUNCTUAL; // adToCreate.frequency = Frequency.PUNCTUAL;
adToCreate.fromDate = new Date('05-22-2023 09:36'); // adToCreate.fromDate = new Date('05-22-2023 09:36');
adToCreate.toDate = new Date('05-22-2023 09:36'); // adToCreate.toDate = new Date('05-22-2023 09:36');
adToCreate.monTime = '09:36'; // adToCreate.monTime = '09:36';
adToCreate.monMargin = 900; // adToCreate.monMargin = 900;
adToCreate.tueMargin = 900; // adToCreate.tueMargin = 900;
adToCreate.wedMargin = 900; // adToCreate.wedMargin = 900;
adToCreate.thuMargin = 900; // adToCreate.thuMargin = 900;
adToCreate.friMargin = 900; // adToCreate.friMargin = 900;
adToCreate.satMargin = 900; // adToCreate.satMargin = 900;
adToCreate.sunMargin = 900; // adToCreate.sunMargin = 900;
adToCreate.seatsProposed = 3; // adToCreate.seatsProposed = 3;
adToCreate.seatsRequested = 0; // adToCreate.seatsRequested = 0;
adToCreate.strict = false; // adToCreate.strict = false;
adToCreate.waypoints = { // adToCreate.waypoints = {
create: [address0 as Waypoint, address1 as Waypoint], // create: [originWaypoint as Waypoint, destinationWaypoint as Waypoint],
}; // };
const ad = await adsRepository.create(adToCreate); // const ad = await adRepository.create(adToCreate);
const afterCount = await prismaService.ad.count(); // const afterCount = await prismaService.ad.count();
expect(afterCount - beforeCount).toBe(1); // expect(afterCount - beforeCount).toBe(1);
expect(ad.uuid).toBe('be459a29-7a41-4c0b-b371-abe90bfb6f00'); // expect(ad.uuid).toBe('be459a29-7a41-4c0b-b371-abe90bfb6f00');
}); // });
it('should create an recurrent ad', async () => { // it('should create a recurrent ad', async () => {
const beforeCount = await prismaService.ad.count(); // const beforeCount = await prismaService.ad.count();
const adToCreate: AdDTO = new AdDTO(); // const adToCreate: AdDTO = new AdDTO();
adToCreate.uuid = '137a26fa-4b38-48ba-aecf-1a75f6b20f3d'; // adToCreate.uuid = '137a26fa-4b38-48ba-aecf-1a75f6b20f3d';
adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200'; // adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200';
adToCreate.driver = true; // adToCreate.driver = true;
adToCreate.passenger = false; // adToCreate.passenger = false;
adToCreate.frequency = Frequency.RECURRENT; // adToCreate.frequency = Frequency.RECURRENT;
adToCreate.fromDate = new Date('01-15-2023 '); // adToCreate.fromDate = new Date('01-15-2023 ');
adToCreate.toDate = new Date('10-31-2023'); // adToCreate.toDate = new Date('10-31-2023');
adToCreate.monTime = '07:30'; // adToCreate.monTime = '07:30';
adToCreate.friTime = '07:45'; // adToCreate.friTime = '07:45';
adToCreate.thuTime = '08:00'; // adToCreate.thuTime = '08:00';
adToCreate.monMargin = 900; // adToCreate.monMargin = 900;
adToCreate.tueMargin = 900; // adToCreate.tueMargin = 900;
adToCreate.wedMargin = 900; // adToCreate.wedMargin = 900;
adToCreate.thuMargin = 900; // adToCreate.thuMargin = 900;
adToCreate.friMargin = 900; // adToCreate.friMargin = 900;
adToCreate.satMargin = 900; // adToCreate.satMargin = 900;
adToCreate.sunMargin = 900; // adToCreate.sunMargin = 900;
adToCreate.seatsProposed = 2; // adToCreate.seatsProposed = 2;
adToCreate.seatsRequested = 0; // adToCreate.seatsRequested = 0;
adToCreate.strict = false; // adToCreate.strict = false;
adToCreate.waypoints = { // adToCreate.waypoints = {
create: [address0 as Waypoint, address1 as Waypoint], // create: [originWaypoint as Waypoint, destinationWaypoint as Waypoint],
}; // };
const ad = await adsRepository.create(adToCreate); // const ad = await adRepository.create(adToCreate);
const afterCount = await prismaService.ad.count(); // const afterCount = await prismaService.ad.count();
expect(afterCount - beforeCount).toBe(1); // expect(afterCount - beforeCount).toBe(1);
expect(ad.uuid).toBe('137a26fa-4b38-48ba-aecf-1a75f6b20f3d'); // expect(ad.uuid).toBe('137a26fa-4b38-48ba-aecf-1a75f6b20f3d');
}); // });
}); // });
}); });

View File

@ -0,0 +1,409 @@
import { AdEntity } from '@modules/ad/core/ad.entity';
import {
CreateAdProps,
DefaultAdProps,
Frequency,
} from '@modules/ad/core/ad.types';
import { MarginDurationsProps } from '@modules/ad/core/value-objects/margin-durations.value-object';
import { WaypointProps } from '@modules/ad/core/value-objects/waypoint.value-object';
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-21',
toDate: '2023-06-21',
schedule: {
wed: '08:30',
},
frequency: Frequency.PUNCTUAL,
};
const recurrentCreateAdProps = {
fromDate: '2023-06-21',
toDate: '2024-06-20',
schedule: {
mon: '08:30',
tue: '08:30',
wed: '08:00',
thu: '08:30',
fri: '08:30',
},
frequency: Frequency.RECURRENT,
};
const punctualPassengerCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: false,
passenger: true,
};
const recurrentPassengerCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...recurrentCreateAdProps,
marginDurations: marginDurationsProps,
driver: false,
passenger: true,
};
const punctualDriverCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: true,
passenger: false,
};
const recurrentDriverCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...recurrentCreateAdProps,
marginDurations: marginDurationsProps,
driver: true,
passenger: false,
};
const punctualDriverPassengerCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: true,
passenger: true,
};
const recurrentDriverPassengerCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...recurrentCreateAdProps,
marginDurations: marginDurationsProps,
driver: true,
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,
};
describe('Ad entity create', () => {
describe('With complete props', () => {
it('should create a new punctual passenger ad entity', async () => {
const punctualPassengerAd: AdEntity = AdEntity.create(
punctualPassengerCreateAdProps,
defaultAdProps,
);
expect(punctualPassengerAd.id.length).toBe(36);
expect(punctualPassengerAd.getProps().schedule.mon).toBeUndefined();
expect(punctualPassengerAd.getProps().schedule.wed).toBe('08:30');
expect(punctualPassengerAd.getProps().driver).toBeFalsy();
expect(punctualPassengerAd.getProps().passenger).toBeTruthy();
});
it('should create a new punctual driver ad entity', async () => {
const punctualDriverAd: AdEntity = AdEntity.create(
punctualDriverCreateAdProps,
defaultAdProps,
);
expect(punctualDriverAd.id.length).toBe(36);
expect(punctualDriverAd.getProps().schedule.mon).toBeUndefined();
expect(punctualDriverAd.getProps().schedule.wed).toBe('08:30');
expect(punctualDriverAd.getProps().driver).toBeTruthy();
expect(punctualDriverAd.getProps().passenger).toBeFalsy();
});
it('should create a new punctual driver and passenger ad entity', async () => {
const punctualDriverPassengerAd: AdEntity = AdEntity.create(
punctualDriverPassengerCreateAdProps,
defaultAdProps,
);
expect(punctualDriverPassengerAd.id.length).toBe(36);
expect(punctualDriverPassengerAd.getProps().schedule.mon).toBeUndefined();
expect(punctualDriverPassengerAd.getProps().schedule.wed).toBe('08:30');
expect(punctualDriverPassengerAd.getProps().driver).toBeTruthy();
expect(punctualDriverPassengerAd.getProps().passenger).toBeTruthy();
});
it('should create a new recurrent passenger ad entity', async () => {
const recurrentPassengerAd: AdEntity = AdEntity.create(
recurrentPassengerCreateAdProps,
defaultAdProps,
);
expect(recurrentPassengerAd.id.length).toBe(36);
expect(recurrentPassengerAd.getProps().schedule.mon).toBe('08:30');
expect(recurrentPassengerAd.getProps().schedule.sat).toBeUndefined();
expect(recurrentPassengerAd.getProps().driver).toBeFalsy();
expect(recurrentPassengerAd.getProps().passenger).toBeTruthy();
});
it('should create a new recurrent driver ad entity', async () => {
const recurrentDriverAd: AdEntity = AdEntity.create(
recurrentDriverCreateAdProps,
defaultAdProps,
);
expect(recurrentDriverAd.id.length).toBe(36);
expect(recurrentDriverAd.getProps().schedule.mon).toBe('08:30');
expect(recurrentDriverAd.getProps().schedule.sat).toBeUndefined();
expect(recurrentDriverAd.getProps().driver).toBeTruthy();
expect(recurrentDriverAd.getProps().passenger).toBeFalsy();
});
it('should create a new recurrent driver and passenger ad entity', async () => {
const recurrentDriverPassengerAd: AdEntity = AdEntity.create(
recurrentDriverPassengerCreateAdProps,
defaultAdProps,
);
expect(recurrentDriverPassengerAd.id.length).toBe(36);
expect(recurrentDriverPassengerAd.getProps().schedule.mon).toBe('08:30');
expect(
recurrentDriverPassengerAd.getProps().schedule.sat,
).toBeUndefined();
expect(recurrentDriverPassengerAd.getProps().driver).toBeTruthy();
expect(recurrentDriverPassengerAd.getProps().passenger).toBeTruthy();
});
});
describe('With incomplete props', () => {
it('should create a new punctual passenger ad entity if no role is given', async () => {
const punctualWithoutRoleCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: false,
passenger: false,
};
const punctualWithoutRoleAd: AdEntity = AdEntity.create(
punctualWithoutRoleCreateAdProps,
defaultAdProps,
);
expect(punctualWithoutRoleAd.id.length).toBe(36);
expect(punctualWithoutRoleAd.getProps().schedule.mon).toBeUndefined();
expect(punctualWithoutRoleAd.getProps().schedule.wed).toBe('08:30');
expect(punctualWithoutRoleAd.getProps().driver).toBeFalsy();
expect(punctualWithoutRoleAd.getProps().passenger).toBeTruthy();
});
it('should create a new strict punctual passenger ad entity if no strict param is given', async () => {
const punctualWithoutStrictCreateAdProps: CreateAdProps = {
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
seatsProposed: 3,
seatsRequested: 1,
strict: undefined,
waypoints: [originWaypointProps, destinationWaypointProps],
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: false,
passenger: true,
};
const punctualWithoutStrictAd: AdEntity = AdEntity.create(
punctualWithoutStrictCreateAdProps,
defaultAdProps,
);
expect(punctualWithoutStrictAd.id.length).toBe(36);
expect(punctualWithoutStrictAd.getProps().schedule.mon).toBeUndefined();
expect(punctualWithoutStrictAd.getProps().schedule.wed).toBe('08:30');
expect(punctualWithoutStrictAd.getProps().driver).toBeFalsy();
expect(punctualWithoutStrictAd.getProps().passenger).toBeTruthy();
expect(punctualWithoutStrictAd.getProps().strict).toBeFalsy();
});
it('should create a new punctual passenger ad entity with seats requested if no seats requested param is given', async () => {
const punctualWithoutSeatsRequestedCreateAdProps: CreateAdProps = {
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
seatsProposed: 3,
seatsRequested: undefined,
strict: false,
waypoints: [originWaypointProps, destinationWaypointProps],
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: false,
passenger: true,
};
const punctualWithoutSeatsRequestedAd: AdEntity = AdEntity.create(
punctualWithoutSeatsRequestedCreateAdProps,
defaultAdProps,
);
expect(punctualWithoutSeatsRequestedAd.id.length).toBe(36);
expect(
punctualWithoutSeatsRequestedAd.getProps().schedule.mon,
).toBeUndefined();
expect(punctualWithoutSeatsRequestedAd.getProps().schedule.wed).toBe(
'08:30',
);
expect(punctualWithoutSeatsRequestedAd.getProps().driver).toBeFalsy();
expect(punctualWithoutSeatsRequestedAd.getProps().passenger).toBeTruthy();
expect(punctualWithoutSeatsRequestedAd.getProps().seatsRequested).toBe(1);
});
it('should create a new punctual driver ad entity with seats proposed if no seats proposed param is given', async () => {
const punctualWithoutSeatsProposedCreateAdProps: CreateAdProps = {
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
seatsProposed: undefined,
seatsRequested: 1,
strict: false,
waypoints: [originWaypointProps, destinationWaypointProps],
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: true,
passenger: false,
};
const punctualWithoutSeatsProposedAd: AdEntity = AdEntity.create(
punctualWithoutSeatsProposedCreateAdProps,
defaultAdProps,
);
expect(punctualWithoutSeatsProposedAd.id.length).toBe(36);
expect(
punctualWithoutSeatsProposedAd.getProps().schedule.mon,
).toBeUndefined();
expect(punctualWithoutSeatsProposedAd.getProps().schedule.wed).toBe(
'08:30',
);
expect(punctualWithoutSeatsProposedAd.getProps().driver).toBeTruthy();
expect(punctualWithoutSeatsProposedAd.getProps().passenger).toBeFalsy();
expect(punctualWithoutSeatsProposedAd.getProps().seatsProposed).toBe(3);
});
it('should create a new punctual driver ad entity with margin durations if margin durations are empty', async () => {
const punctualWithoutMarginDurationsCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
waypoints: [originWaypointProps, destinationWaypointProps],
...punctualCreateAdProps,
marginDurations: {},
driver: true,
passenger: false,
};
const punctualWithoutMarginDurationsAd: AdEntity = AdEntity.create(
punctualWithoutMarginDurationsCreateAdProps,
defaultAdProps,
);
expect(punctualWithoutMarginDurationsAd.id.length).toBe(36);
expect(
punctualWithoutMarginDurationsAd.getProps().schedule.mon,
).toBeUndefined();
expect(punctualWithoutMarginDurationsAd.getProps().schedule.wed).toBe(
'08:30',
);
expect(punctualWithoutMarginDurationsAd.getProps().driver).toBeTruthy();
expect(punctualWithoutMarginDurationsAd.getProps().passenger).toBeFalsy();
expect(
punctualWithoutMarginDurationsAd.getProps().marginDurations.mon,
).toBe(900);
});
it('should create a new punctual driver ad entity with margin durations if margin durations are undefined', async () => {
const punctualWithoutMarginDurationsCreateAdProps: CreateAdProps = {
...baseCreateAdProps,
waypoints: [originWaypointProps, destinationWaypointProps],
...punctualCreateAdProps,
marginDurations: undefined,
driver: true,
passenger: false,
};
const punctualWithoutMarginDurationsAd: AdEntity = AdEntity.create(
punctualWithoutMarginDurationsCreateAdProps,
defaultAdProps,
);
expect(punctualWithoutMarginDurationsAd.id.length).toBe(36);
expect(
punctualWithoutMarginDurationsAd.getProps().schedule.mon,
).toBeUndefined();
expect(punctualWithoutMarginDurationsAd.getProps().schedule.wed).toBe(
'08:30',
);
expect(punctualWithoutMarginDurationsAd.getProps().driver).toBeTruthy();
expect(punctualWithoutMarginDurationsAd.getProps().passenger).toBeFalsy();
expect(
punctualWithoutMarginDurationsAd.getProps().marginDurations.mon,
).toBe(900);
});
it('should create a new punctual passenger ad entity with valid positions if positions are missing', async () => {
const punctualWithoutPositionsCreateAdProps: CreateAdProps = {
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
seatsProposed: 3,
seatsRequested: 1,
strict: undefined,
waypoints: [
{
position: undefined,
address: {
houseNumber: '5',
street: 'Avenue Foch',
locality: 'Nancy',
postalCode: '54000',
country: 'France',
coordinates: {
lon: 48.68944505415954,
lat: 6.176510296462267,
},
},
},
{
position: undefined,
address: {
locality: 'Paris',
postalCode: '75000',
country: 'France',
coordinates: {
lon: 48.8566,
lat: 2.3522,
},
},
},
],
...punctualCreateAdProps,
marginDurations: marginDurationsProps,
driver: false,
passenger: false,
};
const punctualWithoutPositionsAd: AdEntity = AdEntity.create(
punctualWithoutPositionsCreateAdProps,
defaultAdProps,
);
expect(punctualWithoutPositionsAd.id.length).toBe(36);
expect(
punctualWithoutPositionsAd.getProps().schedule.mon,
).toBeUndefined();
expect(punctualWithoutPositionsAd.getProps().schedule.wed).toBe('08:30');
expect(punctualWithoutPositionsAd.getProps().driver).toBeFalsy();
expect(punctualWithoutPositionsAd.getProps().passenger).toBeTruthy();
expect(punctualWithoutPositionsAd.getProps().waypoints[0].position).toBe(
0,
);
expect(punctualWithoutPositionsAd.getProps().waypoints[1].position).toBe(
1,
);
});
});
});

View File

@ -10,6 +10,7 @@ import { Result } from 'oxide.ts';
import { AggregateID } from '@libs/ddd'; import { AggregateID } from '@libs/ddd';
import { AdAlreadyExistsError } from '@modules/ad/core/ad.errors'; import { AdAlreadyExistsError } from '@modules/ad/core/ad.errors';
import { AdEntity } from '@modules/ad/core/ad.entity'; import { AdEntity } from '@modules/ad/core/ad.entity';
import { ConflictException } from '@libs/exceptions';
const originWaypoint: WaypointDTO = { const originWaypoint: WaypointDTO = {
position: 0, position: 0,
@ -44,7 +45,15 @@ const punctualCreateAdRequest: CreateAdRequestDTO = {
}; };
const mockAdRepository = { const mockAdRepository = {
insert: jest.fn(), insert: jest
.fn()
.mockImplementationOnce(() => ({}))
.mockImplementationOnce(() => {
throw new Error();
})
.mockImplementationOnce(() => {
throw new ConflictException('already exists');
}),
}; };
const mockDefaultParamsProvider: DefaultParamsProviderPort = { const mockDefaultParamsProvider: DefaultParamsProviderPort = {
@ -102,5 +111,21 @@ describe('create-ad.service', () => {
await createAdService.execute(createAdCommand); await createAdService.execute(createAdCommand);
expect(result.unwrap()).toBe('047a6ecf-23d4-4d3e-877c-3225d560a8da'); expect(result.unwrap()).toBe('047a6ecf-23d4-4d3e-877c-3225d560a8da');
}); });
it('should throw an error if something bad happens', async () => {
AdEntity.create = jest.fn().mockReturnValue({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
});
await expect(
createAdService.execute(createAdCommand),
).rejects.toBeInstanceOf(Error);
});
it('should return an Err if Ad already exists', async () => {
AdEntity.create = jest.fn().mockReturnValue({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
});
const result: Result<AggregateID, AdAlreadyExistsError> =
await createAdService.execute(createAdCommand);
expect(result.isErr()).toBeTruthy();
});
}); });
}); });