mirror of
https://gitlab.com/mobicoop/v3/service/auth.git
synced 2026-03-24 14:45:48 +00:00
authorization module
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import {
|
||||
HealthCheckService,
|
||||
HealthCheck,
|
||||
HealthCheckResult,
|
||||
} from '@nestjs/terminus';
|
||||
import { Messager } from '../secondaries/messager';
|
||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||
|
||||
// this controller responds to rest GET /health
|
||||
@Controller('health')
|
||||
export class HealthController {
|
||||
constructor(
|
||||
private readonly prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase,
|
||||
private healthCheckService: HealthCheckService,
|
||||
private messager: Messager,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@HealthCheck()
|
||||
async check() {
|
||||
try {
|
||||
return await this.healthCheckService.check([
|
||||
async () => this.prismaHealthIndicatorUseCase.isHealthy('prisma'),
|
||||
]);
|
||||
} catch (error) {
|
||||
const healthCheckResult: HealthCheckResult = error.response;
|
||||
this.messager.publish(
|
||||
'logging.auth.health.crit',
|
||||
JSON.stringify(healthCheckResult.error),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export abstract class IMessageBroker {
|
||||
exchange: string;
|
||||
|
||||
constructor(exchange: string) {
|
||||
this.exchange = exchange;
|
||||
}
|
||||
|
||||
abstract publish(routingKey: string, message: string): void;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { IMessageBroker } from './message-broker';
|
||||
|
||||
@Injectable()
|
||||
export class Messager extends IMessageBroker {
|
||||
constructor(
|
||||
private readonly amqpConnection: AmqpConnection,
|
||||
configService: ConfigService,
|
||||
) {
|
||||
super(configService.get<string>('RMQ_EXCHANGE'));
|
||||
}
|
||||
|
||||
publish = (routingKey: string, message: string): void => {
|
||||
this.amqpConnection.publish(this.exchange, routingKey, message);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface CheckRepositoryPort {
|
||||
healthCheck(): Promise<boolean>;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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,25 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
HealthCheckError,
|
||||
HealthIndicator,
|
||||
HealthIndicatorResult,
|
||||
} from '@nestjs/terminus';
|
||||
import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaHealthIndicatorUseCase extends HealthIndicator {
|
||||
constructor(private readonly repository: AuthenticationRepository) {
|
||||
super();
|
||||
}
|
||||
|
||||
isHealthy = async (key: string): Promise<HealthIndicatorResult> => {
|
||||
try {
|
||||
await this.repository.healthCheck();
|
||||
return this.getStatus(key, true);
|
||||
} catch (e) {
|
||||
throw new HealthCheckError('Prisma', {
|
||||
prisma: e.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
1
src/modules/health/health.constants.ts
Normal file
1
src/modules/health/health.constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const LOGGING_AUTHENTICATION_HEALTH_CRIT = 'logging.auth.health.crit';
|
||||
2
src/modules/health/health.di-tokens.ts
Normal file
2
src/modules/health/health.di-tokens.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY');
|
||||
export const USERNAME_REPOSITORY = Symbol('USERNAME_REPOSITORY');
|
||||
@@ -1,34 +1,45 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HealthServerController } from './adapters/primaries/health-server.controller';
|
||||
import { PrismaHealthIndicatorUseCase } from './domain/usecases/prisma.health-indicator.usecase';
|
||||
import { AuthenticationRepository } from '../oldauthentication/adapters/secondaries/authentication.repository';
|
||||
import { DatabaseModule } from '../database/database.module';
|
||||
import { HealthController } from './adapters/primaries/health.controller';
|
||||
import { Module, Provider } from '@nestjs/common';
|
||||
import { HealthHttpController } from './interface/http-controllers/health.http.controller';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { Messager } from './adapters/secondaries/messager';
|
||||
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,
|
||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
exchanges: [
|
||||
{
|
||||
name: configService.get<string>('RMQ_EXCHANGE'),
|
||||
type: 'topic',
|
||||
},
|
||||
],
|
||||
uri: configService.get<string>('RMQ_URI'),
|
||||
connectionInitOptions: { wait: false },
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
DatabaseModule,
|
||||
],
|
||||
controllers: [HealthServerController, HealthController],
|
||||
providers: [PrismaHealthIndicatorUseCase, AuthenticationRepository, Messager],
|
||||
imports: [TerminusModule, AuthenticationModule],
|
||||
controllers: [...grpcControllers, ...httpControllers],
|
||||
providers: [...useCases, ...repositories, ...messageBrokers],
|
||||
})
|
||||
export class HealthModule {}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { GrpcMethod } from '@nestjs/microservices';
|
||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||
import { RepositoriesHealthIndicatorUseCase } from '../../core/application/usecases/repositories.health-indicator.usecase';
|
||||
|
||||
enum ServingStatus {
|
||||
export enum ServingStatus {
|
||||
UNKNOWN = 0,
|
||||
SERVING = 1,
|
||||
NOT_SERVING = 2,
|
||||
@@ -16,26 +16,25 @@ interface HealthCheckResponse {
|
||||
status: ServingStatus;
|
||||
}
|
||||
|
||||
// this controller responds to gRPC health check service
|
||||
@Controller()
|
||||
export class HealthServerController {
|
||||
export class HealthGrpcController {
|
||||
constructor(
|
||||
private readonly prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase,
|
||||
private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase,
|
||||
) {}
|
||||
|
||||
@GrpcMethod('Health', 'Check')
|
||||
async check(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
data: HealthCheckRequest,
|
||||
data?: HealthCheckRequest,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
metadata: any,
|
||||
metadata?: any,
|
||||
): Promise<HealthCheckResponse> {
|
||||
const healthCheck = await this.prismaHealthIndicatorUseCase.isHealthy(
|
||||
'prisma',
|
||||
const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy(
|
||||
'repositories',
|
||||
);
|
||||
return {
|
||||
status:
|
||||
healthCheck['prisma'].status == 'up'
|
||||
healthCheck['repositories'].status == 'up'
|
||||
? ServingStatus.SERVING
|
||||
: ServingStatus.NOT_SERVING,
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
72
src/modules/health/tests/unit/health.grpc.controller.spec.ts
Normal file
72
src/modules/health/tests/unit/health.grpc.controller.spec.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
90
src/modules/health/tests/unit/health.http.controller.spec.ts
Normal file
90
src/modules/health/tests/unit/health.http.controller.spec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
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,47 +0,0 @@
|
||||
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Messager } from '../../adapters/secondaries/messager';
|
||||
|
||||
const mockAmqpConnection = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockConfigService = {
|
||||
get: jest.fn().mockResolvedValue({
|
||||
RMQ_EXCHANGE: 'mobicoop',
|
||||
}),
|
||||
};
|
||||
|
||||
describe('Messager', () => {
|
||||
let messager: Messager;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
Messager,
|
||||
{
|
||||
provide: AmqpConnection,
|
||||
useValue: mockAmqpConnection,
|
||||
},
|
||||
{
|
||||
provide: ConfigService,
|
||||
useValue: mockConfigService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
messager = module.get<Messager>(Messager);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(messager).toBeDefined();
|
||||
});
|
||||
|
||||
it('should publish a message', async () => {
|
||||
jest.spyOn(mockAmqpConnection, 'publish');
|
||||
messager.publish('test.create.info', 'my-test');
|
||||
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||
import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository';
|
||||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime';
|
||||
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
||||
|
||||
const mockAuthenticationRepository = {
|
||||
healthCheck: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => {
|
||||
return Promise.resolve(true);
|
||||
})
|
||||
.mockImplementation(() => {
|
||||
throw new PrismaClientKnownRequestError('Service unavailable', {
|
||||
code: 'code',
|
||||
clientVersion: 'version',
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
describe('PrismaHealthIndicatorUseCase', () => {
|
||||
let prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AuthenticationRepository,
|
||||
useValue: mockAuthenticationRepository,
|
||||
},
|
||||
PrismaHealthIndicatorUseCase,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
prismaHealthIndicatorUseCase = module.get<PrismaHealthIndicatorUseCase>(
|
||||
PrismaHealthIndicatorUseCase,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(prismaHealthIndicatorUseCase).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
it('should check health successfully', async () => {
|
||||
const healthIndicatorResult: HealthIndicatorResult =
|
||||
await prismaHealthIndicatorUseCase.isHealthy('prisma');
|
||||
|
||||
expect(healthIndicatorResult['prisma'].status).toBe('up');
|
||||
});
|
||||
|
||||
it('should throw an error if database is unavailable', async () => {
|
||||
await expect(
|
||||
prismaHealthIndicatorUseCase.isHealthy('prisma'),
|
||||
).rejects.toBeInstanceOf(HealthCheckError);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user