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