diff --git a/src/app.module.ts b/src/app.module.ts index ee0f470..f11aeaf 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -14,6 +14,7 @@ import { } from '@mobicoop/configuration-module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { RequestContextModule } from 'nestjs-request-context'; +import { HealthModule } from '@modules/health/health.module'; @Module({ imports: [ @@ -53,7 +54,7 @@ import { RequestContextModule } from 'nestjs-request-context'; propagateConfigurationQueue: 'ad-configuration-propagate', }), }), - // HealthModule, + HealthModule, AdModule, ], controllers: [], diff --git a/src/libs/db/prisma-repository.base.ts b/src/libs/db/prisma-repository.base.ts index a6ba546..b27b41e 100644 --- a/src/libs/db/prisma-repository.base.ts +++ b/src/libs/db/prisma-repository.base.ts @@ -3,7 +3,10 @@ import { AggregateRoot, Mapper, RepositoryPort } from '../ddd'; import { ObjectLiteral } from '../types'; import { LoggerPort } from '../ports/logger.port'; import { None, Option, Some } from 'oxide.ts'; -import { PrismaRepositoryPort } from '../ports/prisma-repository.port'; +import { + PrismaRawRepositoryPort, + PrismaRepositoryPort, +} from '../ports/prisma-repository.port'; import { Prisma } from '@prisma/client'; import { ConflictException, DatabaseErrorException } from '@libs/exceptions'; @@ -14,6 +17,7 @@ export abstract class PrismaRepositoryBase< { protected constructor( protected readonly prisma: PrismaRepositoryPort | any, + protected readonly prismaRaw: PrismaRawRepositoryPort, protected readonly mapper: Mapper, protected readonly eventEmitter: EventEmitter2, protected readonly logger: LoggerPort, @@ -43,9 +47,10 @@ export abstract class PrismaRepositoryBase< async healthCheck(): Promise { try { - await this.prisma.$queryRaw`SELECT 1`; + await this.prismaRaw.$queryRaw`SELECT 1`; return true; } catch (e) { + console.log(e); if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseErrorException(e.message); } diff --git a/src/libs/ports/prisma-repository.port.ts b/src/libs/ports/prisma-repository.port.ts index 70cfcee..79b86fe 100644 --- a/src/libs/ports/prisma-repository.port.ts +++ b/src/libs/ports/prisma-repository.port.ts @@ -2,3 +2,10 @@ export interface PrismaRepositoryPort { findUnique(options: any): Promise; create(entity: any): Promise; } + +export interface PrismaRawRepositoryPort { + $queryRaw( + query: TemplateStringsArray, + ...values: any[] + ): Promise; +} diff --git a/src/modules/ad/ad.module.ts b/src/modules/ad/ad.module.ts index e81865a..2211535 100644 --- a/src/modules/ad/ad.module.ts +++ b/src/modules/ad/ad.module.ts @@ -47,5 +47,12 @@ import { PrismaService } from '@libs/db/prisma.service'; useClass: TimezoneFinder, }, ], + exports: [ + PrismaService, + AdMapper, + AD_REPOSITORY, + PARAMS_PROVIDER, + TIMEZONE_FINDER, + ], }) export class AdModule {} diff --git a/src/modules/ad/infrastructure/ad.repository.ts b/src/modules/ad/infrastructure/ad.repository.ts index 1a187ef..3bddba8 100644 --- a/src/modules/ad/infrastructure/ad.repository.ts +++ b/src/modules/ad/infrastructure/ad.repository.ts @@ -66,6 +66,12 @@ export class AdRepository mapper: AdMapper, eventEmitter: EventEmitter2, ) { - super(prisma.ad, mapper, eventEmitter, new Logger(AdRepository.name)); + super( + prisma.ad, + prisma, + mapper, + eventEmitter, + new Logger(AdRepository.name), + ); } } diff --git a/src/modules/ad/tests/domain/frequency-normalizer.spec.ts b/src/modules/ad/tests/domain/frequency-normalizer.spec.ts deleted file mode 100644 index 573a749..0000000 --- a/src/modules/ad/tests/domain/frequency-normalizer.spec.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Day } from '../../../core/_old/types/day.enum'; -import { CreateAdRequestDTO } from '../../../interface/commands/create-ad.request.dto'; -import { ScheduleDTO } from '../../../core/dtos/schedule.dto'; -import { FrequencyNormalizer } from '../../../core/entities/frequency.normalizer'; -import { Frequency } from '../../../interface/commands/frequency.enum'; - -describe('recurrent normalizer transformer for punctual ad ', () => { - const frequencyNormalizer = new FrequencyNormalizer(); - it('should transform punctual ad into recurrent ad', () => { - const punctualAd: CreateAdRequestDTO = { - userUuid: 'cb7ad514-ad0d-463f-9041-b79f9afe33aa', - frequency: Frequency.PUNCTUAL, - departureDate: new Date('2023-03-05T12:39:39+02:00'), - waypoints: [ - { - position: 0, - lon: 48.68944505415954, - lat: 6.176510296462267, - houseNumber: '5', - street: 'Avenue Foch', - locality: 'Nancy', - postalCode: '54000', - country: 'France', - }, - { - position: 1, - lon: 48.8566, - lat: 2.3522, - locality: 'Paris', - postalCode: '75000', - country: 'France', - }, - ], - }; - // expect(frequencyNormalizer.fromDateResolver(punctualAd)).toStrictEqual( - // new Date('2023-03-05T12:39:39Z'), - // ); - // expect(frequencyNormalizer.toDateResolver(punctualAd)).toStrictEqual( - // new Date('2023-03-05T12:39:39Z'), - // ); - // expect( - // frequencyNormalizer.scheduleResolver(punctualAd, Day.mon), - // ).toBeUndefined(); - // expect( - // frequencyNormalizer.scheduleResolver(punctualAd, Day.tue), - // ).toBeUndefined(); - // expect( - // frequencyNormalizer.scheduleResolver(punctualAd, Day.wed), - // ).toBeUndefined(); - // expect( - // frequencyNormalizer.scheduleResolver(punctualAd, Day.thu), - // ).toBeUndefined(); - // expect( - // frequencyNormalizer.scheduleResolver(punctualAd, Day.fri), - // ).toBeUndefined(); - // expect( - // frequencyNormalizer.scheduleResolver(punctualAd, Day.sat), - // ).toBeUndefined(); - expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.sun)).toBe( - '12:39', - ); - }); - // it('should leave recurrent ad as is', () => { - // const recurrentAd: CreateAdRequest = { - // userUuid: '', - // frequency: Frequency.RECURRENT, - // schedule: { - // mon: '08:30', - // tue: '08:30', - // wed: '09:00', - // fri: '09:00', - // }, - // waypoints: [], - // }; - // expect(frequencyNormalizer.fromDateResolver(recurrentAd)).toBe( - // recurrentAd.departure, - // ); - // expect(frequencyNormalizer.toDateResolver(recurrentAd)).toBe( - // recurrentAd.departure, - // ); - // expect(frequencyNormalizer.scheduleResolver(recurrentAd, Day.mon)).toBe( - // recurrentAd.schedule.mon, - // ); - // expect(frequencyNormalizer.scheduleResolver(recurrentAd, Day.tue)).toBe( - // recurrentAd.schedule.tue, - // ); - // expect(frequencyNormalizer.scheduleResolver(recurrentAd, Day.wed)).toBe( - // recurrentAd.schedule.wed, - // ); - // expect(frequencyNormalizer.scheduleResolver(recurrentAd, Day.thu)).toBe( - // recurrentAd.schedule.thu, - // ); - // expect(frequencyNormalizer.scheduleResolver(recurrentAd, Day.fri)).toBe( - // recurrentAd.schedule.fri, - // ); - // expect(frequencyNormalizer.scheduleResolver(recurrentAd, Day.sat)).toBe( - // recurrentAd.schedule.sat, - // ); - // expect(frequencyNormalizer.scheduleResolver(recurrentAd, Day.sun)).toBe( - // recurrentAd.schedule.sun, - // ); - // }); - // it('should pass for each day of the week of a deprarture ', () => { - // const punctualAd: CreateAdRequest = { - // userUuid: '', - // frequency: Frequency.PUNCTUAL, - // departure: undefined, - // schedule: {} as ScheduleDTO, - // waypoints: [], - // }; - // punctualAd.departure = new Date('05-01-2023 '); - // expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.mon)).toBe( - // '00:00', - // ); - // punctualAd.departure = new Date('05-02-2023 06:32:45'); - // expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.tue)).toBe( - // '06:32', - // ); - // punctualAd.departure = new Date('05-03-2023 10:21'); - // expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.wed)).toBe( - // '10:21', - // ); - // punctualAd.departure = new Date('05-04-2023 11:06:00'); - // expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.thu)).toBe( - // '11:06', - // ); - // punctualAd.departure = new Date('05-05-2023 05:20'); - // expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.fri)).toBe( - // '05:20', - // ); - // punctualAd.departure = new Date('05-06-2023'); - // expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.sat)).toBe( - // '00:00', - // ); - // punctualAd.departure = new Date('05-07-2023'); - // expect(frequencyNormalizer.scheduleResolver(punctualAd, Day.sun)).toBe( - // '00:00', - // ); - // }); -}); diff --git a/src/modules/ad/tests/domain/frequency.mapping.spec.ts b/src/modules/ad/tests/domain/frequency.mapping.spec.ts deleted file mode 100644 index a495fe0..0000000 --- a/src/modules/ad/tests/domain/frequency.mapping.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { intToFrequency } from '../../../core/dtos/validators/frequency.mapping'; -import { Frequency } from '../../../interface/commands/frequency.enum'; - -describe('frequency mapping function ', () => { - it('should return punctual', () => { - expect(intToFrequency(1)).toBe(Frequency.PUNCTUAL); - }); - it('should return recurrent', () => { - expect(intToFrequency(2)).toBe(Frequency.RECURRENT); - }); - it('should throw an error if frequency is unknown', () => { - expect(() => intToFrequency(0)).toThrow(); - expect(() => intToFrequency(3)).toThrow(); - }); -}); diff --git a/src/modules/ad/tests/domain/is-punctual-or-recurrent.spec.ts b/src/modules/ad/tests/domain/is-punctual-or-recurrent.spec.ts deleted file mode 100644 index d98e67e..0000000 --- a/src/modules/ad/tests/domain/is-punctual-or-recurrent.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { isPunctualOrRecurrent } from '../../../core/dtos/validators/is-punctual-or-recurrent'; -import { Frequency } from '../../../interface/commands/frequency.enum'; - -describe('punctual or recurrent validators', () => { - describe('punctual case ', () => { - describe('valid cases', () => { - it('should validate with valid departure', () => { - expect( - isPunctualOrRecurrent({ - value: undefined, - constraints: [], - targetName: '', - object: { - frequency: Frequency.PUNCTUAL, - departure: new Date('2023-02-01T18:00:00+02:00'), - }, - property: '', - }), - ).toBeTruthy(); - }); - }); - describe('invalid cases ', () => { - it('should not validate without departure', () => { - expect( - isPunctualOrRecurrent({ - value: undefined, - constraints: [], - targetName: '', - object: { - frequency: Frequency.PUNCTUAL, - }, - property: '', - }), - ).toBeFalsy(); - }); - }); - }); - describe('recurrent case ', () => { - describe('valid cases', () => { - it('should validate with valid from date, to date and non empty schedule', () => { - expect( - isPunctualOrRecurrent({ - value: undefined, - constraints: [], - targetName: '', - object: { - frequency: Frequency.RECURRENT, - fromDate: new Date('2023-01-15'), - toDate: new Date('2023-06-30'), - schedule: { - mon: '08:30', - }, - }, - property: '', - }), - ).toBeTruthy(); - }); - }); - describe('invalid cases ', () => { - it('should not validate with empty schedule', () => { - expect( - isPunctualOrRecurrent({ - value: undefined, - constraints: [], - targetName: '', - object: { - frequency: Frequency.RECURRENT, - fromDate: new Date('2023-01-15'), - toDate: new Date('2023-06-30'), - schedule: {}, - }, - property: '', - }), - ).toBeFalsy(); - }); - it('should not validate with invalid from date to date and empty schedule and margin', () => { - expect( - isPunctualOrRecurrent({ - value: undefined, - constraints: [], - targetName: '', - object: { - frequency: Frequency.RECURRENT, - departure: new Date('2023-10-20'), - toDate: new Date('2023-10-30'), - }, - property: '', - }), - ).toBeFalsy(); - }); - }); - }); -}); diff --git a/src/modules/ad/tests/unit/interface/frequency.mapping.spec.ts b/src/modules/ad/tests/unit/interface/frequency.mapping.spec.ts new file mode 100644 index 0000000..bd8928d --- /dev/null +++ b/src/modules/ad/tests/unit/interface/frequency.mapping.spec.ts @@ -0,0 +1,15 @@ +import { Frequency } from '@modules/ad/core/ad.types'; +import { intToFrequency } from '@modules/ad/interface/grpc-controllers/dtos/validators/frequency.mapping'; + +describe('frequency mapping', () => { + it('should return punctual if frequency is 1', () => { + expect(intToFrequency(1)).toBe(Frequency.PUNCTUAL); + }); + it('should return recurrent if frequency is 2', () => { + expect(intToFrequency(2)).toBe(Frequency.RECURRENT); + }); + it('should throw an error if frequency is unknown', () => { + expect(() => intToFrequency(0)).toThrow(); + expect(() => intToFrequency(3)).toThrow(); + }); +}); diff --git a/src/modules/ad/tests/domain/valid-position-indexes.spec.ts b/src/modules/ad/tests/unit/interface/valid-position-indexes.spec.ts similarity index 68% rename from src/modules/ad/tests/domain/valid-position-indexes.spec.ts rename to src/modules/ad/tests/unit/interface/valid-position-indexes.spec.ts index f00d6f3..d90f99e 100644 --- a/src/modules/ad/tests/domain/valid-position-indexes.spec.ts +++ b/src/modules/ad/tests/unit/interface/valid-position-indexes.spec.ts @@ -1,7 +1,8 @@ -import { Waypoint } from '../../../interface/commands/waypoint'; -import { hasValidPositionIndexes } from '../../../core/dtos/validators/waypoint-position'; -describe('addresses position validators', () => { - const mockAddress1: Waypoint = { +import { hasValidPositionIndexes } from '@modules/ad/interface/grpc-controllers/dtos/validators/waypoint-position'; +import { WaypointDTO } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto'; + +describe('addresses position validator', () => { + const mockAddress1: WaypointDTO = { lon: 48.68944505415954, lat: 6.176510296462267, houseNumber: '5', @@ -10,33 +11,33 @@ describe('addresses position validators', () => { postalCode: '54000', country: 'France', }; - const mockAddress2: Waypoint = { + const mockAddress2: WaypointDTO = { lon: 48.8566, lat: 2.3522, locality: 'Paris', postalCode: '75000', country: 'France', }; - - const mockAddress3: Waypoint = { + const mockAddress3: WaypointDTO = { lon: 49.2628, lat: 4.0347, locality: 'Reims', postalCode: '51454', country: 'France', }; - it('should validate if none of position is definded ', () => { + + it('should validate if no position is defined', () => { expect( hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]), ).toBeTruthy(); }); - it('should throw an error if position are partialy defined ', () => { + it('should not validate if only one position is defined', () => { mockAddress1.position = 0; expect( hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]), ).toBeFalsy(); }); - it('should throw an error if position are partialy defined ', () => { + it('should not validate if positions are partially defined', () => { mockAddress1.position = 0; mockAddress2.position = null; mockAddress3.position = undefined; @@ -45,7 +46,7 @@ describe('addresses position validators', () => { ).toBeFalsy(); }); - it('should throw an error if positions are not incremented ', () => { + it('should not validate if multiple positions have same value', () => { mockAddress1.position = 0; mockAddress2.position = 1; mockAddress3.position = 1; @@ -53,7 +54,7 @@ describe('addresses position validators', () => { hasValidPositionIndexes([mockAddress1, mockAddress2, mockAddress3]), ).toBeFalsy(); }); - it('should validate if all positions are defined and incremented', () => { + it('should validate if all positions are ordered', () => { mockAddress1.position = 0; mockAddress2.position = 1; mockAddress3.position = 2; diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts index 945c281..6b04c26 100644 --- a/src/modules/health/health.module.ts +++ b/src/modules/health/health.module.ts @@ -8,9 +8,10 @@ import { RepositoriesHealthIndicatorUseCase } from './core/usecases/repositories import { AdRepository } from '../ad/infrastructure/ad.repository'; import { AD_REPOSITORY } from './health.di-tokens'; import { HealthGrpcController } from './interface/grpc-controllers/health.grpc.controller'; +import { AdModule } from '@modules/ad/ad.module'; @Module({ - imports: [TerminusModule], + imports: [TerminusModule, AdModule], controllers: [HealthGrpcController, HealthHttpController], providers: [ RepositoriesHealthIndicatorUseCase, diff --git a/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts index 6e5642f..cf9a7fc 100644 --- a/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts +++ b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts @@ -12,7 +12,7 @@ const mockAdRepository = { return Promise.resolve(true); }) .mockImplementation(() => { - throw new DatabaseErrorException('an error occured in the database'); + throw new DatabaseErrorException('An error occured in the database'); }), }; @@ -56,9 +56,11 @@ describe('RepositoriesHealthIndicatorUseCase', () => { }); it('should throw an error if database is unavailable', async () => { + jest.spyOn(mockMessagePublisher, 'publish'); await expect( repositoriesHealthIndicatorUseCase.isHealthy('repositories'), ).rejects.toBeInstanceOf(HealthCheckError); + expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1); }); }); });