use health package
This commit is contained in:
parent
c7fda32694
commit
e6f3a660af
File diff suppressed because it is too large
Load Diff
|
@ -37,6 +37,7 @@
|
|||
"@grpc/grpc-js": "^1.8.0",
|
||||
"@grpc/proto-loader": "^0.7.4",
|
||||
"@mobicoop/ddd-library": "^0.3.0",
|
||||
"@mobicoop/health-module": "^1.1.0",
|
||||
"@mobicoop/message-broker-module": "^1.2.0",
|
||||
"@nestjs/axios": "^1.0.1",
|
||||
"@nestjs/common": "^9.0.0",
|
||||
|
|
|
@ -6,8 +6,16 @@ import {
|
|||
MessageBrokerModule,
|
||||
MessageBrokerModuleOptions,
|
||||
} from '@mobicoop/message-broker-module';
|
||||
import { HealthModule } from '@modules/health/health.module';
|
||||
import { HealthModule, HealthModuleOptions } from '@mobicoop/health-module';
|
||||
import { AuthorizationModule } from '@modules/authorization/authorization.module';
|
||||
import {
|
||||
AUTHENTICATION_REPOSITORY,
|
||||
USERNAME_REPOSITORY,
|
||||
} from '@modules/authentication/authentication.di-tokens';
|
||||
import { MESSAGE_PUBLISHER } from './modules/message-publisher/message-publisher.di-tokens';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
import { ICheckRepository } from '@mobicoop/health-module/dist/core/domain/types/health.types';
|
||||
import { MessagePublisherModule } from './modules/message-publisher/message-publisher.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -34,11 +42,29 @@ import { AuthorizationModule } from '@modules/authorization/authorization.module
|
|||
},
|
||||
}),
|
||||
}),
|
||||
HealthModule.forRootAsync({
|
||||
imports: [AuthenticationModule, MessagePublisherModule],
|
||||
inject: [
|
||||
AUTHENTICATION_REPOSITORY,
|
||||
USERNAME_REPOSITORY,
|
||||
MESSAGE_PUBLISHER,
|
||||
],
|
||||
useFactory: async (
|
||||
authenticationRepository: ICheckRepository,
|
||||
usernameRepository: ICheckRepository,
|
||||
messagePublisher: MessagePublisherPort,
|
||||
): Promise<HealthModuleOptions> => ({
|
||||
serviceName: 'auth',
|
||||
criticalLoggingKey: 'logging.auth.health.crit',
|
||||
checkRepositories: [authenticationRepository, usernameRepository],
|
||||
messagePublisher,
|
||||
}),
|
||||
}),
|
||||
AuthenticationModule,
|
||||
AuthorizationModule,
|
||||
HealthModule,
|
||||
MessagePublisherModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
exports: [AuthenticationModule, MessagePublisherModule],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
@ -21,10 +21,7 @@ async function bootstrap() {
|
|||
__dirname,
|
||||
'modules/authorization/interface/grpc-controllers/authorization.proto',
|
||||
),
|
||||
join(
|
||||
__dirname,
|
||||
'modules/health/interface/grpc-controllers/health.proto',
|
||||
),
|
||||
join(__dirname, 'health.proto'),
|
||||
],
|
||||
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
|
||||
loader: { keepCase: true, enums: String },
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export const AUTH_MESSAGE_PUBLISHER = Symbol('AUTH_MESSAGE_PUBLISHER');
|
||||
export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY');
|
||||
export const USERNAME_REPOSITORY = Symbol('USERNAME_REPOSITORY');
|
||||
|
|
|
@ -3,6 +3,7 @@ import { CreateAuthenticationGrpcController } from './interface/grpc-controllers
|
|||
import { CreateAuthenticationService } from './core/application/commands/create-authentication/create-authentication.service';
|
||||
import { AuthenticationMapper } from './authentication.mapper';
|
||||
import {
|
||||
AUTH_MESSAGE_PUBLISHER,
|
||||
AUTHENTICATION_REPOSITORY,
|
||||
USERNAME_REPOSITORY,
|
||||
} from './authentication.di-tokens';
|
||||
|
@ -11,7 +12,6 @@ import { PrismaService } from './infrastructure/prisma.service';
|
|||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
import { DeleteAuthenticationGrpcController } from './interface/grpc-controllers/delete-authentication.grpc.controller';
|
||||
import { DeleteAuthenticationService } from './core/application/commands/delete-authentication/delete-authentication.service';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
import { UsernameRepository } from './infrastructure/username.repository';
|
||||
import { UsernameMapper } from './username.mapper';
|
||||
|
@ -64,10 +64,10 @@ const repositories: Provider[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const messageBrokers: Provider[] = [
|
||||
const messagePublishers: Provider[] = [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useClass: MessageBrokerPublisher,
|
||||
provide: AUTH_MESSAGE_PUBLISHER,
|
||||
useExisting: MessageBrokerPublisher,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -82,7 +82,7 @@ const orms: Provider[] = [PrismaService];
|
|||
...queryHandlers,
|
||||
...mappers,
|
||||
...repositories,
|
||||
...messageBrokers,
|
||||
...messagePublishers,
|
||||
...orms,
|
||||
],
|
||||
exports: [
|
||||
|
|
|
@ -9,7 +9,7 @@ import { AuthenticationEntity } from '../core/domain/authentication.entity';
|
|||
import { AuthenticationRepositoryPort } from '../core/application/ports/authentication.repository.port';
|
||||
import { PrismaService } from './prisma.service';
|
||||
import { AuthenticationMapper } from '../authentication.mapper';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import { AUTH_MESSAGE_PUBLISHER } from '../authentication.di-tokens';
|
||||
|
||||
type AuthenticationBaseModel = {
|
||||
uuid: string;
|
||||
|
@ -49,7 +49,7 @@ export class AuthenticationRepository
|
|||
prisma: PrismaService,
|
||||
mapper: AuthenticationMapper,
|
||||
eventEmitter: EventEmitter2,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
@Inject(AUTH_MESSAGE_PUBLISHER)
|
||||
protected readonly messagePublisher: MessagePublisherPort,
|
||||
) {
|
||||
super(
|
||||
|
|
|
@ -6,11 +6,11 @@ import {
|
|||
PrismaRepositoryBase,
|
||||
} from '@mobicoop/ddd-library';
|
||||
import { PrismaService } from './prisma.service';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import { UsernameEntity } from '../core/domain/username.entity';
|
||||
import { UsernameRepositoryPort } from '../core/application/ports/username.repository.port';
|
||||
import { UsernameMapper } from '../username.mapper';
|
||||
import { Type } from '../core/domain/username.types';
|
||||
import { AUTH_MESSAGE_PUBLISHER } from '../authentication.di-tokens';
|
||||
|
||||
type UsernameBaseModel = {
|
||||
username: string;
|
||||
|
@ -41,7 +41,7 @@ export class UsernameRepository
|
|||
prisma: PrismaService,
|
||||
mapper: UsernameMapper,
|
||||
eventEmitter: EventEmitter2,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
@Inject(AUTH_MESSAGE_PUBLISHER)
|
||||
protected readonly messagePublisher: MessagePublisherPort,
|
||||
) {
|
||||
super(
|
||||
|
|
|
@ -5,7 +5,6 @@ import { PrismaService } from '@modules/authentication/infrastructure/prisma.ser
|
|||
import { AuthenticationRepository } from '@modules/authentication/infrastructure/authentication.repository';
|
||||
import { AuthenticationMapper } from '@modules/authentication/authentication.mapper';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import {
|
||||
DatabaseErrorException,
|
||||
NotFoundException,
|
||||
|
@ -14,6 +13,7 @@ import {
|
|||
import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity';
|
||||
import { Type } from '@modules/authentication/core/domain/username.types';
|
||||
import { CreateAuthenticationProps } from '@modules/authentication/core/domain/authentication.types';
|
||||
import { AUTH_MESSAGE_PUBLISHER } from '@modules/authentication/authentication.di-tokens';
|
||||
|
||||
const uuid = '165192d4-398a-4469-a16b-98c02cc6f531';
|
||||
|
||||
|
@ -42,17 +42,6 @@ describe('AuthenticationRepository', () => {
|
|||
let prismaService: PrismaService;
|
||||
let authenticationRepository: AuthenticationRepository;
|
||||
|
||||
// const createAuthentications = async (nbToCreate = 10) => {
|
||||
// for (let i = 0; i < nbToCreate; i++) {
|
||||
// await prismaService.auth.create({
|
||||
// data: {
|
||||
// uuid: v4(),
|
||||
// password: bcrypt.hashSync(`password-${i}`, 10),
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [EventEmitterModule.forRoot()],
|
||||
|
@ -61,7 +50,7 @@ describe('AuthenticationRepository', () => {
|
|||
PrismaService,
|
||||
AuthenticationMapper,
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
provide: AUTH_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
],
|
||||
|
@ -84,30 +73,6 @@ describe('AuthenticationRepository', () => {
|
|||
await prismaService.auth.deleteMany();
|
||||
});
|
||||
|
||||
// describe('findAll', () => {
|
||||
// it('should return an empty data array', async () => {
|
||||
// const res = await authenticationRepository.findAll();
|
||||
// expect(res).toEqual({
|
||||
// data: [],
|
||||
// total: 0,
|
||||
// });
|
||||
// });
|
||||
|
||||
// it('should return a data array with 8 auths', async () => {
|
||||
// await createAuthentications(8);
|
||||
// const auths = await authenticationRepository.findAll();
|
||||
// expect(auths.data.length).toBe(8);
|
||||
// expect(auths.total).toBe(8);
|
||||
// });
|
||||
|
||||
// it('should return a data array limited to 10 authentications', async () => {
|
||||
// await createAuthentications(20);
|
||||
// const auths = await authenticationRepository.findAll();
|
||||
// expect(auths.data.length).toBe(10);
|
||||
// expect(auths.total).toBe(20);
|
||||
// });
|
||||
// });
|
||||
|
||||
describe('findOneById', () => {
|
||||
it('should return an authentication', async () => {
|
||||
const authToFind = await prismaService.auth.create({
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { TestingModule, Test } from '@nestjs/testing';
|
||||
import { PrismaService } from '@modules/authentication/infrastructure/prisma.service';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import {
|
||||
DatabaseErrorException,
|
||||
NotFoundException,
|
||||
|
@ -15,6 +14,7 @@ import { UsernameRepository } from '@modules/authentication/infrastructure/usern
|
|||
import { UsernameMapper } from '@modules/authentication/username.mapper';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { UsernameEntity } from '@modules/authentication/core/domain/username.entity';
|
||||
import { AUTH_MESSAGE_PUBLISHER } from '@modules/authentication/authentication.di-tokens';
|
||||
|
||||
const authUuid = 'a4524d22-7be3-46cd-8444-3145470476dc';
|
||||
|
||||
|
@ -46,7 +46,7 @@ describe('UsernameRepository', () => {
|
|||
PrismaService,
|
||||
UsernameMapper,
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
provide: AUTH_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
export interface CheckRepositoryPort {
|
||||
healthCheck(): Promise<boolean>;
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
HealthCheckError,
|
||||
HealthCheckResult,
|
||||
HealthIndicator,
|
||||
HealthIndicatorResult,
|
||||
} from '@nestjs/terminus';
|
||||
import { CheckRepositoryPort } from '../ports/check-repository.port';
|
||||
import {
|
||||
AUTHENTICATION_REPOSITORY,
|
||||
USERNAME_REPOSITORY,
|
||||
} from '@modules/health/health.di-tokens';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import { LOGGING_AUTHENTICATION_HEALTH_CRIT } from '@modules/health/health.constants';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
import { AuthenticationRepositoryPort } from '@modules/authentication/core/application/ports/authentication.repository.port';
|
||||
import { UsernameRepositoryPort } from '@modules/authentication/core/application/ports/username.repository.port';
|
||||
|
||||
@Injectable()
|
||||
export class RepositoriesHealthIndicatorUseCase extends HealthIndicator {
|
||||
private _checkRepositories: CheckRepositoryPort[];
|
||||
constructor(
|
||||
@Inject(AUTHENTICATION_REPOSITORY)
|
||||
private readonly authenticationRepository: AuthenticationRepositoryPort,
|
||||
@Inject(USERNAME_REPOSITORY)
|
||||
private readonly usernameRepository: UsernameRepositoryPort,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: MessagePublisherPort,
|
||||
) {
|
||||
super();
|
||||
this._checkRepositories = [authenticationRepository, usernameRepository];
|
||||
}
|
||||
isHealthy = async (key: string): Promise<HealthIndicatorResult> => {
|
||||
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_AUTHENTICATION_HEALTH_CRIT,
|
||||
JSON.stringify(healthCheckResult.error),
|
||||
);
|
||||
throw new HealthCheckError('Repository', {
|
||||
repository: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export const LOGGING_AUTHENTICATION_HEALTH_CRIT = 'logging.auth.health.crit';
|
|
@ -1,2 +0,0 @@
|
|||
export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY');
|
||||
export const USERNAME_REPOSITORY = Symbol('USERNAME_REPOSITORY');
|
|
@ -1,45 +0,0 @@
|
|||
import { Module, Provider } from '@nestjs/common';
|
||||
import { HealthHttpController } from './interface/http-controllers/health.http.controller';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { RepositoriesHealthIndicatorUseCase } from './core/application/usecases/repositories.health-indicator.usecase';
|
||||
import { HealthGrpcController } from './interface/grpc-controllers/health.grpc.controller';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
import { AuthenticationRepository } from '@modules/authentication/infrastructure/authentication.repository';
|
||||
import {
|
||||
AUTHENTICATION_REPOSITORY,
|
||||
USERNAME_REPOSITORY,
|
||||
} from './health.di-tokens';
|
||||
import { UsernameRepository } from '@modules/authentication/infrastructure/username.repository';
|
||||
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import { AuthenticationModule } from '@modules/authentication/authentication.module';
|
||||
|
||||
const grpcControllers = [HealthGrpcController];
|
||||
|
||||
const httpControllers = [HealthHttpController];
|
||||
|
||||
const useCases: Provider[] = [RepositoriesHealthIndicatorUseCase];
|
||||
|
||||
const repositories: Provider[] = [
|
||||
{
|
||||
provide: AUTHENTICATION_REPOSITORY,
|
||||
useClass: AuthenticationRepository,
|
||||
},
|
||||
{
|
||||
provide: USERNAME_REPOSITORY,
|
||||
useClass: UsernameRepository,
|
||||
},
|
||||
];
|
||||
|
||||
const messageBrokers: Provider[] = [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useClass: MessageBrokerPublisher,
|
||||
},
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports: [TerminusModule, AuthenticationModule],
|
||||
controllers: [...grpcControllers, ...httpControllers],
|
||||
providers: [...useCases, ...repositories, ...messageBrokers],
|
||||
})
|
||||
export class HealthModule {}
|
|
@ -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<HealthCheckResponse> {
|
||||
const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy(
|
||||
'repositories',
|
||||
);
|
||||
return {
|
||||
status:
|
||||
healthCheck['repositories'].status == 'up'
|
||||
? ServingStatus.SERVING
|
||||
: ServingStatus.NOT_SERVING,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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<HealthCheckResult> {
|
||||
try {
|
||||
return await this.healthCheckService.check([
|
||||
async () =>
|
||||
this.repositoriesHealthIndicatorUseCase.isHealthy('repositories'),
|
||||
]);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>(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);
|
||||
});
|
||||
});
|
|
@ -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>(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);
|
||||
});
|
||||
});
|
|
@ -1,84 +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 { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
|
||||
import { DatabaseErrorException } from '@mobicoop/ddd-library';
|
||||
import {
|
||||
AUTHENTICATION_REPOSITORY,
|
||||
USERNAME_REPOSITORY,
|
||||
} from '@modules/health/health.di-tokens';
|
||||
|
||||
const mockAuthenticationRepository = {
|
||||
healthCheck: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.mockImplementation(() => {
|
||||
throw new DatabaseErrorException('An error occured in the database');
|
||||
}),
|
||||
};
|
||||
|
||||
const mockUsernameRepository = {
|
||||
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: AUTHENTICATION_REPOSITORY,
|
||||
useValue: mockAuthenticationRepository,
|
||||
},
|
||||
{
|
||||
provide: USERNAME_REPOSITORY,
|
||||
useValue: mockUsernameRepository,
|
||||
},
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
repositoriesHealthIndicatorUseCase =
|
||||
module.get<RepositoriesHealthIndicatorUseCase>(
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Module, Provider } from '@nestjs/common';
|
||||
import { MESSAGE_PUBLISHER } from './message-publisher.di-tokens';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
|
||||
const messagePublishers: Provider[] = [
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useClass: MessageBrokerPublisher,
|
||||
},
|
||||
];
|
||||
|
||||
@Module({
|
||||
providers: [...messagePublishers],
|
||||
exports: [MESSAGE_PUBLISHER],
|
||||
})
|
||||
export class MessagePublisherModule {}
|
Loading…
Reference in New Issue