removed oxide dependecy, added create ad grpc controller test

This commit is contained in:
sbriat
2023-06-27 11:02:16 +02:00
parent d22de7c448
commit f644d34068
8 changed files with 265 additions and 154 deletions

View File

@@ -0,0 +1,19 @@
export enum RpcExceptionCode {
OK = 0,
CANCELLED = 1,
UNKNOWN = 2,
INVALID_ARGUMENT = 3,
DEADLINE_EXCEEDED = 4,
NOT_FOUND = 5,
ALREADY_EXISTS = 6,
PERMISSION_DENIED = 7,
RESOURCE_EXHAUSTED = 8,
FAILED_PRECONDITION = 9,
ABORTED = 10,
OUT_OF_RANGE = 11,
UNIMPLEMENTED = 12,
INTERNAL = 13,
UNAVAILABLE = 14,
DATA_LOSS = 15,
UNAUTHENTICATED = 16,
}

View File

@@ -1,11 +1,11 @@
import { ExceptionBase } from '@libs/exceptions';
export class AdAlreadyExistsError extends ExceptionBase {
export class AdAlreadyExistsException extends ExceptionBase {
static readonly message = 'Ad already exists';
public readonly code = 'AD.ALREADY_EXISTS';
constructor(cause?: Error, metadata?: unknown) {
super(AdAlreadyExistsError.message, cause, metadata);
super(AdAlreadyExistsException.message, cause, metadata);
}
}

View File

@@ -5,9 +5,8 @@ import { Inject } from '@nestjs/common';
import { AD_REPOSITORY, PARAMS_PROVIDER } from '@modules/ad/ad.di-tokens';
import { AdRepositoryPort } from '@modules/ad/core/ports/ad.repository.port';
import { DefaultParamsProviderPort } from '@modules/ad/core/ports/default-params-provider.port';
import { Err, Ok, Result } from 'oxide.ts';
import { AggregateID } from '@libs/ddd';
import { AdAlreadyExistsError } from '@modules/ad/core/ad.errors';
import { AdAlreadyExistsException } from '@modules/ad/core/ad.errors';
import { AdEntity } from '@modules/ad/core/ad.entity';
import { ConflictException } from '@libs/exceptions';
import { Waypoint } from '../../types/waypoint';
@@ -25,9 +24,7 @@ export class CreateAdService implements ICommandHandler {
this.defaultParams = defaultParamsProvider.getParams();
}
async execute(
command: CreateAdCommand,
): Promise<Result<AggregateID, AdAlreadyExistsError>> {
async execute(command: CreateAdCommand): Promise<AggregateID> {
const ad = AdEntity.create(
{
userId: command.userId,
@@ -77,10 +74,10 @@ export class CreateAdService implements ICommandHandler {
try {
await this.repository.insert(ad);
return Ok(ad.id);
return ad.id;
} catch (error: any) {
if (error instanceof ConflictException) {
return Err(new AdAlreadyExistsError(error));
throw new AdAlreadyExistsException(error);
}
throw error;
}

View File

@@ -4,10 +4,10 @@ import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { RpcValidationPipe } from '../../../../utils/pipes/rpc.validation-pipe';
import { CreateAdRequestDto } from './dtos/create-ad.request.dto';
import { CreateAdCommand } from '../../core/commands/create-ad/create-ad.command';
import { Result, match } from 'oxide.ts';
import { AggregateID } from '@libs/ddd';
import { AdAlreadyExistsError } from '../../core/ad.errors';
import { AdAlreadyExistsException } from '../../core/ad.errors';
import { IdResponse } from '@libs/api/id.response.dto';
import { RpcExceptionCode } from '@libs/exceptions/rpc-exception.codes.enum';
@UsePipes(
new RpcValidationPipe({
@@ -21,22 +21,18 @@ export class CreateAdGrpcController {
@GrpcMethod('AdsService', 'Create')
async create(data: CreateAdRequestDto): Promise<IdResponse> {
const result: Result<AggregateID, AdAlreadyExistsError> =
await this.commandBus.execute(new CreateAdCommand(data));
// Deciding what to do with a Result (similar to Rust matching)
// if Ok we return a response with an id
// if Error decide what to do with it depending on its type
return match(result, {
Ok: (id: string) => new IdResponse(id),
Err: (error: Error) => {
if (error instanceof AdAlreadyExistsError)
throw new RpcException({
code: 6,
message: 'Ad already exists',
});
throw new RpcException({});
},
});
try {
const aggregateID: AggregateID = await this.commandBus.execute(
new CreateAdCommand(data),
);
return new IdResponse(aggregateID);
} catch (error: any) {
if (error instanceof AdAlreadyExistsException)
throw new RpcException({
code: RpcExceptionCode.ALREADY_EXISTS,
message: error.message,
});
throw new RpcException({});
}
}
}

View File

@@ -6,9 +6,8 @@ import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoin
import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto';
import { Frequency } from '@modules/ad/core/ad.types';
import { CreateAdCommand } from '@modules/ad/core/commands/create-ad/create-ad.command';
import { Result } from 'oxide.ts';
import { AggregateID } from '@libs/ddd';
import { AdAlreadyExistsError } from '@modules/ad/core/ad.errors';
import { AdAlreadyExistsException } from '@modules/ad/core/ad.errors';
import { AdEntity } from '@modules/ad/core/ad.entity';
import { ConflictException } from '@libs/exceptions';
@@ -107,9 +106,10 @@ describe('create-ad.service', () => {
AdEntity.create = jest.fn().mockReturnValue({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
});
const result: Result<AggregateID, AdAlreadyExistsError> =
await createAdService.execute(createAdCommand);
expect(result.unwrap()).toBe('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({
@@ -119,13 +119,13 @@ describe('create-ad.service', () => {
createAdService.execute(createAdCommand),
).rejects.toBeInstanceOf(Error);
});
it('should return an Err if Ad already exists', async () => {
it('should throw an exception if Ad already exists', async () => {
AdEntity.create = jest.fn().mockReturnValue({
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
});
const result: Result<AggregateID, AdAlreadyExistsError> =
await createAdService.execute(createAdCommand);
expect(result.isErr()).toBeTruthy();
await expect(
createAdService.execute(createAdCommand),
).rejects.toBeInstanceOf(AdAlreadyExistsException);
});
});
});

View File

@@ -0,0 +1,106 @@
import { IdResponse } from '@libs/api/id.response.dto';
import { RpcExceptionCode } from '@libs/exceptions/rpc-exception.codes.enum';
import { AdAlreadyExistsException } from '@modules/ad/core/ad.errors';
import { Frequency } from '@modules/ad/core/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,
lon: 48.689445,
lat: 6.17651,
houseNumber: '5',
street: 'Avenue Foch',
locality: 'Nancy',
postalCode: '54000',
country: 'France',
};
const destinationWaypoint: WaypointDto = {
position: 1,
lon: 48.8566,
lat: 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: {
thu: '08:15',
},
driver: false,
passenger: true,
seatsRequested: 1,
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,
);
});
it('should be defined', () => {
expect(createAdGrpcController).toBeDefined();
});
it('should create a new ad', async () => {
const result: IdResponse = await createAdGrpcController.create(
punctualCreateAdRequest,
);
expect(result).toBeInstanceOf(IdResponse);
expect(result.id).toBe('200d61a8-d878-4378-a609-c19ea71633d2');
});
it('should throw an dedicated RpcException if ad already exists', async () => {
expect.assertions(2);
try {
await createAdGrpcController.create(punctualCreateAdRequest);
} catch (e: any) {
expect(e).toBeInstanceOf(RpcException);
expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS);
}
});
it('should throw a generic RpcException', async () => {
expect.assertions(2);
try {
await createAdGrpcController.create(punctualCreateAdRequest);
} catch (e: any) {
expect(e).toBeInstanceOf(RpcException);
expect(e.error.code).toBeUndefined();
}
});
});