From 580ece8e7c7d58b1364d9b53e6a057b28f732b9d Mon Sep 17 00:00:00 2001 From: sbriat Date: Tue, 18 Jul 2023 14:42:27 +0200 Subject: [PATCH] use health package, upgrade ddd library, improve integration tests --- package-lock.json | 25 +- package.json | 21 +- src/app.constants.ts | 1 - src/app.module.ts | 39 +- src/modules/ad/ad.di-tokens.ts | 1 + src/modules/ad/ad.module.ts | 17 +- ...when-ad-is-created.domain-event-handler.ts | 21 - ...when-ad-is-created.domain-event-handler.ts | 4 +- .../ad/infrastructure/ad.repository.ts | 17 +- .../tests/integration/ad.repository.spec.ts | 457 +++++------------- ...ad-is-created.domain-event-handler.spec.ts | 94 ---- ...ad-is-created.domain-event-handler.spec.ts | 4 +- .../unit/infrastructure/ad.repository.spec.ts | 11 +- .../ports/check-repository.port.ts | 3 - .../repositories.health-indicator.usecase.ts | 48 -- src/modules/health/health.constants.ts | 1 - src/modules/health/health.di-tokens.ts | 1 - src/modules/health/health.module.ts | 37 -- .../health.grpc.controller.ts | 42 -- .../interface/grpc-controllers/health.proto | 21 - .../health.http.controller.ts | 28 -- .../tests/unit/health.grpc.controller.spec.ts | 72 --- .../tests/unit/health.http.controller.spec.ts | 90 ---- ...ositories.health-indicator.usecase.spec.ts | 66 --- src/modules/messager/messager.di-tokens.ts | 1 + src/modules/messager/messager.module.ts | 36 ++ 26 files changed, 238 insertions(+), 920 deletions(-) delete mode 100644 src/app.constants.ts delete mode 100644 src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts delete mode 100644 src/modules/ad/tests/unit/core/publish-log-message-when-ad-is-created.domain-event-handler.spec.ts delete mode 100644 src/modules/health/core/application/ports/check-repository.port.ts delete mode 100644 src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts delete mode 100644 src/modules/health/health.constants.ts delete mode 100644 src/modules/health/health.di-tokens.ts delete mode 100644 src/modules/health/health.module.ts delete mode 100644 src/modules/health/interface/grpc-controllers/health.grpc.controller.ts delete mode 100644 src/modules/health/interface/grpc-controllers/health.proto delete mode 100644 src/modules/health/interface/http-controllers/health.http.controller.ts delete mode 100644 src/modules/health/tests/unit/health.grpc.controller.spec.ts delete mode 100644 src/modules/health/tests/unit/health.http.controller.spec.ts delete mode 100644 src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts create mode 100644 src/modules/messager/messager.di-tokens.ts create mode 100644 src/modules/messager/messager.module.ts diff --git a/package-lock.json b/package-lock.json index 75f9533..43f5700 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "@grpc/proto-loader": "^0.7.6", "@liaoliaots/nestjs-redis": "^9.0.5", "@mobicoop/configuration-module": "^1.2.0", - "@mobicoop/ddd-library": "^0.0.1", + "@mobicoop/ddd-library": "^0.3.0", + "@mobicoop/health-module": "^1.1.0", "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.1", @@ -1414,9 +1415,9 @@ } }, "node_modules/@mobicoop/ddd-library": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-0.0.1.tgz", - "integrity": "sha512-T6g3pgodrMOZ1yrtNWylgu6EjRz3HPgcD9UoK0cJCvfiq9WjTH9TOZ6wKh9vIijiO+a5KJkZiKHbbjzuJvdwCg==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@mobicoop/ddd-library/-/ddd-library-0.3.0.tgz", + "integrity": "sha512-MoUDqlrDmJkumCFSyW9FY2DLbguT4rytFrmBt9tVNCr2Es6nlz4Ml3HVBwJTZrlJFU79XmiUQ5WAO0MHJt+nAg==", "dependencies": { "@nestjs/event-emitter": "^1.4.2", "@nestjs/microservices": "^9.4.0", @@ -1428,6 +1429,22 @@ "@nestjs/common": "^9.4.2" } }, + "node_modules/@mobicoop/health-module": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mobicoop/health-module/-/health-module-1.1.0.tgz", + "integrity": "sha512-tSdvpwHxMOG7U3txm3sQDUkj1cWeGg9K68u8Y2BKgD8ocMRDufiCXY43ScFXKZqWm8jkcOU6XdwJm3pxvUOq4w==", + "dependencies": { + "@grpc/grpc-js": "^1.8.14", + "@grpc/proto-loader": "^0.7.7", + "@mobicoop/ddd-library": "^0.3.0", + "@mobicoop/message-broker-module": "^1.0.5", + "@nestjs/microservices": "^9.4.2", + "@nestjs/terminus": "^9.2.2" + }, + "peerDependencies": { + "@nestjs/common": "^9.4.2" + } + }, "node_modules/@mobicoop/message-broker-module": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@mobicoop/message-broker-module/-/message-broker-module-1.2.0.tgz", diff --git a/package.json b/package.json index dcc7dde..4fbb6b7 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,8 @@ "test:unit": "jest --testPathPattern 'tests/unit/' --verbose", "test:unit:watch": "jest --testPathPattern 'tests/unit/' --verbose --watch", "test:unit:ci": "jest --testPathPattern 'tests/unit/' --coverage", - "test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose", - "test:integration:watch": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --watch", - "test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/'", + "test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --runInBand", + "test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/' --runInBand", "test:cov": "jest --testPathPattern 'tests/unit/' --coverage", "test:e2e": "jest --config ./test/jest-e2e.json", "generate": "docker exec v3-ad-api sh -c 'npx prisma generate'", @@ -38,7 +37,8 @@ "@grpc/proto-loader": "^0.7.6", "@liaoliaots/nestjs-redis": "^9.0.5", "@mobicoop/configuration-module": "^1.2.0", - "@mobicoop/ddd-library": "^0.0.1", + "@mobicoop/ddd-library": "^0.3.0", + "@mobicoop/health-module": "^1.1.0", "@mobicoop/message-broker-module": "^1.2.0", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.1", @@ -93,14 +93,10 @@ "modulePathIgnorePatterns": [ ".module.ts", ".dto.ts", - ".constants.ts", + ".di-tokens.ts", ".response.ts", - ".response.base.ts", ".port.ts", - "libs/exceptions", - "libs/types", "prisma.service.ts", - "convert-props-to-object.util.ts", "main.ts" ], "rootDir": "src", @@ -114,19 +110,14 @@ "coveragePathIgnorePatterns": [ ".module.ts", ".dto.ts", - ".constants.ts", + ".di-tokens.ts", ".response.ts", - ".response.base.ts", ".port.ts", - "libs/exceptions", - "libs/types", "prisma.service.ts", - "convert-props-to-object.util.ts", "main.ts" ], "coverageDirectory": "../coverage", "moduleNameMapper": { - "^@libs(.*)": "/libs/$1", "^@modules(.*)": "/modules/$1", "^@src(.*)": "$1" }, diff --git a/src/app.constants.ts b/src/app.constants.ts deleted file mode 100644 index 61203bb..0000000 --- a/src/app.constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const MESSAGE_PUBLISHER = Symbol(); diff --git a/src/app.module.ts b/src/app.module.ts index 29d04e8..ac4344e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -7,28 +7,21 @@ import { } from '@mobicoop/configuration-module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { RequestContextModule } from 'nestjs-request-context'; -import { HealthModule } from '@modules/health/health.module'; +import { MessagerModule } from '@modules/messager/messager.module'; +import { HealthModule } from '@mobicoop/health-module'; +import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens'; +import { MESSAGE_PUBLISHER } from '@modules/messager/messager.di-tokens'; import { - MessageBrokerModule, - MessageBrokerModuleOptions, -} from '@mobicoop/message-broker-module'; + HealthModuleOptions, + ICheckRepository, +} from '@mobicoop/health-module/dist/core/domain/types/health.types'; +import { MessagePublisherPort } from '@mobicoop/ddd-library'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), EventEmitterModule.forRoot(), RequestContextModule, - MessageBrokerModule.forRootAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: async ( - configService: ConfigService, - ): Promise => ({ - uri: configService.get('MESSAGE_BROKER_URI'), - exchange: configService.get('MESSAGE_BROKER_EXCHANGE'), - name: 'ad', - }), - }), ConfigurationModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], @@ -50,8 +43,22 @@ import { propagateConfigurationQueue: 'ad-configuration-propagate', }), }), - HealthModule, + HealthModule.forRootAsync({ + imports: [AdModule, MessagerModule], + inject: [AD_REPOSITORY, MESSAGE_PUBLISHER], + useFactory: async ( + adRepository: ICheckRepository, + messagePublisher: MessagePublisherPort, + ): Promise => ({ + serviceName: 'ad', + criticalLoggingKey: 'logging.ad.health.crit', + checkRepositories: [adRepository], + messagePublisher, + }), + }), AdModule, + MessagerModule, ], + exports: [AdModule, MessagerModule], }) export class AppModule {} diff --git a/src/modules/ad/ad.di-tokens.ts b/src/modules/ad/ad.di-tokens.ts index 5d06236..fac28b5 100644 --- a/src/modules/ad/ad.di-tokens.ts +++ b/src/modules/ad/ad.di-tokens.ts @@ -1,3 +1,4 @@ +export const AD_MESSAGE_PUBLISHER = Symbol('AD_MESSAGE_PUBLISHER'); export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER'); export const TIMEZONE_FINDER = Symbol('TIMEZONE_FINDER'); export const TIME_CONVERTER = Symbol('TIME_CONVERTER'); diff --git a/src/modules/ad/ad.module.ts b/src/modules/ad/ad.module.ts index 466e061..b72c3ec 100644 --- a/src/modules/ad/ad.module.ts +++ b/src/modules/ad/ad.module.ts @@ -2,12 +2,12 @@ import { Module, Provider } from '@nestjs/common'; import { CreateAdGrpcController } from './interface/grpc-controllers/create-ad.grpc.controller'; import { CqrsModule } from '@nestjs/cqrs'; import { + AD_MESSAGE_PUBLISHER, AD_REPOSITORY, PARAMS_PROVIDER, TIMEZONE_FINDER, TIME_CONVERTER, } from './ad.di-tokens'; -import { MESSAGE_PUBLISHER } from '@src/app.constants'; import { AdRepository } from './infrastructure/ad.repository'; import { DefaultParamsProvider } from './infrastructure/default-params-provider'; import { AdMapper } from './ad.mapper'; @@ -17,7 +17,6 @@ import { TimeConverter } from './infrastructure/time-converter'; import { FindAdByIdGrpcController } from './interface/grpc-controllers/find-ad-by-id.grpc.controller'; import { FindAdByIdQueryHandler } from './core/application/queries/find-ad-by-id/find-ad-by-id.query-handler'; import { PublishMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler'; -import { PublishLogMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler'; import { PrismaService } from './infrastructure/prisma.service'; import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; @@ -25,7 +24,6 @@ const grpcControllers = [CreateAdGrpcController, FindAdByIdGrpcController]; const eventHandlers: Provider[] = [ PublishMessageWhenAdIsCreatedDomainEventHandler, - PublishLogMessageWhenAdIsCreatedDomainEventHandler, ]; const commandHandlers: Provider[] = [CreateAdService]; @@ -41,16 +39,15 @@ const repositories: Provider[] = [ }, ]; -const messageBrokers: Provider[] = [ +const messagePublishers: Provider[] = [ { - provide: MESSAGE_PUBLISHER, - useClass: MessageBrokerPublisher, + provide: AD_MESSAGE_PUBLISHER, + useExisting: MessageBrokerPublisher, }, ]; - const orms: Provider[] = [PrismaService]; -const utilities: Provider[] = [ +const adapters: Provider[] = [ { provide: PARAMS_PROVIDER, useClass: DefaultParamsProvider, @@ -74,9 +71,9 @@ const utilities: Provider[] = [ ...queryHandlers, ...mappers, ...repositories, - ...messageBrokers, + ...messagePublishers, ...orms, - ...utilities, + ...adapters, ], exports: [ PrismaService, diff --git a/src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts b/src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts deleted file mode 100644 index 14b801c..0000000 --- a/src/modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { OnEvent } from '@nestjs/event-emitter'; -import { MESSAGE_PUBLISHER } from '@src/app.constants'; -import { AdCreatedDomainEvent } from '../../domain/events/ad-created.domain-events'; -import { MessagePublisherPort } from '@mobicoop/ddd-library'; - -@Injectable() -export class PublishLogMessageWhenAdIsCreatedDomainEventHandler { - constructor( - @Inject(MESSAGE_PUBLISHER) - private readonly messagePublisher: MessagePublisherPort, - ) {} - - @OnEvent(AdCreatedDomainEvent.name, { async: true, promisify: true }) - async handle(event: AdCreatedDomainEvent): Promise { - this.messagePublisher.publish( - 'logging.ad.created.info', - JSON.stringify(event), - ); - } -} diff --git a/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts b/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts index 5b6c51d..70c0d7f 100644 --- a/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts +++ b/src/modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler.ts @@ -1,13 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; -import { MESSAGE_PUBLISHER } from '@src/app.constants'; import { AdCreatedDomainEvent } from '../../domain/events/ad-created.domain-events'; import { MessagePublisherPort } from '@mobicoop/ddd-library'; +import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens'; @Injectable() export class PublishMessageWhenAdIsCreatedDomainEventHandler { constructor( - @Inject(MESSAGE_PUBLISHER) + @Inject(AD_MESSAGE_PUBLISHER) private readonly messagePublisher: MessagePublisherPort, ) {} diff --git a/src/modules/ad/infrastructure/ad.repository.ts b/src/modules/ad/infrastructure/ad.repository.ts index 5ccc174..1ddfdfb 100644 --- a/src/modules/ad/infrastructure/ad.repository.ts +++ b/src/modules/ad/infrastructure/ad.repository.ts @@ -1,10 +1,15 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { AdEntity } from '../core/domain/ad.entity'; import { AdMapper } from '../ad.mapper'; import { AdRepositoryPort } from '../core/application/ports/ad.repository.port'; -import { PrismaRepositoryBase } from '@mobicoop/ddd-library'; +import { + LoggerBase, + MessagePublisherPort, + PrismaRepositoryBase, +} from '@mobicoop/ddd-library'; import { PrismaService } from './prisma.service'; +import { AD_MESSAGE_PUBLISHER } from '../ad.di-tokens'; export type AdBaseModel = { uuid: string; @@ -72,13 +77,19 @@ export class AdRepository prisma: PrismaService, mapper: AdMapper, eventEmitter: EventEmitter2, + @Inject(AD_MESSAGE_PUBLISHER) + protected readonly messagePublisher: MessagePublisherPort, ) { super( prisma.ad, prisma, mapper, eventEmitter, - new Logger(AdRepository.name), + new LoggerBase({ + logger: new Logger(AdRepository.name), + domain: 'ad', + messagePublisher, + }), ); } } diff --git a/src/modules/ad/tests/integration/ad.repository.spec.ts b/src/modules/ad/tests/integration/ad.repository.spec.ts index a5875e7..64d3f08 100644 --- a/src/modules/ad/tests/integration/ad.repository.spec.ts +++ b/src/modules/ad/tests/integration/ad.repository.spec.ts @@ -1,10 +1,17 @@ import { + AD_MESSAGE_PUBLISHER, AD_REPOSITORY, PARAMS_PROVIDER, TIMEZONE_FINDER, TIME_CONVERTER, } from '@modules/ad/ad.di-tokens'; import { AdMapper } from '@modules/ad/ad.mapper'; +import { AdEntity } from '@modules/ad/core/domain/ad.entity'; +import { + CreateAdProps, + DefaultAdProps, + Frequency, +} from '@modules/ad/core/domain/ad.types'; import { AdRepository } from '@modules/ad/infrastructure/ad.repository'; import { DefaultParamsProvider } from '@modules/ad/infrastructure/default-params-provider'; import { PrismaService } from '@modules/ad/infrastructure/prisma.service'; @@ -48,20 +55,6 @@ describe('Ad Repository', () => { seatsRequested: 0, 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 = { frequency: `'PUNCTUAL'`, fromDate: `'2023-01-01'`, @@ -81,25 +74,6 @@ describe('Ad Repository', () => { satMargin: 900, sunMargin: 900, }; - // const recurrentAd = { - // frequency: `'RECURRENT'`, - // fromDate: `'2023-01-01'`, - // toDate: `'2023-12-31'`, - // monTime: `'2023-01-01T07:15:00Z'`, - // tueTime: `'2023-01-01T07:15:00Z'`, - // wedTime: `'2023-01-01T07:05:00Z'`, - // thuTime: `'2023-01-01T07:15:00Z'`, - // friTime: `'2023-01-01T07:15:00Z'`, - // satTime: 'NULL', - // sunTime: 'NULL', - // monMargin: 900, - // tueMargin: 900, - // wedMargin: 900, - // thuMargin: 900, - // friMargin: 900, - // satMargin: 900, - // sunMargin: 900, - // }; const originWaypoint = { position: 0, lon: 43.7102, @@ -140,120 +114,15 @@ describe('Ad Repository', () => { } }; - // 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('waypoint', { - // uuid: getSeed(i, baseOriginWaypointUuid.uuid), - // adUuid: adToCreate.uuid, - // ...originWaypoint, - // }); - // await executeInsertCommand('waypoint', { - // uuid: getSeed(i, baseDestinationWaypointUuid.uuid), - // adUuid: adToCreate.uuid, - // ...destinationWaypoint, - // }); - // } - // }; + const mockMessagePublisher = { + publish: jest.fn().mockImplementation(), + }; - // const createPunctualPassengerAds = async (nbToCreate = 10) => { - // const adToCreate = { - // ...baseUuid, - // ...baseUserUuid, - // ...passengerAd, - // ...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 createRecurrentPassengerAds = async (nbToCreate = 10) => { - // const adToCreate = { - // ...baseUuid, - // ...baseUserUuid, - // ...passengerAd, - // ...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 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 mockLogger = { + log: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }; beforeAll(async () => { const module = await Test.createTestingModule({ @@ -280,11 +149,20 @@ describe('Ad Repository', () => { provide: TIME_CONVERTER, useClass: TimeConverter, }, + { + provide: AD_MESSAGE_PUBLISHER, + useValue: mockMessagePublisher, + }, ], - }).compile(); + }) + // disable logging + .setLogger(mockLogger) + .compile(); + prismaService = module.get(PrismaService); adRepository = module.get(AD_REPOSITORY); }); + afterAll(async () => { await prismaService.$disconnect(); }); @@ -292,129 +170,7 @@ describe('Ad Repository', () => { beforeEach(async () => { await prismaService.ad.deleteMany(); }); - // describe('findAll', () => { - // it('should return an empty data array', async () => { - // const res = await adRepository.findAll(); - // expect(res).toEqual({ - // data: [], - // total: 0, - // }); - // }); - // describe('drivers', () => { - // it('should return a data array with 8 punctual driver ads', async () => { - // await createPunctualDriverAds(8); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(8); - // expect(ads.total).toBe(8); - // expect(ads.data[0].driver).toBeTruthy(); - // expect(ads.data[0].passenger).toBeFalsy(); - // }); - - // it('should return a data array limited to 10 punctual driver ads', async () => { - // await createPunctualDriverAds(20); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(10); - // expect(ads.total).toBe(20); - // expect(ads.data[1].driver).toBeTruthy(); - // expect(ads.data[1].passenger).toBeFalsy(); - // }); - - // it('should return a data array with 8 recurrent driver ads', async () => { - // await createRecurrentDriverAds(8); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(8); - // expect(ads.total).toBe(8); - // expect(ads.data[2].driver).toBeTruthy(); - // expect(ads.data[2].passenger).toBeFalsy(); - // }); - - // it('should return a data array limited to 10 recurrent driver ads', async () => { - // await createRecurrentDriverAds(20); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(10); - // expect(ads.total).toBe(20); - // expect(ads.data[3].driver).toBeTruthy(); - // expect(ads.data[3].passenger).toBeFalsy(); - // }); - // }); - - // describe('passengers', () => { - // it('should return a data array with 7 punctual passenger ads', async () => { - // await createPunctualPassengerAds(7); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(7); - // expect(ads.total).toBe(7); - // expect(ads.data[0].passenger).toBeTruthy(); - // expect(ads.data[0].driver).toBeFalsy(); - // }); - - // it('should return a data array limited to 10 punctual passenger ads', async () => { - // await createPunctualPassengerAds(15); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(10); - // expect(ads.total).toBe(15); - // expect(ads.data[1].passenger).toBeTruthy(); - // expect(ads.data[1].driver).toBeFalsy(); - // }); - - // it('should return a data array with 7 recurrent passenger ads', async () => { - // await createRecurrentPassengerAds(7); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(7); - // expect(ads.total).toBe(7); - // expect(ads.data[2].passenger).toBeTruthy(); - // expect(ads.data[2].driver).toBeFalsy(); - // }); - - // it('should return a data array limited to 10 recurrent passenger ads', async () => { - // await createRecurrentPassengerAds(15); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(10); - // expect(ads.total).toBe(15); - // expect(ads.data[3].passenger).toBeTruthy(); - // expect(ads.data[3].driver).toBeFalsy(); - // }); - // }); - - // describe('drivers and passengers', () => { - // it('should return a data array with 6 punctual driver and passenger ads', async () => { - // await createPunctualDriverPassengerAds(6); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(6); - // expect(ads.total).toBe(6); - // expect(ads.data[0].passenger).toBeTruthy(); - // expect(ads.data[0].driver).toBeTruthy(); - // }); - - // it('should return a data array limited to 10 punctual driver and passenger ads', async () => { - // await createPunctualDriverPassengerAds(16); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(10); - // expect(ads.total).toBe(16); - // expect(ads.data[1].passenger).toBeTruthy(); - // expect(ads.data[1].driver).toBeTruthy(); - // }); - - // it('should return a data array with 6 recurrent driver and passenger ads', async () => { - // await createRecurrentDriverPassengerAds(6); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(6); - // expect(ads.total).toBe(6); - // expect(ads.data[2].passenger).toBeTruthy(); - // expect(ads.data[2].driver).toBeTruthy(); - // }); - - // it('should return a data array limited to 10 recurrent driver and passenger ads', async () => { - // await createRecurrentDriverPassengerAds(16); - // const ads = await adRepository.findAll(); - // expect(ads.data.length).toBe(10); - // expect(ads.total).toBe(16); - // expect(ads.data[3].passenger).toBeTruthy(); - // expect(ads.data[3].driver).toBeTruthy(); - // }); - // }); - // }); describe('findOneById', () => { it('should return an ad', async () => { await createPunctualDriverAds(1); @@ -424,80 +180,97 @@ describe('Ad Repository', () => { expect(result.id).toBe(baseUuid.uuid); }); + }); - // it('should return null', async () => { - // const ad = await adRepository.findOneById( - // '544572be-11fb-4244-8235-587221fc9104', - // ); - // expect(ad).toBeNull(); + describe('create', () => { + it('should create an ad', async () => { + const beforeCount = await prismaService.ad.count(); + + const createAdProps: CreateAdProps = { + userId: 'b4b56444-f8d3-4110-917c-e37bba77f383', + driver: true, + passenger: false, + frequency: Frequency.PUNCTUAL, + fromDate: '2023-02-01', + toDate: '2023-02-01', + schedule: { + wed: '12:05', + }, + marginDurations: { + wed: 900, + }, + seatsProposed: 3, + seatsRequested: 1, + strict: false, + waypoints: [ + { + position: 0, + address: { + locality: 'Nice', + postalCode: '06000', + country: 'France', + coordinates: { + lon: 43.7102, + lat: 7.262, + }, + }, + }, + { + position: 1, + address: { + locality: 'Marseille', + postalCode: '13000', + country: 'France', + coordinates: { + lon: 43.2965, + lat: 5.3698, + }, + }, + }, + ], + }; + + 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 adToCreate: AdEntity = AdEntity.create( + createAdProps, + defaultAdProps, + ); + await adRepository.insert(adToCreate); + + const afterCount = await prismaService.ad.count(); + + expect(afterCount - beforeCount).toBe(1); + }); + + // it('should throw a UniqueConstraintException if ad already exists', async () => { + // await prismaService.ad.create({ + // data: { + // uuid: uuid, + // password: bcrypt.hashSync(`password`, 10), + // }, + // }); + + // const authenticationToCreate: AuthenticationEntity = + // await AuthenticationEntity.create(createAuthenticationProps); + // await expect( + // authenticationRepository.insert(authenticationToCreate), + // ).rejects.toBeInstanceOf(UniqueConstraintException); // }); }); - // describe('create', () => { - // it('should create a punctual ad', async () => { - // const beforeCount = await prismaService.ad.count(); - // const adToCreate: AdDTO = new AdDTO(); - - // adToCreate.uuid = 'be459a29-7a41-4c0b-b371-abe90bfb6f00'; - // adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200'; - // adToCreate.driver = true; - // adToCreate.passenger = false; - // adToCreate.frequency = Frequency.PUNCTUAL; - // adToCreate.fromDate = new Date('05-22-2023 09:36'); - // adToCreate.toDate = new Date('05-22-2023 09:36'); - // adToCreate.monTime = '09:36'; - // adToCreate.monMargin = 900; - // adToCreate.tueMargin = 900; - // adToCreate.wedMargin = 900; - // adToCreate.thuMargin = 900; - // adToCreate.friMargin = 900; - // adToCreate.satMargin = 900; - // adToCreate.sunMargin = 900; - // adToCreate.seatsProposed = 3; - // adToCreate.seatsRequested = 0; - // adToCreate.strict = false; - // adToCreate.waypoints = { - // create: [originWaypoint as Waypoint, destinationWaypoint as Waypoint], - // }; - // const ad = await adRepository.create(adToCreate); - - // const afterCount = await prismaService.ad.count(); - - // expect(afterCount - beforeCount).toBe(1); - // expect(ad.uuid).toBe('be459a29-7a41-4c0b-b371-abe90bfb6f00'); - // }); - // it('should create a recurrent ad', async () => { - // const beforeCount = await prismaService.ad.count(); - // const adToCreate: AdDTO = new AdDTO(); - - // adToCreate.uuid = '137a26fa-4b38-48ba-aecf-1a75f6b20f3d'; - // adToCreate.userUuid = '4e52b54d-a729-4dbd-9283-f84a11bb2200'; - // adToCreate.driver = true; - // adToCreate.passenger = false; - // adToCreate.frequency = Frequency.RECURRENT; - // adToCreate.fromDate = new Date('01-15-2023 '); - // adToCreate.toDate = new Date('10-31-2023'); - // adToCreate.monTime = '07:30'; - // adToCreate.friTime = '07:45'; - // adToCreate.thuTime = '08:00'; - // adToCreate.monMargin = 900; - // adToCreate.tueMargin = 900; - // adToCreate.wedMargin = 900; - // adToCreate.thuMargin = 900; - // adToCreate.friMargin = 900; - // adToCreate.satMargin = 900; - // adToCreate.sunMargin = 900; - // adToCreate.seatsProposed = 2; - // adToCreate.seatsRequested = 0; - // adToCreate.strict = false; - // adToCreate.waypoints = { - // create: [originWaypoint as Waypoint, destinationWaypoint as Waypoint], - // }; - // const ad = await adRepository.create(adToCreate); - - // const afterCount = await prismaService.ad.count(); - - // expect(afterCount - beforeCount).toBe(1); - // expect(ad.uuid).toBe('137a26fa-4b38-48ba-aecf-1a75f6b20f3d'); - // }); - // }); }); diff --git a/src/modules/ad/tests/unit/core/publish-log-message-when-ad-is-created.domain-event-handler.spec.ts b/src/modules/ad/tests/unit/core/publish-log-message-when-ad-is-created.domain-event-handler.spec.ts deleted file mode 100644 index ec25078..0000000 --- a/src/modules/ad/tests/unit/core/publish-log-message-when-ad-is-created.domain-event-handler.spec.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Frequency } from '@modules/ad/core/domain/ad.types'; -import { PublishLogMessageWhenAdIsCreatedDomainEventHandler } from '@modules/ad/core/application/event-handlers/publish-log-message-when-ad-is-created.domain-event-handler'; -import { AdCreatedDomainEvent } from '@modules/ad/core/domain/events/ad-created.domain-events'; -import { Test, TestingModule } from '@nestjs/testing'; -import { MESSAGE_PUBLISHER } from '@src/app.constants'; - -const mockMessagePublisher = { - publish: jest.fn().mockImplementation(), -}; - -describe('Publish log message when ad is created domain event handler', () => { - let publishLogMessageWhenAdIsCreatedDomainEventHandler: PublishLogMessageWhenAdIsCreatedDomainEventHandler; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: MESSAGE_PUBLISHER, - useValue: mockMessagePublisher, - }, - PublishLogMessageWhenAdIsCreatedDomainEventHandler, - ], - }).compile(); - - publishLogMessageWhenAdIsCreatedDomainEventHandler = - module.get( - PublishLogMessageWhenAdIsCreatedDomainEventHandler, - ); - }); - - it('should publish a log message', () => { - jest.spyOn(mockMessagePublisher, 'publish'); - const adCreatedDomainEvent: AdCreatedDomainEvent = { - id: 'some-domain-event-id', - aggregateId: 'some-aggregate-id', - userId: 'some-user-id', - driver: false, - passenger: true, - frequency: Frequency.PUNCTUAL, - fromDate: '2023-06-28', - toDate: '2023-06-28', - monTime: undefined, - tueTime: undefined, - wedTime: '07:15', - thuTime: undefined, - friTime: undefined, - satTime: undefined, - sunTime: undefined, - monMarginDuration: 900, - tueMarginDuration: 900, - wedMarginDuration: 900, - thuMarginDuration: 900, - friMarginDuration: 900, - satMarginDuration: 900, - sunMarginDuration: 900, - seatsProposed: 3, - seatsRequested: 1, - strict: false, - waypoints: [ - { - position: 0, - houseNumber: '5', - street: 'Avenue Foch', - locality: 'Nancy', - postalCode: '54000', - country: 'France', - lat: 48.689445, - lon: 6.1765102, - }, - { - position: 1, - locality: 'Paris', - postalCode: '75000', - country: 'France', - lat: 48.8566, - lon: 2.3522, - }, - ], - metadata: { - timestamp: new Date('2023-06-28T05:00:00Z').getTime(), - correlationId: 'some-correlation-id', - }, - }; - publishLogMessageWhenAdIsCreatedDomainEventHandler.handle( - adCreatedDomainEvent, - ); - expect(publishLogMessageWhenAdIsCreatedDomainEventHandler).toBeDefined(); - expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1); - expect(mockMessagePublisher.publish).toHaveBeenCalledWith( - 'logging.ad.created.info', - '{"id":"some-domain-event-id","aggregateId":"some-aggregate-id","userId":"some-user-id","driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-06-28","toDate":"2023-06-28","wedTime":"07:15","monMarginDuration":900,"tueMarginDuration":900,"wedMarginDuration":900,"thuMarginDuration":900,"friMarginDuration":900,"satMarginDuration":900,"sunMarginDuration":900,"seatsProposed":3,"seatsRequested":1,"strict":false,"waypoints":[{"position":0,"houseNumber":"5","street":"Avenue Foch","locality":"Nancy","postalCode":"54000","country":"France","lat":48.689445,"lon":6.1765102},{"position":1,"locality":"Paris","postalCode":"75000","country":"France","lat":48.8566,"lon":2.3522}],"metadata":{"timestamp":1687928400000,"correlationId":"some-correlation-id"}}', - ); - }); -}); diff --git a/src/modules/ad/tests/unit/core/publish-message-when-ad-is-created.domain-event-handler.spec.ts b/src/modules/ad/tests/unit/core/publish-message-when-ad-is-created.domain-event-handler.spec.ts index 12909b8..79f667e 100644 --- a/src/modules/ad/tests/unit/core/publish-message-when-ad-is-created.domain-event-handler.spec.ts +++ b/src/modules/ad/tests/unit/core/publish-message-when-ad-is-created.domain-event-handler.spec.ts @@ -2,7 +2,7 @@ import { Frequency } from '@modules/ad/core/domain/ad.types'; import { PublishMessageWhenAdIsCreatedDomainEventHandler } from '@modules/ad/core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler'; import { AdCreatedDomainEvent } from '@modules/ad/core/domain/events/ad-created.domain-events'; import { Test, TestingModule } from '@nestjs/testing'; -import { MESSAGE_PUBLISHER } from '@src/app.constants'; +import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens'; const mockMessagePublisher = { publish: jest.fn().mockImplementation(), @@ -15,7 +15,7 @@ describe('Publish message when ad is created domain event handler', () => { const module: TestingModule = await Test.createTestingModule({ providers: [ { - provide: MESSAGE_PUBLISHER, + provide: AD_MESSAGE_PUBLISHER, useValue: mockMessagePublisher, }, PublishMessageWhenAdIsCreatedDomainEventHandler, diff --git a/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts b/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts index 3f79692..d082f8e 100644 --- a/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts +++ b/src/modules/ad/tests/unit/infrastructure/ad.repository.spec.ts @@ -50,6 +50,10 @@ const mockTimeConverter: TimeConverterPort = { utcDatetimeToLocalTime: jest.fn(), }; +const mockMessagePublisher = { + publish: jest.fn().mockImplementation(), +}; + describe('Ad repository', () => { let prismaService: PrismaService; let adMapper: AdMapper; @@ -82,7 +86,12 @@ describe('Ad repository', () => { }); it('should be defined', () => { expect( - new AdRepository(prismaService, adMapper, eventEmitter), + new AdRepository( + prismaService, + adMapper, + eventEmitter, + mockMessagePublisher, + ), ).toBeDefined(); }); }); diff --git a/src/modules/health/core/application/ports/check-repository.port.ts b/src/modules/health/core/application/ports/check-repository.port.ts deleted file mode 100644 index 64d8980..0000000 --- a/src/modules/health/core/application/ports/check-repository.port.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface CheckRepositoryPort { - healthCheck(): Promise; -} diff --git a/src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts b/src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts deleted file mode 100644 index 34aa245..0000000 --- a/src/modules/health/core/application/usecases/repositories.health-indicator.usecase.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { - HealthCheckError, - HealthCheckResult, - HealthIndicator, - HealthIndicatorResult, -} from '@nestjs/terminus'; -import { CheckRepositoryPort } from '../ports/check-repository.port'; -import { AD_REPOSITORY } from '@modules/health/health.di-tokens'; -import { AdRepositoryPort } from '@modules/ad/core/application/ports/ad.repository.port'; -import { MESSAGE_PUBLISHER } from '@src/app.constants'; -import { LOGGING_AD_HEALTH_CRIT } from '@modules/health/health.constants'; -import { MessagePublisherPort } from '@mobicoop/ddd-library'; - -@Injectable() -export class RepositoriesHealthIndicatorUseCase extends HealthIndicator { - private _checkRepositories: CheckRepositoryPort[]; - constructor( - @Inject(AD_REPOSITORY) - private readonly adRepository: AdRepositoryPort, - @Inject(MESSAGE_PUBLISHER) - private readonly messagePublisher: MessagePublisherPort, - ) { - super(); - this._checkRepositories = [adRepository]; - } - isHealthy = async (key: string): Promise => { - try { - await Promise.all( - this._checkRepositories.map( - async (checkRepository: CheckRepositoryPort) => { - await checkRepository.healthCheck(); - }, - ), - ); - return this.getStatus(key, true); - } catch (error) { - const healthCheckResult: HealthCheckResult = error; - this.messagePublisher.publish( - LOGGING_AD_HEALTH_CRIT, - JSON.stringify(healthCheckResult.error), - ); - throw new HealthCheckError('Repository', { - repository: error.message, - }); - } - }; -} diff --git a/src/modules/health/health.constants.ts b/src/modules/health/health.constants.ts deleted file mode 100644 index 3f29432..0000000 --- a/src/modules/health/health.constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const LOGGING_AD_HEALTH_CRIT = 'logging.ad.health.crit'; diff --git a/src/modules/health/health.di-tokens.ts b/src/modules/health/health.di-tokens.ts deleted file mode 100644 index 2706306..0000000 --- a/src/modules/health/health.di-tokens.ts +++ /dev/null @@ -1 +0,0 @@ -export const AD_REPOSITORY = Symbol('AD_REPOSITORY'); diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts deleted file mode 100644 index fe1565a..0000000 --- a/src/modules/health/health.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Module, Provider } from '@nestjs/common'; -import { HealthHttpController } from './interface/http-controllers/health.http.controller'; -import { TerminusModule } from '@nestjs/terminus'; -import { MESSAGE_PUBLISHER } from 'src/app.constants'; -import { RepositoriesHealthIndicatorUseCase } from './core/application/usecases/repositories.health-indicator.usecase'; -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'; -import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; - -const grpcControllers = [HealthGrpcController]; - -const httpControllers = [HealthHttpController]; - -const useCases: Provider[] = [RepositoriesHealthIndicatorUseCase]; - -const repositories: Provider[] = [ - { - provide: AD_REPOSITORY, - useClass: AdRepository, - }, -]; - -const messageBrokers: Provider[] = [ - { - provide: MESSAGE_PUBLISHER, - useClass: MessageBrokerPublisher, - }, -]; - -@Module({ - imports: [TerminusModule, AdModule], - controllers: [...grpcControllers, ...httpControllers], - providers: [...useCases, ...repositories, ...messageBrokers], -}) -export class HealthModule {} diff --git a/src/modules/health/interface/grpc-controllers/health.grpc.controller.ts b/src/modules/health/interface/grpc-controllers/health.grpc.controller.ts deleted file mode 100644 index a016ee4..0000000 --- a/src/modules/health/interface/grpc-controllers/health.grpc.controller.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Controller } from '@nestjs/common'; -import { GrpcMethod } from '@nestjs/microservices'; -import { RepositoriesHealthIndicatorUseCase } from '../../core/application/usecases/repositories.health-indicator.usecase'; - -export enum ServingStatus { - UNKNOWN = 0, - SERVING = 1, - NOT_SERVING = 2, -} - -interface HealthCheckRequest { - service: string; -} - -interface HealthCheckResponse { - status: ServingStatus; -} - -@Controller() -export class HealthGrpcController { - constructor( - private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase, - ) {} - - @GrpcMethod('Health', 'Check') - async check( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - data?: HealthCheckRequest, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - metadata?: any, - ): Promise { - const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy( - 'repositories', - ); - return { - status: - healthCheck['repositories'].status == 'up' - ? ServingStatus.SERVING - : ServingStatus.NOT_SERVING, - }; - } -} diff --git a/src/modules/health/interface/grpc-controllers/health.proto b/src/modules/health/interface/grpc-controllers/health.proto deleted file mode 100644 index 74e1a4c..0000000 --- a/src/modules/health/interface/grpc-controllers/health.proto +++ /dev/null @@ -1,21 +0,0 @@ -syntax = "proto3"; - -package health; - - -service Health { - rpc Check(HealthCheckRequest) returns (HealthCheckResponse); -} - -message HealthCheckRequest { - string service = 1; -} - -message HealthCheckResponse { - enum ServingStatus { - UNKNOWN = 0; - SERVING = 1; - NOT_SERVING = 2; - } - ServingStatus status = 1; -} diff --git a/src/modules/health/interface/http-controllers/health.http.controller.ts b/src/modules/health/interface/http-controllers/health.http.controller.ts deleted file mode 100644 index 32428e9..0000000 --- a/src/modules/health/interface/http-controllers/health.http.controller.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase'; -import { Controller, Get } from '@nestjs/common'; -import { - HealthCheckService, - HealthCheck, - HealthCheckResult, -} from '@nestjs/terminus'; - -@Controller('health') -export class HealthHttpController { - constructor( - private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase, - private readonly healthCheckService: HealthCheckService, - ) {} - - @Get() - @HealthCheck() - async check(): Promise { - try { - return await this.healthCheckService.check([ - async () => - this.repositoriesHealthIndicatorUseCase.isHealthy('repositories'), - ]); - } catch (error) { - throw error; - } - } -} diff --git a/src/modules/health/tests/unit/health.grpc.controller.spec.ts b/src/modules/health/tests/unit/health.grpc.controller.spec.ts deleted file mode 100644 index 25434ad..0000000 --- a/src/modules/health/tests/unit/health.grpc.controller.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase'; -import { - HealthGrpcController, - ServingStatus, -} from '@modules/health/interface/grpc-controllers/health.grpc.controller'; -import { Test, TestingModule } from '@nestjs/testing'; - -const mockRepositoriesHealthIndicatorUseCase = { - isHealthy: jest - .fn() - .mockImplementationOnce(() => ({ - repositories: { - status: 'up', - }, - })) - .mockImplementationOnce(() => ({ - repositories: { - status: 'down', - }, - })), -}; - -describe('Health Grpc Controller', () => { - let healthGrpcController: HealthGrpcController; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: RepositoriesHealthIndicatorUseCase, - useValue: mockRepositoriesHealthIndicatorUseCase, - }, - HealthGrpcController, - ], - }).compile(); - - healthGrpcController = - module.get(HealthGrpcController); - }); - - afterEach(async () => { - jest.clearAllMocks(); - }); - - it('should be defined', () => { - expect(healthGrpcController).toBeDefined(); - }); - - it('should return a Serving status ', async () => { - jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); - const servingStatus: { status: ServingStatus } = - await healthGrpcController.check(); - expect(servingStatus).toEqual({ - status: ServingStatus.SERVING, - }); - expect( - mockRepositoriesHealthIndicatorUseCase.isHealthy, - ).toHaveBeenCalledTimes(1); - }); - - it('should return a Not Serving status ', async () => { - jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); - const servingStatus: { status: ServingStatus } = - await healthGrpcController.check(); - expect(servingStatus).toEqual({ - status: ServingStatus.NOT_SERVING, - }); - expect( - mockRepositoriesHealthIndicatorUseCase.isHealthy, - ).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/modules/health/tests/unit/health.http.controller.spec.ts b/src/modules/health/tests/unit/health.http.controller.spec.ts deleted file mode 100644 index 3fe40bb..0000000 --- a/src/modules/health/tests/unit/health.http.controller.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { RepositoriesHealthIndicatorUseCase } from '@modules/health/core/application/usecases/repositories.health-indicator.usecase'; -import { HealthHttpController } from '@modules/health/interface/http-controllers/health.http.controller'; -import { HealthCheckResult, HealthCheckService } from '@nestjs/terminus'; -import { Test, TestingModule } from '@nestjs/testing'; - -const mockHealthCheckService = { - check: jest - .fn() - .mockImplementationOnce(() => ({ - status: 'ok', - info: { - repositories: { - status: 'up', - }, - }, - error: {}, - details: { - repositories: { - status: 'up', - }, - }, - })) - .mockImplementationOnce(() => ({ - status: 'error', - info: {}, - error: { - repository: - "\nInvalid `prisma.$queryRaw()` invocation:\n\n\nCan't reach database server at `v3-db`:`5432`\n\nPlease make sure your database server is running at `v3-db`:`5432`.", - }, - details: { - repository: - "\nInvalid `prisma.$queryRaw()` invocation:\n\n\nCan't reach database server at `v3-db`:`5432`\n\nPlease make sure your database server is running at `v3-db`:`5432`.", - }, - })), -}; - -const mockRepositoriesHealthIndicatorUseCase = { - isHealthy: jest.fn(), -}; - -describe('Health Http Controller', () => { - let healthHttpController: HealthHttpController; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: HealthCheckService, - useValue: mockHealthCheckService, - }, - { - provide: RepositoriesHealthIndicatorUseCase, - useValue: mockRepositoriesHealthIndicatorUseCase, - }, - HealthHttpController, - ], - }).compile(); - - healthHttpController = - module.get(HealthHttpController); - }); - - afterEach(async () => { - jest.clearAllMocks(); - }); - - it('should be defined', () => { - expect(healthHttpController).toBeDefined(); - }); - - it('should return an HealthCheckResult with Ok status ', async () => { - jest.spyOn(mockHealthCheckService, 'check'); - jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); - - const healthCheckResult: HealthCheckResult = - await healthHttpController.check(); - expect(healthCheckResult.status).toBe('ok'); - expect(mockHealthCheckService.check).toHaveBeenCalledTimes(1); - }); - - it('should return an HealthCheckResult with Error status ', async () => { - jest.spyOn(mockHealthCheckService, 'check'); - jest.spyOn(mockRepositoriesHealthIndicatorUseCase, 'isHealthy'); - - const healthCheckResult: HealthCheckResult = - await healthHttpController.check(); - expect(healthCheckResult.status).toBe('error'); - expect(mockHealthCheckService.check).toHaveBeenCalledTimes(1); - }); -}); 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 deleted file mode 100644 index 340d558..0000000 --- a/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus'; -import { RepositoriesHealthIndicatorUseCase } from '../../core/application/usecases/repositories.health-indicator.usecase'; -import { AD_REPOSITORY } from '@modules/health/health.di-tokens'; -import { MESSAGE_PUBLISHER } from '@src/app.constants'; -import { DatabaseErrorException } from '@mobicoop/ddd-library'; - -const mockAdRepository = { - healthCheck: jest - .fn() - .mockImplementationOnce(() => { - return Promise.resolve(true); - }) - .mockImplementation(() => { - throw new DatabaseErrorException('An error occured in the database'); - }), -}; - -const mockMessagePublisher = { - publish: jest.fn().mockImplementation(), -}; - -describe('RepositoriesHealthIndicatorUseCase', () => { - let repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - RepositoriesHealthIndicatorUseCase, - { - provide: AD_REPOSITORY, - useValue: mockAdRepository, - }, - { - provide: MESSAGE_PUBLISHER, - useValue: mockMessagePublisher, - }, - ], - }).compile(); - - repositoriesHealthIndicatorUseCase = - module.get( - RepositoriesHealthIndicatorUseCase, - ); - }); - - it('should be defined', () => { - expect(repositoriesHealthIndicatorUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should check health successfully', async () => { - const healthIndicatorResult: HealthIndicatorResult = - await repositoriesHealthIndicatorUseCase.isHealthy('repositories'); - expect(healthIndicatorResult['repositories'].status).toBe('up'); - }); - - 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); - }); - }); -}); diff --git a/src/modules/messager/messager.di-tokens.ts b/src/modules/messager/messager.di-tokens.ts new file mode 100644 index 0000000..48e8011 --- /dev/null +++ b/src/modules/messager/messager.di-tokens.ts @@ -0,0 +1 @@ +export const MESSAGE_PUBLISHER = Symbol('MESSAGE_PUBLISHER'); diff --git a/src/modules/messager/messager.module.ts b/src/modules/messager/messager.module.ts new file mode 100644 index 0000000..a70717a --- /dev/null +++ b/src/modules/messager/messager.module.ts @@ -0,0 +1,36 @@ +import { Module, Provider } from '@nestjs/common'; +import { MESSAGE_PUBLISHER } from './messager.di-tokens'; +import { + MessageBrokerModule, + MessageBrokerModuleOptions, + MessageBrokerPublisher, +} from '@mobicoop/message-broker-module'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +const imports = [ + MessageBrokerModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async ( + configService: ConfigService, + ): Promise => ({ + uri: configService.get('MESSAGE_BROKER_URI'), + exchange: configService.get('MESSAGE_BROKER_EXCHANGE'), + name: 'ad', + }), + }), +]; + +const providers: Provider[] = [ + { + provide: MESSAGE_PUBLISHER, + useClass: MessageBrokerPublisher, + }, +]; + +@Module({ + imports, + providers, + exports: [MESSAGE_PUBLISHER], +}) +export class MessagerModule {}