Merge branch 'improveTests' into 'main'
improve tests See merge request v3/services/user!13
This commit is contained in:
commit
c11a316057
|
@ -74,6 +74,8 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO : using any is not good, but needed for nested entities
|
||||||
|
// TODO : Refactor for good clean architecture ?
|
||||||
async create(entity: Partial<T> | any, include?: any): Promise<T> {
|
async create(entity: Partial<T> | any, include?: any): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const res = await this._prisma[this._model].create({
|
const res = await this._prisma[this._model].create({
|
||||||
|
@ -101,7 +103,6 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
||||||
where: { uuid },
|
where: { uuid },
|
||||||
data: entity,
|
data: entity,
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedEntity;
|
return updatedEntity;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof PrismaClientKnownRequestError) {
|
if (e instanceof PrismaClientKnownRequestError) {
|
||||||
|
@ -147,6 +148,27 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
||||||
const entity = await this._prisma[this._model].delete({
|
const entity = await this._prisma[this._model].delete({
|
||||||
where: { uuid },
|
where: { uuid },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof PrismaClientKnownRequestError) {
|
||||||
|
throw new DatabaseException(
|
||||||
|
PrismaClientKnownRequestError.name,
|
||||||
|
e.code,
|
||||||
|
e.message,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new DatabaseException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteMany(where: any): Promise<void> {
|
||||||
|
try {
|
||||||
|
const entity = await this._prisma[this._model].deleteMany({
|
||||||
|
where: where,
|
||||||
|
});
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof PrismaClientKnownRequestError) {
|
if (e instanceof PrismaClientKnownRequestError) {
|
||||||
|
|
|
@ -13,4 +13,5 @@ export interface IRepository<T> {
|
||||||
update(uuid: string, entity: Partial<T>, include?: any): Promise<T>;
|
update(uuid: string, entity: Partial<T>, include?: any): Promise<T>;
|
||||||
updateWhere(where: any, entity: Partial<T> | any, include?: any): Promise<T>;
|
updateWhere(where: any, entity: Partial<T> | any, include?: any): Promise<T>;
|
||||||
delete(uuid: string): Promise<T>;
|
delete(uuid: string): Promise<T>;
|
||||||
|
deleteMany(where: any): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { PrismaService } from '../../src/adapters/secondaries/prisma-service';
|
import { PrismaService } from '../../src/adapters/secondaries/prisma-service';
|
||||||
import { PrismaRepository } from '../../src/adapters/secondaries/prisma-repository.abstract';
|
import { PrismaRepository } from '../../src/adapters/secondaries/prisma-repository.abstract';
|
||||||
import { DatabaseException } from '../../src/exceptions/database.exception';
|
import { DatabaseException } from '../../src/exceptions/database.exception';
|
||||||
|
import { PrismaClientKnownRequestError } from '@prisma/client/runtime';
|
||||||
|
|
||||||
class FakeEntity {
|
class FakeEntity {
|
||||||
uuid?: string;
|
uuid?: string;
|
||||||
|
@ -57,7 +58,20 @@ const mockPrismaService = {
|
||||||
return Promise.resolve([fakeEntities, fakeEntities.length]);
|
return Promise.resolve([fakeEntities, fakeEntities.length]);
|
||||||
}),
|
}),
|
||||||
fake: {
|
fake: {
|
||||||
create: jest.fn().mockResolvedValue(fakeEntityCreated),
|
create: jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce(fakeEntityCreated)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new Error('an unknown error');
|
||||||
|
}),
|
||||||
|
|
||||||
findMany: jest.fn().mockImplementation((params?: any) => {
|
findMany: jest.fn().mockImplementation((params?: any) => {
|
||||||
if (params?.where?.limit == 1) {
|
if (params?.where?.limit == 1) {
|
||||||
|
@ -77,22 +91,66 @@ const mockPrismaService = {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity && params?.where?.uuid == 'unknown') {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
} else if (!entity) {
|
||||||
throw new Error('no entity');
|
throw new Error('no entity');
|
||||||
}
|
}
|
||||||
|
|
||||||
return entity;
|
return entity;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
findFirst: jest.fn().mockImplementation((params?: any) => {
|
findFirst: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
if (params?.where?.name) {
|
if (params?.where?.name) {
|
||||||
return Promise.resolve(
|
return Promise.resolve(
|
||||||
fakeEntities.find((entity) => entity.name === params?.where?.name),
|
fakeEntities.find((entity) => entity.name === params?.where?.name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new Error('an unknown error');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
update: jest.fn().mockImplementation((params: any) => {
|
update: jest
|
||||||
|
.fn()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.mockImplementationOnce((params: any) => {
|
||||||
|
const entity = fakeEntities.find(
|
||||||
|
(entity) => entity.name === params.where.name,
|
||||||
|
);
|
||||||
|
Object.entries(params.data).map(([key, value]) => {
|
||||||
|
entity[key] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.resolve(entity);
|
||||||
|
})
|
||||||
|
.mockImplementation((params: any) => {
|
||||||
const entity = fakeEntities.find(
|
const entity = fakeEntities.find(
|
||||||
(entity) => entity.uuid === params.where.uuid,
|
(entity) => entity.uuid === params.where.uuid,
|
||||||
);
|
);
|
||||||
|
@ -103,7 +161,40 @@ const mockPrismaService = {
|
||||||
return Promise.resolve(entity);
|
return Promise.resolve(entity);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
delete: jest.fn().mockImplementation((params: any) => {
|
delete: jest
|
||||||
|
.fn()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.mockImplementation((params: any) => {
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
fakeEntities.forEach((entity, index) => {
|
||||||
|
if (entity.uuid === params?.where?.uuid) {
|
||||||
|
found = true;
|
||||||
|
fakeEntities.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
deleteMany: jest
|
||||||
|
.fn()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.mockImplementationOnce((params?: any) => {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.mockImplementation((params: any) => {
|
||||||
let found = false;
|
let found = false;
|
||||||
|
|
||||||
fakeEntities.forEach((entity, index) => {
|
fakeEntities.forEach((entity, index) => {
|
||||||
|
@ -180,14 +271,32 @@ describe('PrismaRepository', () => {
|
||||||
expect(newEntity).toBe(fakeEntityCreated);
|
expect(newEntity).toBe(fakeEntityCreated);
|
||||||
expect(prisma.fake.create).toHaveBeenCalledTimes(1);
|
expect(prisma.fake.create).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw a DatabaseException for client error', async () => {
|
||||||
|
await expect(
|
||||||
|
fakeRepository.create(fakeEntityToCreate),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findOne', () => {
|
it('should throw a DatabaseException if uuid is not found', async () => {
|
||||||
|
await expect(
|
||||||
|
fakeRepository.create(fakeEntityToCreate),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('findOneByUuid', () => {
|
||||||
it('should find an entity by uuid', async () => {
|
it('should find an entity by uuid', async () => {
|
||||||
const entity = await fakeRepository.findOneByUuid(fakeEntities[0].uuid);
|
const entity = await fakeRepository.findOneByUuid(fakeEntities[0].uuid);
|
||||||
expect(entity).toBe(fakeEntities[0]);
|
expect(entity).toBe(fakeEntities[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw a DatabaseException for client error', async () => {
|
||||||
|
await expect(
|
||||||
|
fakeRepository.findOneByUuid('unknown'),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw a DatabaseException if uuid is not found', async () => {
|
it('should throw a DatabaseException if uuid is not found', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
fakeRepository.findOneByUuid('wrong-uuid'),
|
fakeRepository.findOneByUuid('wrong-uuid'),
|
||||||
|
@ -195,8 +304,55 @@ describe('PrismaRepository', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('findOne', () => {
|
||||||
|
it('should find one entity', async () => {
|
||||||
|
const entity = await fakeRepository.findOne({
|
||||||
|
name: fakeEntities[0].name,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(entity.name).toBe(fakeEntities[0].name);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw a DatabaseException for client error', async () => {
|
||||||
|
await expect(
|
||||||
|
fakeRepository.findOne({
|
||||||
|
name: fakeEntities[0].name,
|
||||||
|
}),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw a DatabaseException for unknown error', async () => {
|
||||||
|
await expect(
|
||||||
|
fakeRepository.findOne({
|
||||||
|
name: fakeEntities[0].name,
|
||||||
|
}),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('update', () => {
|
describe('update', () => {
|
||||||
it('should update an entity', async () => {
|
it('should throw a DatabaseException for client error', async () => {
|
||||||
|
await expect(
|
||||||
|
fakeRepository.update('fake-uuid', { name: 'error' }),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
|
await expect(
|
||||||
|
fakeRepository.updateWhere({ name: 'error' }, { name: 'new error' }),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update an entity with name', async () => {
|
||||||
|
const newName = 'new-random-name';
|
||||||
|
|
||||||
|
await fakeRepository.updateWhere(
|
||||||
|
{ name: fakeEntities[0].name },
|
||||||
|
{
|
||||||
|
name: newName,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(fakeEntities[0].name).toBe(newName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update an entity with uuid', async () => {
|
||||||
const newName = 'random-name';
|
const newName = 'random-name';
|
||||||
|
|
||||||
await fakeRepository.update(fakeEntities[0].uuid, {
|
await fakeRepository.update(fakeEntities[0].uuid, {
|
||||||
|
@ -209,10 +365,19 @@ describe('PrismaRepository', () => {
|
||||||
await expect(
|
await expect(
|
||||||
fakeRepository.update('fake-uuid', { name: 'error' }),
|
fakeRepository.update('fake-uuid', { name: 'error' }),
|
||||||
).rejects.toBeInstanceOf(DatabaseException);
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
|
await expect(
|
||||||
|
fakeRepository.updateWhere({ name: 'error' }, { name: 'new error' }),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('delete', () => {
|
describe('delete', () => {
|
||||||
|
it('should throw a DatabaseException for client error', async () => {
|
||||||
|
await expect(fakeRepository.delete('fake-uuid')).rejects.toBeInstanceOf(
|
||||||
|
DatabaseException,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('should delete an entity', async () => {
|
it('should delete an entity', async () => {
|
||||||
const savedUuid = fakeEntities[0].uuid;
|
const savedUuid = fakeEntities[0].uuid;
|
||||||
|
|
||||||
|
@ -232,13 +397,29 @@ describe('PrismaRepository', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('findOne', () => {
|
describe('deleteMany', () => {
|
||||||
it('should find one entity', async () => {
|
it('should throw a DatabaseException for client error', async () => {
|
||||||
const entity = await fakeRepository.findOne({
|
await expect(
|
||||||
name: fakeEntities[0].name,
|
fakeRepository.deleteMany({ uuid: 'fake-uuid' }),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(entity.name).toBe(fakeEntities[0].name);
|
it('should delete entities based on their uuid', async () => {
|
||||||
|
const savedUuid = fakeEntities[0].uuid;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const res = await fakeRepository.deleteMany({ uuid: savedUuid });
|
||||||
|
|
||||||
|
const deletedEntity = fakeEntities.find(
|
||||||
|
(entity) => entity.uuid === savedUuid,
|
||||||
|
);
|
||||||
|
expect(deletedEntity).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw an exception if an entity doesn't exist", async () => {
|
||||||
|
await expect(
|
||||||
|
fakeRepository.deleteMany({ uuid: 'fake-uuid' }),
|
||||||
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { FindAllUsersQuery } from '../../queries/find-all-users.query';
|
||||||
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
|
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
|
||||||
import { UserPresenter } from './user.presenter';
|
import { UserPresenter } from './user.presenter';
|
||||||
import { ICollection } from '../../../database/src/interfaces/collection.interface';
|
import { ICollection } from '../../../database/src/interfaces/collection.interface';
|
||||||
import { RpcValidationPipe } from '../../../../utils/rpc.validation-pipe';
|
import { RpcValidationPipe } from '../../../../utils/pipes/rpc.validation-pipe';
|
||||||
|
|
||||||
@UsePipes(
|
@UsePipes(
|
||||||
new RpcValidationPipe({
|
new RpcValidationPipe({
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
|
|
||||||
|
const mockAmqpConnection = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('LoggingMessager', () => {
|
||||||
|
let loggingMessager: LoggingMessager;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
LoggingMessager,
|
||||||
|
{
|
||||||
|
provide: AmqpConnection,
|
||||||
|
useValue: mockAmqpConnection,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
loggingMessager = module.get<LoggingMessager>(LoggingMessager);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(LoggingMessager).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should publish a message', async () => {
|
||||||
|
jest.spyOn(mockAmqpConnection, 'publish');
|
||||||
|
await loggingMessager.publish('user.create.info', 'my-test');
|
||||||
|
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { UserMessager } from '../../adapters/secondaries/user.messager';
|
||||||
|
|
||||||
|
const mockAmqpConnection = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('UserMessager', () => {
|
||||||
|
let userMessager: UserMessager;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
imports: [],
|
||||||
|
providers: [
|
||||||
|
UserMessager,
|
||||||
|
{
|
||||||
|
provide: AmqpConnection,
|
||||||
|
useValue: mockAmqpConnection,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
userMessager = module.get<UserMessager>(UserMessager);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(userMessager).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should publish a message', async () => {
|
||||||
|
jest.spyOn(mockAmqpConnection, 'publish');
|
||||||
|
await userMessager.publish('user.create.info', 'my-test');
|
||||||
|
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { ArgumentMetadata } from '@nestjs/common';
|
||||||
|
import { UpdateUserRequest } from '../../../modules/users/domain/dtos/update-user.request';
|
||||||
|
import { RpcValidationPipe } from '../../pipes/rpc.validation-pipe';
|
||||||
|
|
||||||
|
describe('RpcValidationPipe', () => {
|
||||||
|
it('should not validate request', async () => {
|
||||||
|
const target: RpcValidationPipe = new RpcValidationPipe({
|
||||||
|
whitelist: true,
|
||||||
|
forbidUnknownValues: false,
|
||||||
|
});
|
||||||
|
const metadata: ArgumentMetadata = {
|
||||||
|
type: 'body',
|
||||||
|
metatype: UpdateUserRequest,
|
||||||
|
data: '',
|
||||||
|
};
|
||||||
|
await target.transform(<UpdateUserRequest>{}, metadata).catch((err) => {
|
||||||
|
expect(err.message).toEqual('Rpc Exception');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue