libs tests
This commit is contained in:
parent
8d1d0f82cf
commit
26dab584b2
14
package.json
14
package.json
|
@ -93,10 +93,15 @@
|
|||
"ts"
|
||||
],
|
||||
"modulePathIgnorePatterns": [
|
||||
"libs/",
|
||||
".module.ts",
|
||||
".dto.ts",
|
||||
".constants.ts",
|
||||
".response.ts",
|
||||
".response.base.ts",
|
||||
".port.ts",
|
||||
"libs/exceptions",
|
||||
"libs/types",
|
||||
"prisma-service.base.ts",
|
||||
"main.ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
|
@ -108,10 +113,15 @@
|
|||
"**/*.(t|j)s"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
"libs/",
|
||||
".module.ts",
|
||||
".dto.ts",
|
||||
".constants.ts",
|
||||
".response.ts",
|
||||
".response.base.ts",
|
||||
".port.ts",
|
||||
"libs/exceptions",
|
||||
"libs/types",
|
||||
"prisma-service.base.ts",
|
||||
"main.ts"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export * from './exception.base';
|
||||
export * from './exception.codes';
|
||||
export * from './exceptions';
|
||||
export * from './rpc-exception.codes.enum';
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue