mirror of
https://gitlab.com/mobicoop/v3/service/ad.git
synced 2026-01-09 21:42:41 +00:00
move tests folder to the root
This commit is contained in:
314
tests/integration/ad/ad.repository.spec.ts
Normal file
314
tests/integration/ad/ad.repository.spec.ts
Normal file
@@ -0,0 +1,314 @@
|
||||
import {
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
OUTPUT_DATETIME_TRANSFORMER,
|
||||
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, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||
import { OutputDateTimeTransformer } from '@modules/ad/infrastructure/output-datetime-transformer';
|
||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||
import { TimeConverter } from '@modules/ad/infrastructure/time-converter';
|
||||
import { TimezoneFinder } from '@modules/ad/infrastructure/timezone-finder';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
describe('Ad Repository', () => {
|
||||
let prismaService: PrismaService;
|
||||
let adRepository: AdRepository;
|
||||
|
||||
const executeInsertCommand = async (table: string, object: any) => {
|
||||
const command = `INSERT INTO ${table} ("${Object.keys(object).join(
|
||||
'","',
|
||||
)}") VALUES (${Object.values(object).join(',')})`;
|
||||
|
||||
await prismaService.$executeRawUnsafe(command);
|
||||
};
|
||||
const getSeed = (index: number, uuid: string): string => {
|
||||
return `'${uuid.slice(0, -2)}${index.toString(16).padStart(2, '0')}'`;
|
||||
};
|
||||
|
||||
const baseUuid = {
|
||||
uuid: 'be459a29-7a41-4c0b-b371-abe90bfb6f00',
|
||||
};
|
||||
const baseScheduleUuid = {
|
||||
uuid: 'bad5e786-3b15-4e51-a8fc-926fa9327ff1',
|
||||
};
|
||||
const baseOriginWaypointUuid = {
|
||||
uuid: 'bad5e786-3b15-4e51-a8fc-926fa9327ff1',
|
||||
};
|
||||
const baseDestinationWaypointUuid = {
|
||||
uuid: '4d200eb6-7389-487f-a1ca-dbc0e40381c9',
|
||||
};
|
||||
const baseUserUuid = {
|
||||
userUuid: "'113e0000-0000-4000-a000-000000000000'",
|
||||
};
|
||||
const driverAd = {
|
||||
driver: 'true',
|
||||
passenger: 'false',
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: 'false',
|
||||
};
|
||||
const punctualAd = {
|
||||
frequency: `'PUNCTUAL'`,
|
||||
fromDate: `'2023-01-01'`,
|
||||
toDate: `'2023-01-01'`,
|
||||
};
|
||||
const schedulePunctualAd = {
|
||||
day: 0,
|
||||
time: `'07:00'`,
|
||||
margin: 900,
|
||||
};
|
||||
const originWaypoint = {
|
||||
position: 0,
|
||||
lat: 43.7102,
|
||||
lon: 7.262,
|
||||
locality: "'Nice'",
|
||||
postalCode: "'06000'",
|
||||
country: "'France'",
|
||||
};
|
||||
const destinationWaypoint = {
|
||||
position: 1,
|
||||
lat: 43.2965,
|
||||
lon: 5.3698,
|
||||
locality: "'Marseille'",
|
||||
postalCode: "'13000'",
|
||||
country: "'France'",
|
||||
};
|
||||
|
||||
const createPunctualDriverAds = 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('schedule_item', {
|
||||
uuid: getSeed(i, baseScheduleUuid.uuid),
|
||||
adUuid: adToCreate.uuid,
|
||||
...schedulePunctualAd,
|
||||
});
|
||||
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 mockLogger = {
|
||||
log: jest.fn(),
|
||||
warn: jest.fn(),
|
||||
error: jest.fn(),
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
EventEmitterModule.forRoot(),
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
],
|
||||
providers: [
|
||||
PrismaService,
|
||||
AdMapper,
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useClass: AdRepository,
|
||||
},
|
||||
{
|
||||
provide: AD_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
{
|
||||
provide: TIMEZONE_FINDER,
|
||||
useClass: TimezoneFinder,
|
||||
},
|
||||
{
|
||||
provide: TIME_CONVERTER,
|
||||
useClass: TimeConverter,
|
||||
},
|
||||
{
|
||||
provide: OUTPUT_DATETIME_TRANSFORMER,
|
||||
useClass: OutputDateTimeTransformer,
|
||||
},
|
||||
],
|
||||
})
|
||||
// disable logging
|
||||
.setLogger(mockLogger)
|
||||
.compile();
|
||||
|
||||
prismaService = module.get<PrismaService>(PrismaService);
|
||||
adRepository = module.get<AdRepository>(AD_REPOSITORY);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prismaService.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await prismaService.ad.deleteMany();
|
||||
});
|
||||
|
||||
describe('findOneById', () => {
|
||||
it('should return an ad', async () => {
|
||||
await createPunctualDriverAds(1);
|
||||
const result = await adRepository.findOneById(baseUuid.uuid, {
|
||||
waypoints: true,
|
||||
schedule: true,
|
||||
});
|
||||
|
||||
expect(result.id).toBe(baseUuid.uuid);
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should create a punctual 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: [
|
||||
{
|
||||
day: 3,
|
||||
time: '12:05',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
address: {
|
||||
locality: 'Nice',
|
||||
postalCode: '06000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 43.7102,
|
||||
lon: 7.262,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Marseille',
|
||||
postalCode: '13000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 43.2965,
|
||||
lon: 5.3698,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const adToCreate: AdEntity = AdEntity.create(createAdProps);
|
||||
await adRepository.insert(adToCreate);
|
||||
|
||||
const afterCount = await prismaService.ad.count();
|
||||
|
||||
expect(afterCount - beforeCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should create a recurrent ad', async () => {
|
||||
const beforeCount = await prismaService.ad.count();
|
||||
|
||||
const createAdProps: CreateAdProps = {
|
||||
userId: 'b4b56444-f8d3-4110-917c-e37bba77f383',
|
||||
driver: true,
|
||||
passenger: false,
|
||||
frequency: Frequency.RECURRENT,
|
||||
fromDate: '2023-02-01',
|
||||
toDate: '2024-01-31',
|
||||
schedule: [
|
||||
{
|
||||
day: 1,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 2,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 3,
|
||||
time: '09:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 4,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
{
|
||||
day: 5,
|
||||
time: '08:00',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
address: {
|
||||
locality: 'Nice',
|
||||
postalCode: '06000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 43.7102,
|
||||
lon: 7.262,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Marseille',
|
||||
postalCode: '13000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 43.2965,
|
||||
lon: 5.3698,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const adToCreate: AdEntity = AdEntity.create(createAdProps);
|
||||
await adRepository.insert(adToCreate);
|
||||
|
||||
const afterCount = await prismaService.ad.count();
|
||||
|
||||
expect(afterCount - beforeCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
166
tests/unit/ad/ad.mapper.spec.ts
Normal file
166
tests/unit/ad/ad.mapper.spec.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import { OUTPUT_DATETIME_TRANSFORMER } from '@modules/ad/ad.di-tokens';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { Frequency, Status } from '@modules/ad/core/domain/ad.types';
|
||||
import {
|
||||
AdReadModel,
|
||||
AdWriteModel,
|
||||
} from '@modules/ad/infrastructure/ad.repository';
|
||||
import { AdResponseDto } from '@modules/ad/interface/dtos/ad.response.dto';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
const now = new Date('2023-06-21 06:00:00');
|
||||
const adEntity: AdEntity = new AdEntity({
|
||||
id: 'c160cf8c-f057-4962-841f-3ad68346df44',
|
||||
props: {
|
||||
userId: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e',
|
||||
driver: false,
|
||||
passenger: true,
|
||||
status: Status.PENDING,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-06-21',
|
||||
toDate: '2023-06-21',
|
||||
schedule: [
|
||||
{
|
||||
day: 3,
|
||||
time: '07:15',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
address: {
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.1765102,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
strict: false,
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
},
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
const adReadModel: AdReadModel = {
|
||||
uuid: 'c160cf8c-f057-4962-841f-3ad68346df44',
|
||||
userUuid: '7ca2490b-d04d-4ac5-8d6c-5c416fab922e',
|
||||
driver: false,
|
||||
passenger: true,
|
||||
status: Status.PENDING,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: new Date('2023-06-21'),
|
||||
toDate: new Date('2023-06-21'),
|
||||
schedule: [
|
||||
{
|
||||
uuid: '3978f3d6-560f-4a8f-83ba-9bf5aa9a2d27',
|
||||
day: 3,
|
||||
time: new Date('2023-06-21T07:05:00Z'),
|
||||
margin: 900,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
],
|
||||
waypoints: [
|
||||
{
|
||||
uuid: '6f53f55e-2bdb-4c23-b6a9-6d7b498e47b9',
|
||||
position: 0,
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
lat: 48.689445,
|
||||
lon: 6.1765102,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
{
|
||||
uuid: 'e18c6a84-0ab7-4e44-af1d-829d0b0d0573',
|
||||
position: 1,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
],
|
||||
strict: false,
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
const mockOutputDatetimeTransformer: DateTimeTransformerPort = {
|
||||
fromDate: jest.fn(),
|
||||
toDate: jest.fn(),
|
||||
day: jest.fn(),
|
||||
time: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Ad Mapper', () => {
|
||||
let adMapper: AdMapper;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
AdMapper,
|
||||
{
|
||||
provide: OUTPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockOutputDatetimeTransformer,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
adMapper = module.get<AdMapper>(AdMapper);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(adMapper).toBeDefined();
|
||||
});
|
||||
|
||||
it('should map domain entity to persistence data', async () => {
|
||||
const mapped: AdWriteModel = adMapper.toPersistence(adEntity);
|
||||
expect(mapped.waypoints?.create[0].uuid.length).toBe(36);
|
||||
expect(mapped.waypoints?.create[1].uuid.length).toBe(36);
|
||||
expect(mapped.schedule?.create.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should map persisted data to domain entity', async () => {
|
||||
const mapped: AdEntity = adMapper.toDomain(adReadModel);
|
||||
expect(mapped.getProps().waypoints[0].address.coordinates.lat).toBe(
|
||||
48.689445,
|
||||
);
|
||||
expect(mapped.getProps().waypoints[1].address.coordinates.lon).toBe(2.3522);
|
||||
expect(mapped.getProps().schedule.length).toBe(1);
|
||||
expect(mapped.getProps().schedule[0].time).toBe('07:05');
|
||||
});
|
||||
|
||||
it('should map domain entity to response', async () => {
|
||||
const mapped: AdResponseDto = adMapper.toResponse(adEntity);
|
||||
expect(mapped.id).toBe('c160cf8c-f057-4962-841f-3ad68346df44');
|
||||
});
|
||||
});
|
||||
228
tests/unit/ad/core/ad.entity.spec.ts
Normal file
228
tests/unit/ad/core/ad.entity.spec.ts
Normal file
@@ -0,0 +1,228 @@
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import {
|
||||
CreateAdProps,
|
||||
Frequency,
|
||||
Status,
|
||||
} from '@modules/ad/core/domain/ad.types';
|
||||
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
|
||||
const originWaypointProps: WaypointProps = {
|
||||
position: 0,
|
||||
address: {
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
},
|
||||
};
|
||||
const destinationWaypointProps: WaypointProps = {
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const baseCreateAdProps = {
|
||||
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [originWaypointProps, destinationWaypointProps],
|
||||
};
|
||||
const punctualCreateAdProps = {
|
||||
fromDate: '2023-06-21',
|
||||
toDate: '2023-06-21',
|
||||
schedule: [
|
||||
{
|
||||
day: 3,
|
||||
time: '08:30',
|
||||
margin: 600,
|
||||
},
|
||||
],
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
};
|
||||
const recurrentCreateAdProps = {
|
||||
fromDate: '2023-06-21',
|
||||
toDate: '2024-06-20',
|
||||
schedule: [
|
||||
{
|
||||
day: 1,
|
||||
time: '08:30',
|
||||
margin: 600,
|
||||
},
|
||||
{
|
||||
day: 2,
|
||||
time: '08:30',
|
||||
margin: 600,
|
||||
},
|
||||
{
|
||||
day: 3,
|
||||
time: '08:00',
|
||||
margin: 600,
|
||||
},
|
||||
{
|
||||
day: 4,
|
||||
time: '08:30',
|
||||
margin: 600,
|
||||
},
|
||||
{
|
||||
day: 5,
|
||||
time: '08:30',
|
||||
margin: 600,
|
||||
},
|
||||
],
|
||||
frequency: Frequency.RECURRENT,
|
||||
};
|
||||
const punctualPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...punctualCreateAdProps,
|
||||
driver: false,
|
||||
passenger: true,
|
||||
};
|
||||
const recurrentPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...recurrentCreateAdProps,
|
||||
driver: false,
|
||||
passenger: true,
|
||||
};
|
||||
const punctualDriverCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...punctualCreateAdProps,
|
||||
driver: true,
|
||||
passenger: false,
|
||||
};
|
||||
const recurrentDriverCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...recurrentCreateAdProps,
|
||||
driver: true,
|
||||
passenger: false,
|
||||
};
|
||||
const punctualDriverPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...punctualCreateAdProps,
|
||||
driver: true,
|
||||
passenger: true,
|
||||
};
|
||||
const recurrentDriverPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...recurrentCreateAdProps,
|
||||
driver: true,
|
||||
passenger: true,
|
||||
};
|
||||
|
||||
describe('Ad entity create', () => {
|
||||
describe('With complete props', () => {
|
||||
it('should create a new punctual passenger ad entity', async () => {
|
||||
const punctualPassengerAd: AdEntity = AdEntity.create(
|
||||
punctualPassengerCreateAdProps,
|
||||
);
|
||||
expect(punctualPassengerAd.id.length).toBe(36);
|
||||
expect(punctualPassengerAd.getProps().status).toBe(Status.PENDING);
|
||||
expect(punctualPassengerAd.getProps().schedule.length).toBe(1);
|
||||
expect(punctualPassengerAd.getProps().schedule[0].day).toBe(3);
|
||||
expect(punctualPassengerAd.getProps().schedule[0].time).toBe('08:30');
|
||||
expect(punctualPassengerAd.getProps().driver).toBeFalsy();
|
||||
expect(punctualPassengerAd.getProps().passenger).toBeTruthy();
|
||||
});
|
||||
it('should create a new punctual driver ad entity', async () => {
|
||||
const punctualDriverAd: AdEntity = AdEntity.create(
|
||||
punctualDriverCreateAdProps,
|
||||
);
|
||||
expect(punctualDriverAd.id.length).toBe(36);
|
||||
expect(punctualDriverAd.getProps().schedule.length).toBe(1);
|
||||
expect(punctualDriverAd.getProps().schedule[0].day).toBe(3);
|
||||
expect(punctualDriverAd.getProps().schedule[0].time).toBe('08:30');
|
||||
expect(punctualDriverAd.getProps().driver).toBeTruthy();
|
||||
expect(punctualDriverAd.getProps().passenger).toBeFalsy();
|
||||
});
|
||||
it('should create a new punctual driver and passenger ad entity', async () => {
|
||||
const punctualDriverPassengerAd: AdEntity = AdEntity.create(
|
||||
punctualDriverPassengerCreateAdProps,
|
||||
);
|
||||
expect(punctualDriverPassengerAd.id.length).toBe(36);
|
||||
expect(punctualDriverPassengerAd.getProps().schedule.length).toBe(1);
|
||||
expect(punctualDriverPassengerAd.getProps().schedule[0].day).toBe(3);
|
||||
expect(punctualDriverPassengerAd.getProps().schedule[0].time).toBe(
|
||||
'08:30',
|
||||
);
|
||||
expect(punctualDriverPassengerAd.getProps().driver).toBeTruthy();
|
||||
expect(punctualDriverPassengerAd.getProps().passenger).toBeTruthy();
|
||||
});
|
||||
it('should create a new recurrent passenger ad entity', async () => {
|
||||
const recurrentPassengerAd: AdEntity = AdEntity.create(
|
||||
recurrentPassengerCreateAdProps,
|
||||
);
|
||||
expect(recurrentPassengerAd.id.length).toBe(36);
|
||||
expect(recurrentPassengerAd.getProps().schedule.length).toBe(5);
|
||||
expect(recurrentPassengerAd.getProps().schedule[0].day).toBe(1);
|
||||
expect(recurrentPassengerAd.getProps().schedule[2].time).toBe('08:00');
|
||||
expect(recurrentPassengerAd.getProps().driver).toBeFalsy();
|
||||
expect(recurrentPassengerAd.getProps().passenger).toBeTruthy();
|
||||
});
|
||||
it('should create a new recurrent driver ad entity', async () => {
|
||||
const recurrentDriverAd: AdEntity = AdEntity.create(
|
||||
recurrentDriverCreateAdProps,
|
||||
);
|
||||
expect(recurrentDriverAd.id.length).toBe(36);
|
||||
expect(recurrentDriverAd.getProps().schedule.length).toBe(5);
|
||||
expect(recurrentDriverAd.getProps().schedule[1].day).toBe(2);
|
||||
expect(recurrentDriverAd.getProps().schedule[0].time).toBe('08:30');
|
||||
expect(recurrentDriverAd.getProps().driver).toBeTruthy();
|
||||
expect(recurrentDriverAd.getProps().passenger).toBeFalsy();
|
||||
});
|
||||
it('should create a new recurrent driver and passenger ad entity', async () => {
|
||||
const recurrentDriverPassengerAd: AdEntity = AdEntity.create(
|
||||
recurrentDriverPassengerCreateAdProps,
|
||||
);
|
||||
expect(recurrentDriverPassengerAd.id.length).toBe(36);
|
||||
expect(recurrentDriverPassengerAd.getProps().schedule.length).toBe(5);
|
||||
expect(recurrentDriverPassengerAd.getProps().schedule[3].day).toBe(4);
|
||||
expect(recurrentDriverPassengerAd.getProps().schedule[4].time).toBe(
|
||||
'08:30',
|
||||
);
|
||||
expect(recurrentDriverPassengerAd.getProps().driver).toBeTruthy();
|
||||
expect(recurrentDriverPassengerAd.getProps().passenger).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ad entity validate status', () => {
|
||||
it('should validate status of a pending ad entity', async () => {
|
||||
const punctualPassengerAd: AdEntity = AdEntity.create(
|
||||
punctualPassengerCreateAdProps,
|
||||
);
|
||||
punctualPassengerAd.valid();
|
||||
expect(punctualPassengerAd.getProps().status).toBe(Status.VALID);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ad entity invalidate status', () => {
|
||||
it('should invalidate status of a pending ad entity', async () => {
|
||||
const punctualPassengerAd: AdEntity = AdEntity.create(
|
||||
punctualPassengerCreateAdProps,
|
||||
);
|
||||
punctualPassengerAd.invalid();
|
||||
expect(punctualPassengerAd.getProps().status).toBe(Status.INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Ad entity suspend status', () => {
|
||||
it('should suspend status of a pending ad entity', async () => {
|
||||
const punctualPassengerAd: AdEntity = AdEntity.create(
|
||||
punctualPassengerCreateAdProps,
|
||||
);
|
||||
punctualPassengerAd.suspend();
|
||||
expect(punctualPassengerAd.getProps().status).toBe(Status.SUSPENDED);
|
||||
});
|
||||
});
|
||||
24
tests/unit/ad/core/address.value-object.spec.ts
Normal file
24
tests/unit/ad/core/address.value-object.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Address } from '@modules/ad/core/domain/value-objects/address.value-object';
|
||||
|
||||
describe('Address value object', () => {
|
||||
it('should create an address value object', () => {
|
||||
const addressVO = new Address({
|
||||
houseNumber: '5',
|
||||
street: 'rue de la monnaie',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
});
|
||||
expect(addressVO.houseNumber).toBe('5');
|
||||
expect(addressVO.street).toBe('rue de la monnaie');
|
||||
expect(addressVO.locality).toBe('Nancy');
|
||||
expect(addressVO.postalCode).toBe('54000');
|
||||
expect(addressVO.country).toBe('France');
|
||||
expect(addressVO.coordinates.lat).toBe(48.689445);
|
||||
expect(addressVO.name).toBeUndefined();
|
||||
});
|
||||
});
|
||||
12
tests/unit/ad/core/coordinates.value-object.spec.ts
Normal file
12
tests/unit/ad/core/coordinates.value-object.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Coordinates } from '@modules/ad/core/domain/value-objects/coordinates.value-object';
|
||||
|
||||
describe('Coordinates value object', () => {
|
||||
it('should create a coordinates value object', () => {
|
||||
const coordinatesVO = new Coordinates({
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
});
|
||||
expect(coordinatesVO.lat).toBe(48.689445);
|
||||
expect(coordinatesVO.lon).toBe(6.17651);
|
||||
});
|
||||
});
|
||||
126
tests/unit/ad/core/create-ad.service.spec.ts
Normal file
126
tests/unit/ad/core/create-ad.service.spec.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import {
|
||||
AD_REPOSITORY,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
} from '@modules/ad/ad.di-tokens';
|
||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||
import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto';
|
||||
import { AggregateID } from '@mobicoop/ddd-library';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { ConflictException } from '@mobicoop/ddd-library';
|
||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { CreateAdService } from '@modules/ad/core/application/commands/create-ad/create-ad.service';
|
||||
import { CreateAdCommand } from '@modules/ad/core/application/commands/create-ad/create-ad.command';
|
||||
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
|
||||
const originWaypoint: WaypointDto = {
|
||||
position: 0,
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
};
|
||||
const destinationWaypoint: WaypointDto = {
|
||||
position: 1,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
};
|
||||
const punctualCreateAdRequest: CreateAdRequestDto = {
|
||||
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
||||
fromDate: '2023-12-21',
|
||||
toDate: '2023-12-21',
|
||||
schedule: [
|
||||
{
|
||||
time: '08:15',
|
||||
margin: 900,
|
||||
day: 4,
|
||||
},
|
||||
],
|
||||
driver: true,
|
||||
passenger: true,
|
||||
seatsRequested: 1,
|
||||
seatsProposed: 3,
|
||||
strict: false,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
waypoints: [originWaypoint, destinationWaypoint],
|
||||
};
|
||||
|
||||
const mockAdRepository = {
|
||||
insert: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => ({}))
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error();
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
throw new ConflictException('already exists');
|
||||
}),
|
||||
};
|
||||
|
||||
const mockInputDateTimeTransformer: DateTimeTransformerPort = {
|
||||
fromDate: jest.fn(),
|
||||
toDate: jest.fn(),
|
||||
day: jest.fn(),
|
||||
time: jest.fn(),
|
||||
};
|
||||
|
||||
describe('create-ad.service', () => {
|
||||
let createAdService: CreateAdService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useValue: mockAdRepository,
|
||||
},
|
||||
{
|
||||
provide: INPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockInputDateTimeTransformer,
|
||||
},
|
||||
CreateAdService,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
createAdService = module.get<CreateAdService>(CreateAdService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(createAdService).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execution', () => {
|
||||
const createAdCommand = new CreateAdCommand(punctualCreateAdRequest);
|
||||
it('should create a new punctual ad', async () => {
|
||||
AdEntity.create = jest.fn().mockReturnValue({
|
||||
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
||||
});
|
||||
const result: AggregateID =
|
||||
await createAdService.execute(createAdCommand);
|
||||
expect(result).toBe('047a6ecf-23d4-4d3e-877c-3225d560a8da');
|
||||
});
|
||||
it('should throw an error if something bad happens', async () => {
|
||||
AdEntity.create = jest.fn().mockReturnValue({
|
||||
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
||||
});
|
||||
await expect(
|
||||
createAdService.execute(createAdCommand),
|
||||
).rejects.toBeInstanceOf(Error);
|
||||
});
|
||||
it('should throw an exception if Ad already exists', async () => {
|
||||
AdEntity.create = jest.fn().mockReturnValue({
|
||||
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
||||
});
|
||||
await expect(
|
||||
createAdService.execute(createAdCommand),
|
||||
).rejects.toBeInstanceOf(AdAlreadyExistsException);
|
||||
});
|
||||
});
|
||||
});
|
||||
98
tests/unit/ad/core/find-ad-by-id.query-handler.spec.ts
Normal file
98
tests/unit/ad/core/find-ad-by-id.query-handler.spec.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { FindAdByIdQuery } from '@modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query';
|
||||
import { FindAdByIdQueryHandler } from '@modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query-handler';
|
||||
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const originWaypointProps: WaypointProps = {
|
||||
position: 0,
|
||||
address: {
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
},
|
||||
};
|
||||
const destinationWaypointProps: WaypointProps = {
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
},
|
||||
};
|
||||
const baseCreateAdProps = {
|
||||
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [originWaypointProps, destinationWaypointProps],
|
||||
};
|
||||
const punctualCreateAdProps = {
|
||||
fromDate: '2023-06-22',
|
||||
toDate: '2023-06-22',
|
||||
schedule: [
|
||||
{
|
||||
time: '08:30',
|
||||
},
|
||||
],
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
};
|
||||
const punctualPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...punctualCreateAdProps,
|
||||
driver: false,
|
||||
passenger: true,
|
||||
};
|
||||
|
||||
const ad: AdEntity = AdEntity.create(punctualPassengerCreateAdProps);
|
||||
|
||||
const mockAdRepository = {
|
||||
findOneById: jest.fn().mockImplementation(() => ad),
|
||||
};
|
||||
|
||||
describe('find-ad-by-id.query-handler', () => {
|
||||
let findAdByIdQueryHandler: FindAdByIdQueryHandler;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useValue: mockAdRepository,
|
||||
},
|
||||
FindAdByIdQueryHandler,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
findAdByIdQueryHandler = module.get<FindAdByIdQueryHandler>(
|
||||
FindAdByIdQueryHandler,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(findAdByIdQueryHandler).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execution', () => {
|
||||
it('should return an ad', async () => {
|
||||
const findAdbyIdQuery = new FindAdByIdQuery(
|
||||
'dd264806-13b4-4226-9b18-87adf0ad5dd1',
|
||||
);
|
||||
const ad: AdEntity =
|
||||
await findAdByIdQueryHandler.execute(findAdbyIdQuery);
|
||||
expect(ad.getProps().fromDate).toBe('2023-06-22');
|
||||
});
|
||||
});
|
||||
});
|
||||
105
tests/unit/ad/core/find-ads-by-ids.query-handler.spec.ts
Normal file
105
tests/unit/ad/core/find-ads-by-ids.query-handler.spec.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { FindAdsByIdsQueryHandler } from '@modules/ad/core/application/queries/find-ads-by-ids/find-ads-by-ids.query-handler';
|
||||
import { FindAdsByIdsQuery } from '@modules/ad/core/application/queries/find-ads-by-ids/find-ads-by-ids.query';
|
||||
|
||||
const originWaypointProps: WaypointProps = {
|
||||
position: 0,
|
||||
address: {
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
},
|
||||
};
|
||||
const destinationWaypointProps: WaypointProps = {
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
},
|
||||
};
|
||||
const baseCreateAdProps = {
|
||||
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [originWaypointProps, destinationWaypointProps],
|
||||
};
|
||||
const punctualCreateAdProps = {
|
||||
fromDate: '2023-06-22',
|
||||
toDate: '2023-06-22',
|
||||
schedule: [
|
||||
{
|
||||
time: '08:30',
|
||||
},
|
||||
],
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
};
|
||||
const punctualPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...punctualCreateAdProps,
|
||||
driver: false,
|
||||
passenger: true,
|
||||
};
|
||||
|
||||
const ads: AdEntity[] = [
|
||||
AdEntity.create(punctualPassengerCreateAdProps),
|
||||
AdEntity.create(punctualPassengerCreateAdProps),
|
||||
AdEntity.create(punctualPassengerCreateAdProps),
|
||||
];
|
||||
|
||||
const mockAdRepository = {
|
||||
findAllByIds: jest.fn().mockImplementation(() => ads),
|
||||
};
|
||||
|
||||
describe('Find Ads By Ids Query Handler', () => {
|
||||
let findAdsByIdsQueryHandler: FindAdsByIdsQueryHandler;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useValue: mockAdRepository,
|
||||
},
|
||||
FindAdsByIdsQueryHandler,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
findAdsByIdsQueryHandler = module.get<FindAdsByIdsQueryHandler>(
|
||||
FindAdsByIdsQueryHandler,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(findAdsByIdsQueryHandler).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execution', () => {
|
||||
it('should return an ad', async () => {
|
||||
const findAdsByIdsQuery = new FindAdsByIdsQuery([
|
||||
'dd264806-13b4-4226-9b18-87adf0ad5dd1',
|
||||
'dd264806-13b4-4226-9b18-87adf0ad5dd2',
|
||||
'dd264806-13b4-4226-9b18-87adf0ad5dd3',
|
||||
]);
|
||||
const ads: AdEntity[] =
|
||||
await findAdsByIdsQueryHandler.execute(findAdsByIdsQuery);
|
||||
expect(ads).toHaveLength(3);
|
||||
expect(ads[1].getProps().fromDate).toBe('2023-06-22');
|
||||
});
|
||||
});
|
||||
});
|
||||
98
tests/unit/ad/core/invalidate-ad.service.spec.ts
Normal file
98
tests/unit/ad/core/invalidate-ad.service.spec.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||
import { AggregateID } from '@mobicoop/ddd-library';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
import { InvalidateAdService } from '@modules/ad/core/application/commands/invalidate-ad/invalidate-ad.service';
|
||||
import { InvalidateAdCommand } from '@modules/ad/core/application/commands/invalidate-ad/invalidate-ad.command';
|
||||
|
||||
const originWaypointProps: WaypointProps = {
|
||||
position: 0,
|
||||
address: {
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
},
|
||||
};
|
||||
const destinationWaypointProps: WaypointProps = {
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
},
|
||||
};
|
||||
const baseCreateAdProps = {
|
||||
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [originWaypointProps, destinationWaypointProps],
|
||||
};
|
||||
const punctualCreateAdProps = {
|
||||
fromDate: '2023-06-22',
|
||||
toDate: '2023-06-22',
|
||||
schedule: [
|
||||
{
|
||||
time: '08:30',
|
||||
},
|
||||
],
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
};
|
||||
const punctualPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...punctualCreateAdProps,
|
||||
driver: false,
|
||||
passenger: true,
|
||||
};
|
||||
|
||||
const ad: AdEntity = AdEntity.create(punctualPassengerCreateAdProps);
|
||||
|
||||
const mockAdRepository = {
|
||||
findOneById: jest.fn().mockImplementation(() => ad),
|
||||
update: jest.fn().mockImplementation(() => ad.id),
|
||||
};
|
||||
|
||||
describe('Invalidate Ad Service', () => {
|
||||
let invalidateAdService: InvalidateAdService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useValue: mockAdRepository,
|
||||
},
|
||||
InvalidateAdService,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
invalidateAdService = module.get<InvalidateAdService>(InvalidateAdService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(invalidateAdService).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execution', () => {
|
||||
it('should invalidate an ad', async () => {
|
||||
jest.spyOn(ad, 'invalid');
|
||||
const invalidateAdCommand = new InvalidateAdCommand(ad.id);
|
||||
const result: AggregateID =
|
||||
await invalidateAdService.execute(invalidateAdCommand);
|
||||
expect(result).toBe(ad.id);
|
||||
expect(ad.invalid).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
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-event';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens';
|
||||
import { AD_CREATED_ROUTING_KEY } from '@src/app.constants';
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
describe('Publish message when ad is created domain event handler', () => {
|
||||
let publishMessageWhenAdIsCreatedDomainEventHandler: PublishMessageWhenAdIsCreatedDomainEventHandler;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AD_MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
PublishMessageWhenAdIsCreatedDomainEventHandler,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
publishMessageWhenAdIsCreatedDomainEventHandler =
|
||||
module.get<PublishMessageWhenAdIsCreatedDomainEventHandler>(
|
||||
PublishMessageWhenAdIsCreatedDomainEventHandler,
|
||||
);
|
||||
});
|
||||
|
||||
it('should publish a 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',
|
||||
schedule: [
|
||||
{
|
||||
day: 3,
|
||||
time: '07:15',
|
||||
margin: 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',
|
||||
},
|
||||
};
|
||||
publishMessageWhenAdIsCreatedDomainEventHandler.handle(
|
||||
adCreatedDomainEvent,
|
||||
);
|
||||
expect(publishMessageWhenAdIsCreatedDomainEventHandler).toBeDefined();
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
|
||||
AD_CREATED_ROUTING_KEY,
|
||||
'{"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","schedule":[{"day":3,"time":"07:15","margin":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"}}',
|
||||
);
|
||||
});
|
||||
});
|
||||
14
tests/unit/ad/core/schedule-item.value-object.spec.ts
Normal file
14
tests/unit/ad/core/schedule-item.value-object.spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ScheduleItem } from '@modules/ad/core/domain/value-objects/schedule-item.value-object';
|
||||
|
||||
describe('Schedule item value object', () => {
|
||||
it('should create a schedule item value object', () => {
|
||||
const scheduleItemVO = new ScheduleItem({
|
||||
day: 0,
|
||||
time: '07:00',
|
||||
margin: 900,
|
||||
});
|
||||
expect(scheduleItemVO.day).toBe(0);
|
||||
expect(scheduleItemVO.time).toBe('07:00');
|
||||
expect(scheduleItemVO.margin).toBe(900);
|
||||
});
|
||||
});
|
||||
98
tests/unit/ad/core/validate-ad.service.spec.ts
Normal file
98
tests/unit/ad/core/validate-ad.service.spec.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||
import { AggregateID } from '@mobicoop/ddd-library';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { ValidateAdService } from '@modules/ad/core/application/commands/validate-ad/validate-ad.service';
|
||||
import { ValidateAdCommand } from '@modules/ad/core/application/commands/validate-ad/validate-ad.command';
|
||||
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
|
||||
const originWaypointProps: WaypointProps = {
|
||||
position: 0,
|
||||
address: {
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
},
|
||||
};
|
||||
const destinationWaypointProps: WaypointProps = {
|
||||
position: 1,
|
||||
address: {
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
},
|
||||
},
|
||||
};
|
||||
const baseCreateAdProps = {
|
||||
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [originWaypointProps, destinationWaypointProps],
|
||||
};
|
||||
const punctualCreateAdProps = {
|
||||
fromDate: '2023-06-22',
|
||||
toDate: '2023-06-22',
|
||||
schedule: [
|
||||
{
|
||||
time: '08:30',
|
||||
},
|
||||
],
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
};
|
||||
const punctualPassengerCreateAdProps: CreateAdProps = {
|
||||
...baseCreateAdProps,
|
||||
...punctualCreateAdProps,
|
||||
driver: false,
|
||||
passenger: true,
|
||||
};
|
||||
|
||||
const ad: AdEntity = AdEntity.create(punctualPassengerCreateAdProps);
|
||||
|
||||
const mockAdRepository = {
|
||||
findOneById: jest.fn().mockImplementation(() => ad),
|
||||
update: jest.fn().mockImplementation(() => ad.id),
|
||||
};
|
||||
|
||||
describe('Validate Ad Service', () => {
|
||||
let validateAdService: ValidateAdService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useValue: mockAdRepository,
|
||||
},
|
||||
ValidateAdService,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
validateAdService = module.get<ValidateAdService>(ValidateAdService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(validateAdService).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execution', () => {
|
||||
it('should validate an ad', async () => {
|
||||
jest.spyOn(ad, 'valid');
|
||||
const validateAdCommand = new ValidateAdCommand(ad.id);
|
||||
const result: AggregateID =
|
||||
await validateAdService.execute(validateAdCommand);
|
||||
expect(result).toBe(ad.id);
|
||||
expect(ad.valid).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
22
tests/unit/ad/core/waypoint.value-object.spec.ts
Normal file
22
tests/unit/ad/core/waypoint.value-object.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Waypoint } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||
|
||||
describe('Waypoint value object', () => {
|
||||
it('should create a waypoint value object', () => {
|
||||
const waypointVO = new Waypoint({
|
||||
position: 0,
|
||||
address: {
|
||||
houseNumber: '5',
|
||||
street: 'rue de la monnaie',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
coordinates: {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(waypointVO.position).toBe(0);
|
||||
expect(waypointVO.address.country).toBe('France');
|
||||
});
|
||||
});
|
||||
52
tests/unit/ad/infrastructure/ad.repository.spec.ts
Normal file
52
tests/unit/ad/infrastructure/ad.repository.spec.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { OUTPUT_DATETIME_TRANSFORMER } from '@modules/ad/ad.di-tokens';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
const mockOutputDatetimeTransformer: DateTimeTransformerPort = {
|
||||
fromDate: jest.fn(),
|
||||
toDate: jest.fn(),
|
||||
day: jest.fn(),
|
||||
time: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Ad repository', () => {
|
||||
let prismaService: PrismaService;
|
||||
let adMapper: AdMapper;
|
||||
let eventEmitter: EventEmitter2;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [EventEmitterModule.forRoot()],
|
||||
providers: [
|
||||
PrismaService,
|
||||
AdMapper,
|
||||
{
|
||||
provide: OUTPUT_DATETIME_TRANSFORMER,
|
||||
useValue: mockOutputDatetimeTransformer,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
prismaService = module.get<PrismaService>(PrismaService);
|
||||
adMapper = module.get<AdMapper>(AdMapper);
|
||||
eventEmitter = module.get<EventEmitter2>(EventEmitter2);
|
||||
});
|
||||
it('should be defined', () => {
|
||||
expect(
|
||||
new AdRepository(
|
||||
prismaService,
|
||||
adMapper,
|
||||
eventEmitter,
|
||||
mockMessagePublisher,
|
||||
),
|
||||
).toBeDefined();
|
||||
});
|
||||
});
|
||||
248
tests/unit/ad/infrastructure/input-datetime-transformer.spec.ts
Normal file
248
tests/unit/ad/infrastructure/input-datetime-transformer.spec.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import { TIMEZONE_FINDER, TIME_CONVERTER } from '@modules/ad/ad.di-tokens';
|
||||
import { Frequency } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
||||
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
||||
import { InputDateTimeTransformer } from '@modules/ad/infrastructure/input-datetime-transformer';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockTimezoneFinder: TimezoneFinderPort = {
|
||||
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
||||
};
|
||||
|
||||
const mockTimeConverter: TimeConverterPort = {
|
||||
localStringTimeToUtcStringTime: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => '00:15'),
|
||||
utcStringTimeToLocalStringTime: jest.fn(),
|
||||
localStringDateTimeToUtcDate: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => new Date('2023-07-30T06:15:00.000Z'))
|
||||
.mockImplementationOnce(() => new Date('2023-07-20T08:15:00.000Z'))
|
||||
.mockImplementationOnce(() => new Date('2023-07-19T23:15:00.000Z'))
|
||||
.mockImplementationOnce(() => new Date('2023-07-19T23:15:00.000Z')),
|
||||
utcStringDateTimeToLocalIsoString: jest.fn(),
|
||||
utcUnixEpochDayFromTime: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => 4)
|
||||
.mockImplementationOnce(() => 3)
|
||||
.mockImplementationOnce(() => 3)
|
||||
.mockImplementationOnce(() => 5)
|
||||
.mockImplementationOnce(() => 5),
|
||||
localUnixEpochDayFromTime: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Input Datetime Transformer', () => {
|
||||
let inputDatetimeTransformer: InputDateTimeTransformer;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: TIMEZONE_FINDER,
|
||||
useValue: mockTimezoneFinder,
|
||||
},
|
||||
{
|
||||
provide: TIME_CONVERTER,
|
||||
useValue: mockTimeConverter,
|
||||
},
|
||||
InputDateTimeTransformer,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
inputDatetimeTransformer = module.get<InputDateTimeTransformer>(
|
||||
InputDateTimeTransformer,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(inputDatetimeTransformer).toBeDefined();
|
||||
});
|
||||
|
||||
describe('fromDate', () => {
|
||||
it('should return fromDate as is if frequency is recurrent', () => {
|
||||
const transformedFromDate: string = inputDatetimeTransformer.fromDate(
|
||||
{
|
||||
date: '2023-07-30',
|
||||
time: '07:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(transformedFromDate).toBe('2023-07-30');
|
||||
});
|
||||
it('should return transformed fromDate if frequency is punctual and coordinates are those of Nancy', () => {
|
||||
const transformedFromDate: string = inputDatetimeTransformer.fromDate(
|
||||
{
|
||||
date: '2023-07-30',
|
||||
time: '07:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(transformedFromDate).toBe('2023-07-30');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toDate', () => {
|
||||
it('should return toDate as is if frequency is recurrent', () => {
|
||||
const transformedToDate: string = inputDatetimeTransformer.toDate(
|
||||
'2024-07-29',
|
||||
{
|
||||
date: '2023-07-20',
|
||||
time: '10:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(transformedToDate).toBe('2024-07-29');
|
||||
});
|
||||
it('should return transformed fromDate if frequency is punctual', () => {
|
||||
const transformedToDate: string = inputDatetimeTransformer.toDate(
|
||||
'2024-07-30',
|
||||
{
|
||||
date: '2023-07-20',
|
||||
time: '10:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(transformedToDate).toBe('2023-07-20');
|
||||
});
|
||||
});
|
||||
|
||||
describe('day', () => {
|
||||
it('should not change day if frequency is recurrent and converted UTC time is on the same day', () => {
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '01:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(1);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the previous day', () => {
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '00:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(0);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the previous day and given day is sunday', () => {
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
0,
|
||||
{
|
||||
date: '2023-07-23',
|
||||
time: '00:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(6);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the next day', () => {
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '23:15',
|
||||
coordinates: {
|
||||
lon: 30.82,
|
||||
lat: 49.37,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(2);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted UTC time is on the next day and given day is saturday(6)', () => {
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
6,
|
||||
{
|
||||
date: '2023-07-29',
|
||||
time: '23:15',
|
||||
coordinates: {
|
||||
lon: 30.82,
|
||||
lat: 49.37,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(0);
|
||||
});
|
||||
it('should return utc fromDate day if frequency is punctual', () => {
|
||||
const day: number = inputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-20',
|
||||
time: '00:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(day).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('time', () => {
|
||||
it('should transform given time to utc time if frequency is recurrent', () => {
|
||||
const time: string = inputDatetimeTransformer.time(
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '01:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(time).toBe('00:15');
|
||||
});
|
||||
it('should return given time to utc time if frequency is punctual', () => {
|
||||
const time: string = inputDatetimeTransformer.time(
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '01:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(time).toBe('23:15');
|
||||
});
|
||||
});
|
||||
});
|
||||
248
tests/unit/ad/infrastructure/output-datetime-transformer.spec.ts
Normal file
248
tests/unit/ad/infrastructure/output-datetime-transformer.spec.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import { TIMEZONE_FINDER, TIME_CONVERTER } from '@modules/ad/ad.di-tokens';
|
||||
import { Frequency } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
||||
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
||||
import { OutputDateTimeTransformer } from '@modules/ad/infrastructure/output-datetime-transformer';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockTimezoneFinder: TimezoneFinderPort = {
|
||||
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
||||
};
|
||||
|
||||
const mockTimeConverter: TimeConverterPort = {
|
||||
localStringTimeToUtcStringTime: jest.fn(),
|
||||
utcStringTimeToLocalStringTime: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => '00:15'),
|
||||
localStringDateTimeToUtcDate: jest.fn(),
|
||||
utcStringDateTimeToLocalIsoString: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => '2023-07-30T08:15:00.000+02:00')
|
||||
.mockImplementationOnce(() => '2023-07-20T10:15:00.000+02:00')
|
||||
.mockImplementationOnce(() => '2023-07-19T23:15:00.000+02:00')
|
||||
.mockImplementationOnce(() => '2023-07-20T00:15:00.000+02:00'),
|
||||
utcUnixEpochDayFromTime: jest.fn(),
|
||||
localUnixEpochDayFromTime: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => 4)
|
||||
.mockImplementationOnce(() => 5)
|
||||
.mockImplementationOnce(() => 5)
|
||||
.mockImplementationOnce(() => 3)
|
||||
.mockImplementationOnce(() => 3),
|
||||
};
|
||||
|
||||
describe('Output Datetime Transformer', () => {
|
||||
let outputDatetimeTransformer: OutputDateTimeTransformer;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: TIMEZONE_FINDER,
|
||||
useValue: mockTimezoneFinder,
|
||||
},
|
||||
{
|
||||
provide: TIME_CONVERTER,
|
||||
useValue: mockTimeConverter,
|
||||
},
|
||||
OutputDateTimeTransformer,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
outputDatetimeTransformer = module.get<OutputDateTimeTransformer>(
|
||||
OutputDateTimeTransformer,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(outputDatetimeTransformer).toBeDefined();
|
||||
});
|
||||
|
||||
describe('fromDate', () => {
|
||||
it('should return fromDate as is if frequency is recurrent', () => {
|
||||
const transformedFromDate: string = outputDatetimeTransformer.fromDate(
|
||||
{
|
||||
date: '2023-07-30',
|
||||
time: '07:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(transformedFromDate).toBe('2023-07-30');
|
||||
});
|
||||
it('should return transformed fromDate if frequency is punctual and coordinates are those of Nancy', () => {
|
||||
const transformedFromDate: string = outputDatetimeTransformer.fromDate(
|
||||
{
|
||||
date: '2023-07-30',
|
||||
time: '07:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(transformedFromDate).toBe('2023-07-30');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toDate', () => {
|
||||
it('should return toDate as is if frequency is recurrent', () => {
|
||||
const transformedToDate: string = outputDatetimeTransformer.toDate(
|
||||
'2024-07-29',
|
||||
{
|
||||
date: '2023-07-20',
|
||||
time: '10:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(transformedToDate).toBe('2024-07-29');
|
||||
});
|
||||
it('should return transformed fromDate if frequency is punctual', () => {
|
||||
const transformedToDate: string = outputDatetimeTransformer.toDate(
|
||||
'2024-07-30',
|
||||
{
|
||||
date: '2023-07-20',
|
||||
time: '08:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(transformedToDate).toBe('2023-07-20');
|
||||
});
|
||||
});
|
||||
|
||||
describe('day', () => {
|
||||
it('should not change day if frequency is recurrent and converted local time is on the same day', () => {
|
||||
const day: number = outputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-24',
|
||||
time: '00:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(1);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted local time is on the next day', () => {
|
||||
const day: number = outputDatetimeTransformer.day(
|
||||
0,
|
||||
{
|
||||
date: '2023-07-23',
|
||||
time: '23:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(1);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted local time is on the next day and given day is saturday', () => {
|
||||
const day: number = outputDatetimeTransformer.day(
|
||||
6,
|
||||
{
|
||||
date: '2023-07-23',
|
||||
time: '23:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(0);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted local time is on the previous day', () => {
|
||||
const day: number = outputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-25',
|
||||
time: '00:15',
|
||||
coordinates: {
|
||||
lon: 30.82,
|
||||
lat: 49.37,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(0);
|
||||
});
|
||||
it('should change day if frequency is recurrent and converted local time is on the previous day and given day is sunday(0)', () => {
|
||||
const day: number = outputDatetimeTransformer.day(
|
||||
0,
|
||||
{
|
||||
date: '2023-07-30',
|
||||
time: '00:15',
|
||||
coordinates: {
|
||||
lon: 30.82,
|
||||
lat: 49.37,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(day).toBe(6);
|
||||
});
|
||||
it('should return local fromDate day if frequency is punctual', () => {
|
||||
const day: number = outputDatetimeTransformer.day(
|
||||
1,
|
||||
{
|
||||
date: '2023-07-20',
|
||||
time: '00:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(day).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('time', () => {
|
||||
it('should transform utc time to local time if frequency is recurrent', () => {
|
||||
const time: string = outputDatetimeTransformer.time(
|
||||
{
|
||||
date: '2023-07-23',
|
||||
time: '23:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.RECURRENT,
|
||||
);
|
||||
expect(time).toBe('00:15');
|
||||
});
|
||||
it('should return local time if frequency is punctual', () => {
|
||||
const time: string = outputDatetimeTransformer.time(
|
||||
{
|
||||
date: '2023-07-19',
|
||||
time: '23:15',
|
||||
coordinates: {
|
||||
lon: 6.175,
|
||||
lat: 48.685,
|
||||
},
|
||||
},
|
||||
Frequency.PUNCTUAL,
|
||||
);
|
||||
expect(time).toBe('00:15');
|
||||
});
|
||||
});
|
||||
});
|
||||
311
tests/unit/ad/infrastructure/time-converter.spec.ts
Normal file
311
tests/unit/ad/infrastructure/time-converter.spec.ts
Normal file
@@ -0,0 +1,311 @@
|
||||
import { TimeConverter } from '@modules/ad/infrastructure/time-converter';
|
||||
|
||||
describe('Time Converter', () => {
|
||||
it('should be defined', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(timeConverter).toBeDefined();
|
||||
});
|
||||
|
||||
describe('localStringTimeToUtcStringTime', () => {
|
||||
it('should convert a paris time to utc time', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisTime = '08:00';
|
||||
const utcDatetime = timeConverter.localStringTimeToUtcStringTime(
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(utcDatetime).toBe('07:00');
|
||||
});
|
||||
it('should throw an error if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const fooBarTime = '08:00';
|
||||
expect(() => {
|
||||
timeConverter.localStringTimeToUtcStringTime(fooBarTime, 'Foo/Bar');
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('utcStringTimeToLocalStringTime', () => {
|
||||
it('should convert a utc time to a paris time', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = '07:00';
|
||||
const parisTime = timeConverter.utcStringTimeToLocalStringTime(
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(parisTime).toBe('08:00');
|
||||
});
|
||||
it('should throw an error if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = '27:00';
|
||||
expect(() => {
|
||||
timeConverter.utcStringTimeToLocalStringTime(utcTime, 'Europe/Paris');
|
||||
}).toThrow();
|
||||
});
|
||||
it('should throw an error if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcTime = '07:00';
|
||||
expect(() => {
|
||||
timeConverter.utcStringTimeToLocalStringTime(utcTime, 'Foo/Bar');
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('localStringDateTimeToUtcDate', () => {
|
||||
it('should convert a summer paris date and time to a utc date with dst', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-06-22';
|
||||
const parisTime = '12:00';
|
||||
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
true,
|
||||
);
|
||||
expect(utcDate.toISOString()).toBe('2023-06-22T10:00:00.000Z');
|
||||
});
|
||||
it('should convert a winter paris date and time to a utc date with dst', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-02-02';
|
||||
const parisTime = '12:00';
|
||||
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
true,
|
||||
);
|
||||
expect(utcDate.toISOString()).toBe('2023-02-02T11:00:00.000Z');
|
||||
});
|
||||
it('should convert a summer paris date and time to a utc date without dst', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-06-22';
|
||||
const parisTime = '12:00';
|
||||
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(utcDate.toISOString()).toBe('2023-06-22T11:00:00.000Z');
|
||||
});
|
||||
it('should convert a tonga date and time to a utc date', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const tongaDate = '2023-02-02';
|
||||
const tongaTime = '12:00';
|
||||
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||
tongaDate,
|
||||
tongaTime,
|
||||
'Pacific/Tongatapu',
|
||||
);
|
||||
expect(utcDate.toISOString()).toBe('2023-02-01T23:00:00.000Z');
|
||||
});
|
||||
it('should convert a papeete date and time to a utc date', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const papeeteDate = '2023-02-02';
|
||||
const papeeteTime = '15:00';
|
||||
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||
papeeteDate,
|
||||
papeeteTime,
|
||||
'Pacific/Tahiti',
|
||||
);
|
||||
expect(utcDate.toISOString()).toBe('2023-02-03T01:00:00.000Z');
|
||||
});
|
||||
it('should throw an error if date is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-06-32';
|
||||
const parisTime = '08:00';
|
||||
expect(() => {
|
||||
timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
}).toThrow();
|
||||
});
|
||||
it('should throw an error if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-06-22';
|
||||
const parisTime = '28:00';
|
||||
expect(() => {
|
||||
timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
}).toThrow();
|
||||
});
|
||||
it('should throw an error if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const parisDate = '2023-06-22';
|
||||
const parisTime = '12:00';
|
||||
expect(() => {
|
||||
timeConverter.localStringDateTimeToUtcDate(
|
||||
parisDate,
|
||||
parisTime,
|
||||
'Foo/Bar',
|
||||
);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('utcStringDateTimeToLocalIsoString', () => {
|
||||
it('should convert a utc string date and time to a summer paris date isostring with dst', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '10:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
true,
|
||||
);
|
||||
expect(localIsoString).toBe('2023-06-22T12:00:00.000+02:00');
|
||||
});
|
||||
it('should convert a utc string date and time to a winter paris date isostring with dst', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-02-02';
|
||||
const utcTime = '10:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
true,
|
||||
);
|
||||
expect(localIsoString).toBe('2023-02-02T11:00:00.000+01:00');
|
||||
});
|
||||
it('should convert a utc string date and time to a summer paris date isostring', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '10:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
expect(localIsoString).toBe('2023-06-22T11:00:00.000+01:00');
|
||||
});
|
||||
it('should convert a utc date to a tonga date isostring', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-02-01';
|
||||
const utcTime = '23:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Pacific/Tongatapu',
|
||||
);
|
||||
expect(localIsoString).toBe('2023-02-02T12:00:00.000+13:00');
|
||||
});
|
||||
it('should convert a utc date to a papeete date isostring', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-02-03';
|
||||
const utcTime = '01:00';
|
||||
const localIsoString = timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Pacific/Tahiti',
|
||||
);
|
||||
expect(localIsoString).toBe('2023-02-02T15:00:00.000-10:00');
|
||||
});
|
||||
it('should throw an error if date is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-32';
|
||||
const utcTime = '07:00';
|
||||
expect(() => {
|
||||
timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
}).toThrow();
|
||||
});
|
||||
it('should throw an error if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '27:00';
|
||||
expect(() => {
|
||||
timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Europe/Paris',
|
||||
);
|
||||
}).toThrow();
|
||||
});
|
||||
it('should throw an error if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
const utcDate = '2023-06-22';
|
||||
const utcTime = '07:00';
|
||||
expect(() => {
|
||||
timeConverter.utcStringDateTimeToLocalIsoString(
|
||||
utcDate,
|
||||
utcTime,
|
||||
'Foo/Bar',
|
||||
);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('utcUnixEpochDayFromTime', () => {
|
||||
it('should get the utc day of paris at 12:00', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.utcUnixEpochDayFromTime('12:00', 'Europe/Paris'),
|
||||
).toBe(4);
|
||||
});
|
||||
it('should get the utc day of paris at 00:00', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.utcUnixEpochDayFromTime('00:00', 'Europe/Paris'),
|
||||
).toBe(3);
|
||||
});
|
||||
it('should get the utc day of papeete at 16:00', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.utcUnixEpochDayFromTime('16:00', 'Pacific/Tahiti'),
|
||||
).toBe(5);
|
||||
});
|
||||
it('should throw an error if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(() => {
|
||||
timeConverter.utcUnixEpochDayFromTime('28:00', 'Europe/Paris');
|
||||
}).toThrow();
|
||||
});
|
||||
it('should throw an error if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(() => {
|
||||
timeConverter.utcUnixEpochDayFromTime('12:00', 'Foo/Bar');
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('localUnixEpochDayFromTime', () => {
|
||||
it('should get the day of paris at 12:00 utc', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('12:00', 'Europe/Paris'),
|
||||
).toBe(4);
|
||||
});
|
||||
it('should get the day of paris at 23:00 utc', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('23:00', 'Europe/Paris'),
|
||||
).toBe(5);
|
||||
});
|
||||
it('should get the day of papeete at 05:00 utc', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(
|
||||
timeConverter.localUnixEpochDayFromTime('05:00', 'Pacific/Tahiti'),
|
||||
).toBe(3);
|
||||
});
|
||||
it('should throw an error if time is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(() => {
|
||||
timeConverter.localUnixEpochDayFromTime('28:00', 'Europe/Paris');
|
||||
}).toThrow();
|
||||
});
|
||||
it('should throw an error if timezone is invalid', () => {
|
||||
const timeConverter: TimeConverter = new TimeConverter();
|
||||
expect(() => {
|
||||
timeConverter.localUnixEpochDayFromTime('12:00', 'Foo/Bar');
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
14
tests/unit/ad/infrastructure/timezone-finder.spec.ts
Normal file
14
tests/unit/ad/infrastructure/timezone-finder.spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { TimezoneFinder } from '@modules/ad/infrastructure/timezone-finder';
|
||||
|
||||
describe('Timezone Finder', () => {
|
||||
it('should be defined', () => {
|
||||
const timezoneFinder: TimezoneFinder = new TimezoneFinder();
|
||||
expect(timezoneFinder).toBeDefined();
|
||||
});
|
||||
it('should get timezone for Nancy(France) as Europe/Paris', () => {
|
||||
const timezoneFinder: TimezoneFinder = new TimezoneFinder();
|
||||
const timezones = timezoneFinder.timezones(6.179373, 48.687913);
|
||||
expect(timezones.length).toBe(1);
|
||||
expect(timezones[0]).toBe('Europe/Paris');
|
||||
});
|
||||
});
|
||||
122
tests/unit/ad/interface/create-ad.grpc.controller.spec.ts
Normal file
122
tests/unit/ad/interface/create-ad.grpc.controller.spec.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { IdResponse } from '@mobicoop/ddd-library';
|
||||
import { RpcExceptionCode } from '@mobicoop/ddd-library';
|
||||
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { CreateAdGrpcController } from '@modules/ad/interface/grpc-controllers/create-ad.grpc.controller';
|
||||
import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto';
|
||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||
import { CommandBus } from '@nestjs/cqrs';
|
||||
import { RpcException } from '@nestjs/microservices';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const originWaypoint: WaypointDto = {
|
||||
position: 0,
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
};
|
||||
const destinationWaypoint: WaypointDto = {
|
||||
position: 1,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
};
|
||||
const punctualCreateAdRequest: CreateAdRequestDto = {
|
||||
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
||||
fromDate: '2023-12-21',
|
||||
toDate: '2023-12-21',
|
||||
schedule: [
|
||||
{
|
||||
time: '08:15',
|
||||
day: 4,
|
||||
margin: 600,
|
||||
},
|
||||
],
|
||||
driver: false,
|
||||
passenger: true,
|
||||
seatsRequested: 1,
|
||||
seatsProposed: 3,
|
||||
strict: false,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
waypoints: [originWaypoint, destinationWaypoint],
|
||||
};
|
||||
|
||||
const mockCommandBus = {
|
||||
execute: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => '200d61a8-d878-4378-a609-c19ea71633d2')
|
||||
.mockImplementationOnce(() => {
|
||||
throw new AdAlreadyExistsException();
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error();
|
||||
}),
|
||||
};
|
||||
|
||||
describe('Create Ad Grpc Controller', () => {
|
||||
let createAdGrpcController: CreateAdGrpcController;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: CommandBus,
|
||||
useValue: mockCommandBus,
|
||||
},
|
||||
CreateAdGrpcController,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
createAdGrpcController = module.get<CreateAdGrpcController>(
|
||||
CreateAdGrpcController,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(createAdGrpcController).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create a new ad', async () => {
|
||||
jest.spyOn(mockCommandBus, 'execute');
|
||||
const result: IdResponse = await createAdGrpcController.create(
|
||||
punctualCreateAdRequest,
|
||||
);
|
||||
expect(result).toBeInstanceOf(IdResponse);
|
||||
expect(result.id).toBe('200d61a8-d878-4378-a609-c19ea71633d2');
|
||||
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should throw a dedicated RpcException if ad already exists', async () => {
|
||||
jest.spyOn(mockCommandBus, 'execute');
|
||||
expect.assertions(3);
|
||||
try {
|
||||
await createAdGrpcController.create(punctualCreateAdRequest);
|
||||
} catch (e: any) {
|
||||
expect(e).toBeInstanceOf(RpcException);
|
||||
expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS);
|
||||
}
|
||||
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should throw a generic RpcException', async () => {
|
||||
jest.spyOn(mockCommandBus, 'execute');
|
||||
expect.assertions(3);
|
||||
try {
|
||||
await createAdGrpcController.create(punctualCreateAdRequest);
|
||||
} catch (e: any) {
|
||||
expect(e).toBeInstanceOf(RpcException);
|
||||
expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN);
|
||||
}
|
||||
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
140
tests/unit/ad/interface/find-ad-by-id.grpc.controller.spec.ts
Normal file
140
tests/unit/ad/interface/find-ad-by-id.grpc.controller.spec.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { NotFoundException } from '@mobicoop/ddd-library';
|
||||
import { RpcExceptionCode } from '@mobicoop/ddd-library';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { FindAdByIdGrpcController } from '@modules/ad/interface/grpc-controllers/find-ad-by-id.grpc.controller';
|
||||
import { QueryBus } from '@nestjs/cqrs';
|
||||
import { RpcException } from '@nestjs/microservices';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockQueryBus = {
|
||||
execute: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => '200d61a8-d878-4378-a609-c19ea71633d2')
|
||||
.mockImplementationOnce(() => {
|
||||
throw new NotFoundException();
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error();
|
||||
}),
|
||||
};
|
||||
|
||||
const mockAdMapper = {
|
||||
toResponse: jest.fn().mockImplementationOnce(() => ({
|
||||
userId: '8cc90d1a-4a59-4289-a7d8-078f9db7857f',
|
||||
driver: true,
|
||||
passenger: true,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-06-27',
|
||||
toDate: '2023-06-27',
|
||||
schedule: {
|
||||
tue: '07:15',
|
||||
},
|
||||
marginDurations: {
|
||||
mon: 900,
|
||||
tue: 900,
|
||||
wed: 900,
|
||||
thu: 900,
|
||||
fri: 900,
|
||||
sat: 900,
|
||||
sun: 900,
|
||||
},
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
},
|
||||
],
|
||||
})),
|
||||
};
|
||||
|
||||
describe('Find Ad By Id Grpc Controller', () => {
|
||||
let findAdbyIdGrpcController: FindAdByIdGrpcController;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: QueryBus,
|
||||
useValue: mockQueryBus,
|
||||
},
|
||||
{
|
||||
provide: AdMapper,
|
||||
useValue: mockAdMapper,
|
||||
},
|
||||
FindAdByIdGrpcController,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
findAdbyIdGrpcController = module.get<FindAdByIdGrpcController>(
|
||||
FindAdByIdGrpcController,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(findAdbyIdGrpcController).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return an ad', async () => {
|
||||
jest.spyOn(mockQueryBus, 'execute');
|
||||
jest.spyOn(mockAdMapper, 'toResponse');
|
||||
const response = await findAdbyIdGrpcController.findOnebyId({
|
||||
id: '6dcf093c-c7db-4dae-8e9c-c715cebf83c7',
|
||||
});
|
||||
expect(response.userId).toBe('8cc90d1a-4a59-4289-a7d8-078f9db7857f');
|
||||
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
|
||||
expect(mockAdMapper.toResponse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should throw a dedicated RpcException if ad is not found', async () => {
|
||||
jest.spyOn(mockQueryBus, 'execute');
|
||||
jest.spyOn(mockAdMapper, 'toResponse');
|
||||
expect.assertions(4);
|
||||
try {
|
||||
await findAdbyIdGrpcController.findOnebyId({
|
||||
id: 'ac85f5f4-41cd-4c5d-9aee-0a1acb176fb8',
|
||||
});
|
||||
} catch (e: any) {
|
||||
expect(e).toBeInstanceOf(RpcException);
|
||||
expect(e.error.code).toBe(RpcExceptionCode.NOT_FOUND);
|
||||
}
|
||||
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
|
||||
expect(mockAdMapper.toResponse).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('should throw a generic RpcException', async () => {
|
||||
jest.spyOn(mockQueryBus, 'execute');
|
||||
jest.spyOn(mockAdMapper, 'toResponse');
|
||||
expect.assertions(4);
|
||||
try {
|
||||
await findAdbyIdGrpcController.findOnebyId({
|
||||
id: '53c8e7ec-ef68-42bc-ba4c-5ef3effa60a6',
|
||||
});
|
||||
} catch (e: any) {
|
||||
expect(e).toBeInstanceOf(RpcException);
|
||||
expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN);
|
||||
}
|
||||
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
|
||||
expect(mockAdMapper.toResponse).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
132
tests/unit/ad/interface/find-ads-by-ids.grpc.controller.spec.ts
Normal file
132
tests/unit/ad/interface/find-ads-by-ids.grpc.controller.spec.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { RpcExceptionCode } from '@mobicoop/ddd-library';
|
||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { FindAdsByIdsGrpcController } from '@modules/ad/interface/grpc-controllers/find-ads-by-ids.grpc.controller';
|
||||
import { QueryBus } from '@nestjs/cqrs';
|
||||
import { RpcException } from '@nestjs/microservices';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const mockQueryBus = {
|
||||
execute: jest
|
||||
.fn()
|
||||
.mockImplementationOnce(() => [
|
||||
'200d61a8-d878-4378-a609-c19ea71633d2',
|
||||
'200d61a8-d878-4378-a609-c19ea71633d3',
|
||||
'200d61a8-d878-4378-a609-c19ea71633d4',
|
||||
])
|
||||
.mockImplementationOnce(() => {
|
||||
throw new Error();
|
||||
}),
|
||||
};
|
||||
|
||||
const mockAdMapper = {
|
||||
toResponse: jest.fn().mockImplementationOnce(() => ({
|
||||
userId: '8cc90d1a-4a59-4289-a7d8-078f9db7857f',
|
||||
driver: true,
|
||||
passenger: true,
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
fromDate: '2023-06-27',
|
||||
toDate: '2023-06-27',
|
||||
schedule: {
|
||||
tue: '07:15',
|
||||
},
|
||||
marginDurations: {
|
||||
mon: 900,
|
||||
tue: 900,
|
||||
wed: 900,
|
||||
thu: 900,
|
||||
fri: 900,
|
||||
sat: 900,
|
||||
sun: 900,
|
||||
},
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
waypoints: [
|
||||
{
|
||||
position: 0,
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
},
|
||||
],
|
||||
})),
|
||||
};
|
||||
|
||||
describe('Find Ads By Ids Grpc Controller', () => {
|
||||
let findAdsByIdsGrpcController: FindAdsByIdsGrpcController;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: QueryBus,
|
||||
useValue: mockQueryBus,
|
||||
},
|
||||
{
|
||||
provide: AdMapper,
|
||||
useValue: mockAdMapper,
|
||||
},
|
||||
FindAdsByIdsGrpcController,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
findAdsByIdsGrpcController = module.get<FindAdsByIdsGrpcController>(
|
||||
FindAdsByIdsGrpcController,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(findAdsByIdsGrpcController).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return ads', async () => {
|
||||
jest.spyOn(mockQueryBus, 'execute');
|
||||
jest.spyOn(mockAdMapper, 'toResponse');
|
||||
const response = await findAdsByIdsGrpcController.findAllByIds({
|
||||
ids: [
|
||||
'200d61a8-d878-4378-a609-c19ea71633d2',
|
||||
'200d61a8-d878-4378-a609-c19ea71633d3',
|
||||
'200d61a8-d878-4378-a609-c19ea71633d4',
|
||||
],
|
||||
});
|
||||
expect(response.ads).toHaveLength(3);
|
||||
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
|
||||
expect(mockAdMapper.toResponse).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should throw a generic RpcException', async () => {
|
||||
jest.spyOn(mockQueryBus, 'execute');
|
||||
jest.spyOn(mockAdMapper, 'toResponse');
|
||||
expect.assertions(4);
|
||||
try {
|
||||
await findAdsByIdsGrpcController.findAllByIds({
|
||||
ids: [
|
||||
'200d61a8-d878-4378-a609-c19ea71633d2',
|
||||
'200d61a8-d878-4378-a609-c19ea71633d3',
|
||||
'200d61a8-d878-4378-a609-c19ea71633d4',
|
||||
],
|
||||
});
|
||||
} catch (e: any) {
|
||||
expect(e).toBeInstanceOf(RpcException);
|
||||
expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN);
|
||||
}
|
||||
expect(mockQueryBus.execute).toHaveBeenCalledTimes(1);
|
||||
expect(mockAdMapper.toResponse).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
47
tests/unit/ad/interface/has-role.decorator.spec.ts
Normal file
47
tests/unit/ad/interface/has-role.decorator.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { HasRole } from '@modules/ad/interface/grpc-controllers/dtos/validators/decorators/has-role.decorator';
|
||||
import { Validator } from 'class-validator';
|
||||
|
||||
describe('has role decorator', () => {
|
||||
class MyClass {
|
||||
@HasRole('passenger')
|
||||
driver: boolean;
|
||||
|
||||
passenger: boolean;
|
||||
}
|
||||
it('should return a property decorator has a function', () => {
|
||||
const hasRole = HasRole('property');
|
||||
expect(typeof hasRole).toBe('function');
|
||||
});
|
||||
it('should validate an instance with driver only set to true', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.driver = true;
|
||||
myClassInstance.passenger = false;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
it('should validate an instance with passenger only set to true', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.driver = false;
|
||||
myClassInstance.passenger = true;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
it('should validate an instance with driver and passenger set to true', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.driver = true;
|
||||
myClassInstance.passenger = true;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
it('should not validate an instance without driver and passenger set to true', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.driver = false;
|
||||
myClassInstance.passenger = false;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(1);
|
||||
});
|
||||
});
|
||||
59
tests/unit/ad/interface/has-seats.decorator.spec.ts
Normal file
59
tests/unit/ad/interface/has-seats.decorator.spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { HasSeats } from '@modules/ad/interface/grpc-controllers/dtos/validators/decorators/has-seats.decorator';
|
||||
import { Validator } from 'class-validator';
|
||||
|
||||
describe('has seats decorator', () => {
|
||||
class MyClass {
|
||||
@HasSeats('seatsProposed')
|
||||
driver: boolean;
|
||||
@HasSeats('seatsRequested')
|
||||
passenger: boolean;
|
||||
|
||||
seatsProposed?: number;
|
||||
seatsRequested?: number;
|
||||
}
|
||||
it('should return a property decorator has a function', () => {
|
||||
const hasSeats = HasSeats('property');
|
||||
expect(typeof hasSeats).toBe('function');
|
||||
});
|
||||
it('should validate an instance with seats proposed as driver', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.driver = true;
|
||||
myClassInstance.seatsProposed = 3;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
it('should validate an instance with seats requested as passenger', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.passenger = true;
|
||||
myClassInstance.seatsRequested = 1;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
it('should validate an instance with seats proposed as driver and passenger', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.driver = true;
|
||||
myClassInstance.seatsProposed = 3;
|
||||
myClassInstance.passenger = true;
|
||||
myClassInstance.seatsRequested = 1;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
it('should not validate an instance without seats proposed as driver', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.driver = true;
|
||||
myClassInstance.seatsProposed = 0;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(1);
|
||||
});
|
||||
it('should not validate an instance without seats requested as passenger', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.passenger = true;
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
import { HasValidPositionIndexes } from '@modules/ad/interface/grpc-controllers/dtos/validators/decorators/has-valid-position-indexes.decorator';
|
||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||
import { Validator } from 'class-validator';
|
||||
|
||||
describe('valid position indexes decorator', () => {
|
||||
class MyClass {
|
||||
@HasValidPositionIndexes()
|
||||
waypoints: WaypointDto[];
|
||||
}
|
||||
it('should return a property decorator has a function', () => {
|
||||
const hasValidPositionIndexes = HasValidPositionIndexes();
|
||||
expect(typeof hasValidPositionIndexes).toBe('function');
|
||||
});
|
||||
it('should validate an array of waypoints with valid positions', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.waypoints = [
|
||||
{
|
||||
position: 0,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
lon: 49.2628,
|
||||
lat: 4.0347,
|
||||
locality: 'Reims',
|
||||
postalCode: '51454',
|
||||
country: 'France',
|
||||
},
|
||||
];
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
it('should not validate an array of waypoints with invalid positions', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.waypoints = [
|
||||
{
|
||||
position: 1,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
},
|
||||
{
|
||||
position: 1,
|
||||
lon: 49.2628,
|
||||
lat: 4.0347,
|
||||
locality: 'Reims',
|
||||
postalCode: '51454',
|
||||
country: 'France',
|
||||
},
|
||||
];
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { hasValidPositionIndexes } from '@modules/ad/interface/grpc-controllers/dtos/validators/has-valid-position-indexes.validator';
|
||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||
|
||||
describe('addresses position validator', () => {
|
||||
const waypoint1: WaypointDto = {
|
||||
position: 0,
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
houseNumber: '5',
|
||||
street: 'Avenue Foch',
|
||||
locality: 'Nancy',
|
||||
postalCode: '54000',
|
||||
country: 'France',
|
||||
};
|
||||
const waypoint2: WaypointDto = {
|
||||
position: 1,
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
locality: 'Paris',
|
||||
postalCode: '75000',
|
||||
country: 'France',
|
||||
};
|
||||
const waypoint3: WaypointDto = {
|
||||
position: 2,
|
||||
lon: 49.2628,
|
||||
lat: 4.0347,
|
||||
locality: 'Reims',
|
||||
postalCode: '51454',
|
||||
country: 'France',
|
||||
};
|
||||
|
||||
it('should not validate if multiple positions have same value', () => {
|
||||
expect(
|
||||
hasValidPositionIndexes([waypoint1, waypoint1, waypoint2]),
|
||||
).toBeFalsy();
|
||||
});
|
||||
it('should not validate if positions are not consecutives', () => {
|
||||
expect(hasValidPositionIndexes([waypoint1, waypoint3])).toBeFalsy();
|
||||
});
|
||||
it('should not validate if waypoints are empty', () => {
|
||||
expect(hasValidPositionIndexes([])).toBeFalsy();
|
||||
});
|
||||
it('should validate if all positions are ordered', () => {
|
||||
expect(
|
||||
hasValidPositionIndexes([waypoint1, waypoint2, waypoint3]),
|
||||
).toBeTruthy();
|
||||
waypoint1.position = 1;
|
||||
waypoint2.position = 2;
|
||||
waypoint3.position = 3;
|
||||
expect(
|
||||
hasValidPositionIndexes([waypoint1, waypoint2, waypoint3]),
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
45
tests/unit/ad/interface/is-after-or-equal.decorator.spec.ts
Normal file
45
tests/unit/ad/interface/is-after-or-equal.decorator.spec.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { IsAfterOrEqual } from '@modules/ad/interface/grpc-controllers/dtos/validators/decorators/is-after-or-equal.decorator';
|
||||
import { Validator } from 'class-validator';
|
||||
|
||||
describe('Is after or equal decorator', () => {
|
||||
class MyClass {
|
||||
firstDate: string;
|
||||
|
||||
@IsAfterOrEqual('firstDate', {
|
||||
message: 'secondDate must be after or equal to firstDate',
|
||||
})
|
||||
secondDate: string;
|
||||
}
|
||||
|
||||
it('should return a property decorator has a function', () => {
|
||||
const isAfterOrEqual = IsAfterOrEqual('someProperty');
|
||||
expect(typeof isAfterOrEqual).toBe('function');
|
||||
});
|
||||
|
||||
it('should validate a secondDate posterior to firstDate', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.firstDate = '2023-07-20';
|
||||
myClassInstance.secondDate = '2023-07-30';
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not validate a secondDate prior to firstDate', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.firstDate = '2023-07-20';
|
||||
myClassInstance.secondDate = '2023-07-19';
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not validate if dates are invalid', async () => {
|
||||
const myClassInstance = new MyClass();
|
||||
myClassInstance.firstDate = '2023-07-40';
|
||||
myClassInstance.secondDate = '2023-07-19';
|
||||
const validator = new Validator();
|
||||
const validation = await validator.validate(myClassInstance);
|
||||
expect(validation.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import { MatcherAdCreatedMessageHandler } from '@modules/ad/interface/message-handlers/matcher-ad-created.message-handler';
|
||||
import { CommandBus } from '@nestjs/cqrs';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const matcherAdCreatedMessage =
|
||||
'{"id":"4eb6a6af-ecfd-41c3-9118-473a507014d4","driverDuration":"3512","driverDistance":"65845","fwdAzimuth":"90","backAzimuth":"270"}';
|
||||
|
||||
const mockCommandBus = {
|
||||
execute: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Matcher Ad Created Message Handler', () => {
|
||||
let matcherAdCreatedMessageHandler: MatcherAdCreatedMessageHandler;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: CommandBus,
|
||||
useValue: mockCommandBus,
|
||||
},
|
||||
MatcherAdCreatedMessageHandler,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
matcherAdCreatedMessageHandler = module.get<MatcherAdCreatedMessageHandler>(
|
||||
MatcherAdCreatedMessageHandler,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(matcherAdCreatedMessageHandler).toBeDefined();
|
||||
});
|
||||
|
||||
it('should validate an ad', async () => {
|
||||
jest.spyOn(mockCommandBus, 'execute');
|
||||
await matcherAdCreatedMessageHandler.matcherAdCreated(
|
||||
matcherAdCreatedMessage,
|
||||
);
|
||||
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { MatcherAdCreationFailedMessageHandler } from '@modules/ad/interface/message-handlers/matcher-ad-creation-failed.message-handler';
|
||||
import { CommandBus } from '@nestjs/cqrs';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
const matcherAdCreationFailedMessage =
|
||||
'{"id":"4eb6a6af-ecfd-41c3-9118-473a507014d4"}';
|
||||
|
||||
const mockCommandBus = {
|
||||
execute: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Matcher Ad Creation Failed Message Handler', () => {
|
||||
let matcherAdCreationFailedMessageHandler: MatcherAdCreationFailedMessageHandler;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: CommandBus,
|
||||
useValue: mockCommandBus,
|
||||
},
|
||||
MatcherAdCreationFailedMessageHandler,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
matcherAdCreationFailedMessageHandler =
|
||||
module.get<MatcherAdCreationFailedMessageHandler>(
|
||||
MatcherAdCreationFailedMessageHandler,
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(matcherAdCreationFailedMessageHandler).toBeDefined();
|
||||
});
|
||||
|
||||
it('should invalidate an ad', async () => {
|
||||
jest.spyOn(mockCommandBus, 'execute');
|
||||
await matcherAdCreationFailedMessageHandler.matcherAdCreationFailed(
|
||||
matcherAdCreationFailedMessage,
|
||||
);
|
||||
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user