libs tests

This commit is contained in:
sbriat 2023-06-27 17:51:20 +02:00
parent 8d1d0f82cf
commit 26dab584b2
5 changed files with 328 additions and 2 deletions

View File

@ -93,10 +93,15 @@
"ts" "ts"
], ],
"modulePathIgnorePatterns": [ "modulePathIgnorePatterns": [
"libs/",
".module.ts", ".module.ts",
".dto.ts", ".dto.ts",
".constants.ts", ".constants.ts",
".response.ts",
".response.base.ts",
".port.ts",
"libs/exceptions",
"libs/types",
"prisma-service.base.ts",
"main.ts" "main.ts"
], ],
"rootDir": "src", "rootDir": "src",
@ -108,10 +113,15 @@
"**/*.(t|j)s" "**/*.(t|j)s"
], ],
"coveragePathIgnorePatterns": [ "coveragePathIgnorePatterns": [
"libs/",
".module.ts", ".module.ts",
".dto.ts", ".dto.ts",
".constants.ts", ".constants.ts",
".response.ts",
".response.base.ts",
".port.ts",
"libs/exceptions",
"libs/types",
"prisma-service.base.ts",
"main.ts" "main.ts"
], ],
"coverageDirectory": "../coverage", "coverageDirectory": "../coverage",

View File

@ -1,3 +1,4 @@
export * from './exception.base'; export * from './exception.base';
export * from './exception.codes'; export * from './exception.codes';
export * from './exceptions'; export * from './exceptions';
export * from './rpc-exception.codes.enum';

View File

@ -0,0 +1,250 @@
import { ResponseBase } from '@libs/api/response.base';
import { PrismaRepositoryBase } from '@libs/db/prisma-repository.base';
import { PrismaService } from '@libs/db/prisma.service';
import { AggregateID, AggregateRoot, Mapper, RepositoryPort } from '@libs/ddd';
import { ConflictException } from '@libs/exceptions';
import { Injectable, Logger } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing';
import { Prisma } from '@prisma/client';
import { v4 } from 'uuid';
export interface FakeProps {
name: string;
}
export interface CreateFakeProps {
name: string;
}
class FakeEntity extends AggregateRoot<FakeProps> {
protected readonly _id: AggregateID;
static create = (create: CreateFakeProps): FakeEntity => {
const id = v4();
const props: FakeProps = { ...create };
const fake = new FakeEntity({ id, props });
return fake;
};
validate(): void {
// not implemented
}
}
type FakeModel = {
uuid: string;
name: string;
createdAt: Date;
updatedAt: Date;
};
type FakeRepositoryPort = RepositoryPort<FakeEntity>;
class FakeResponseDto extends ResponseBase {
name: string;
}
const fakeDbWriteModel: FakeModel = {
uuid: 'd567ea3b-4981-43c9-9449-a409b5fa9fed',
name: 'fakeName',
createdAt: new Date('2023-06-28T16:02:00Z'),
updatedAt: new Date('2023-06-28T16:02:00Z'),
};
let modelId = 2;
const modelUuid = 'uuid-';
const modelName = 'name-';
const createRandomModel = (): FakeModel => {
const fakeModel: FakeModel = {
uuid: `${modelUuid}${modelId}`,
name: `${modelName}${modelId}`,
createdAt: new Date('2023-06-30T08:00:00Z'),
updatedAt: new Date('2023-06-30T08:00:00Z'),
};
modelId++;
return fakeModel;
};
const fakeModels: FakeModel[] = [];
Array.from({ length: 10 }).forEach(() => {
fakeModels.push(createRandomModel());
});
@Injectable()
class FakeMapper
implements Mapper<FakeEntity, FakeModel, FakeModel, FakeResponseDto>
{
toPersistence = (entity: FakeEntity): FakeModel => {
const copy = entity.getProps();
const record: FakeModel = {
uuid: copy.id,
name: copy.name,
createdAt: copy.createdAt,
updatedAt: copy.updatedAt,
};
return record;
};
toDomain = (record: FakeModel): FakeEntity => {
const entity = new FakeEntity({
id: record.uuid,
createdAt: new Date(record.createdAt),
updatedAt: new Date(record.updatedAt),
props: {
name: record.name,
},
});
return entity;
};
toResponse = (entity: FakeEntity): FakeResponseDto => {
const props = entity.getProps();
const response = new FakeResponseDto(entity);
response.name = props.name;
return response;
};
}
@Injectable()
class FakePrismaService extends PrismaService {
fake: any;
}
const mockPrismaService = {
$queryRaw: jest
.fn()
.mockImplementationOnce(() => {
throw new Prisma.PrismaClientKnownRequestError('unknown request', {
code: 'code',
clientVersion: 'version',
});
})
.mockImplementationOnce(() => {
return true;
})
.mockImplementation(() => {
throw new Prisma.PrismaClientKnownRequestError('Database unavailable', {
code: 'code',
clientVersion: 'version',
});
}),
fake: {
create: jest
.fn()
.mockResolvedValueOnce(fakeDbWriteModel)
.mockImplementationOnce(() => {
throw new Prisma.PrismaClientKnownRequestError('Already exists', {
code: 'code',
clientVersion: 'version',
});
})
.mockImplementationOnce(() => {
throw new Error('an unknown error');
}),
findUnique: jest.fn().mockImplementation(async (params?: any) => {
let model: FakeModel;
if (params?.where?.uuid) {
model = fakeModels.find(
(entity) => entity.uuid === params?.where?.uuid,
);
}
if (!model && params?.where?.uuid == 'unknown') {
throw new Prisma.PrismaClientKnownRequestError('unknown request', {
code: 'code',
clientVersion: 'version',
});
} else if (!model) {
throw new Error('No record found');
}
return model;
}),
},
};
@Injectable()
class FakeRepository
extends PrismaRepositoryBase<FakeEntity, FakeModel, FakeModel>
implements FakeRepositoryPort
{
constructor(
prisma: FakePrismaService,
mapper: FakeMapper,
eventEmitter: EventEmitter2,
) {
super(
prisma.fake,
prisma,
mapper,
eventEmitter,
new Logger(FakeRepository.name),
);
}
}
describe('PrismaRepositoryBase', () => {
let fakeRepository: FakeRepository;
let prisma: FakePrismaService;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
EventEmitter2,
FakeRepository,
FakeMapper,
{
provide: FakePrismaService,
useValue: mockPrismaService,
},
],
}).compile();
fakeRepository = module.get<FakeRepository>(FakeRepository);
prisma = module.get<FakePrismaService>(FakePrismaService);
});
it('should be defined', () => {
expect(fakeRepository).toBeDefined();
expect(prisma).toBeDefined();
});
describe('insert', () => {
it('should create a model', async () => {
jest.spyOn(prisma.fake, 'create');
await fakeRepository.insert(
FakeEntity.create({
name: 'someFakeName',
}),
);
expect(prisma.fake.create).toHaveBeenCalledTimes(1);
});
it('should throw a ConflictException if model already exists', async () => {
await expect(
fakeRepository.insert(
FakeEntity.create({
name: 'someFakeName',
}),
),
).rejects.toBeInstanceOf(ConflictException);
});
it('should throw an exception an error occurs', async () => {
await expect(
fakeRepository.insert(
FakeEntity.create({
name: 'someFakeName',
}),
),
).rejects.toBeInstanceOf(Error);
});
});
});

View File

@ -0,0 +1,65 @@
import { Guard } from '@libs/guard';
describe('Guard', () => {
describe('isEmpty', () => {
it('should return false for a number', () => {
expect(Guard.isEmpty(1)).toBeFalsy();
});
it('should return false for a falsy boolean', () => {
expect(Guard.isEmpty(false)).toBeFalsy();
});
it('should return false for a truthy boolean', () => {
expect(Guard.isEmpty(true)).toBeFalsy();
});
it('should return true for undefined', () => {
expect(Guard.isEmpty(undefined)).toBeTruthy();
});
it('should return true for null', () => {
expect(Guard.isEmpty(null)).toBeTruthy();
});
it('should return false for a Date', () => {
expect(Guard.isEmpty(new Date('2023-06-28'))).toBeFalsy();
});
it('should return false for an object with keys', () => {
expect(Guard.isEmpty({ key: 'value' })).toBeFalsy();
});
it('should return true for an object without keys', () => {
expect(Guard.isEmpty({})).toBeTruthy();
});
it('should return true for an array without values', () => {
expect(Guard.isEmpty([])).toBeTruthy();
});
it('should return true for an array with only empty values', () => {
expect(Guard.isEmpty([null, undefined])).toBeTruthy();
});
it('should return false for an array with some empty values', () => {
expect(Guard.isEmpty([1, null, undefined])).toBeFalsy();
});
it('should return true for an empty string', () => {
expect(Guard.isEmpty('')).toBeTruthy();
});
});
describe('lengthIsBetween', () => {
it('should return true for a string in the range', () => {
expect(Guard.lengthIsBetween('test', 0, 4)).toBeTruthy();
});
it('should return true for a number in the range', () => {
expect(Guard.lengthIsBetween(2, 0, 4)).toBeTruthy();
});
it('should return true for an array with number of elements in the range', () => {
expect(Guard.lengthIsBetween([1, 2, 3], 0, 4)).toBeTruthy();
});
it('should return false for a string not in the range', () => {
expect(Guard.lengthIsBetween('test', 5, 9)).toBeFalsy();
});
it('should return false for a number not in the range', () => {
expect(Guard.lengthIsBetween(2, 3, 6)).toBeFalsy();
});
it('should return false for an array with number of elements not in the range', () => {
expect(Guard.lengthIsBetween([1, 2, 3], 10, 12)).toBeFalsy();
});
it('should throw an exception if value is empty', () => {
expect(() => Guard.lengthIsBetween(undefined, 0, 4)).toThrow();
});
});
});