572 lines
17 KiB
TypeScript
572 lines
17 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { Test, TestingModule } from '@nestjs/testing';
|
|
import { PrismaService } from '../../src/adapters/secondaries/prisma-service';
|
|
import { PrismaRepository } from '../../src/adapters/secondaries/prisma-repository.abstract';
|
|
import { DatabaseException } from '../../src/exceptions/database.exception';
|
|
import { PrismaClientKnownRequestError } from '@prisma/client/runtime';
|
|
|
|
class FakeEntity {
|
|
uuid?: string;
|
|
name: string;
|
|
}
|
|
|
|
let entityId = 2;
|
|
const entityUuid = 'uuid-';
|
|
const entityName = 'name-';
|
|
|
|
const createRandomEntity = (): FakeEntity => {
|
|
const entity: FakeEntity = {
|
|
uuid: `${entityUuid}${entityId}`,
|
|
name: `${entityName}${entityId}`,
|
|
};
|
|
|
|
entityId++;
|
|
|
|
return entity;
|
|
};
|
|
|
|
const fakeEntityToCreate: FakeEntity = {
|
|
name: 'test',
|
|
};
|
|
|
|
const fakeEntityCreated: FakeEntity = {
|
|
...fakeEntityToCreate,
|
|
uuid: 'some-uuid',
|
|
};
|
|
|
|
const fakeEntities: FakeEntity[] = [];
|
|
Array.from({ length: 10 }).forEach(() => {
|
|
fakeEntities.push(createRandomEntity());
|
|
});
|
|
|
|
@Injectable()
|
|
class FakePrismaRepository extends PrismaRepository<FakeEntity> {
|
|
protected _model = 'fake';
|
|
}
|
|
|
|
class FakePrismaService extends PrismaService {
|
|
fake: any;
|
|
}
|
|
|
|
const mockPrismaService = {
|
|
$transaction: jest.fn().mockImplementation(async (data: any) => {
|
|
const entities = await data[0];
|
|
if (entities.length == 1) {
|
|
return Promise.resolve([[fakeEntityCreated], 1]);
|
|
}
|
|
|
|
return Promise.resolve([fakeEntities, fakeEntities.length]);
|
|
}),
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
$queryRawUnsafe: jest.fn().mockImplementation((query?: string) => {
|
|
return Promise.resolve(fakeEntities);
|
|
}),
|
|
$executeRawUnsafe: jest
|
|
.fn()
|
|
.mockResolvedValueOnce(fakeEntityCreated)
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
.mockImplementationOnce((fields: object) => {
|
|
throw new PrismaClientKnownRequestError('unknown request', {
|
|
code: 'code',
|
|
clientVersion: 'version',
|
|
});
|
|
})
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
.mockImplementationOnce((fields: object) => {
|
|
throw new Error('an unknown error');
|
|
})
|
|
.mockResolvedValueOnce(fakeEntityCreated)
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
.mockImplementationOnce((fields: object) => {
|
|
throw new PrismaClientKnownRequestError('unknown request', {
|
|
code: 'code',
|
|
clientVersion: 'version',
|
|
});
|
|
})
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
.mockImplementationOnce((fields: object) => {
|
|
throw new Error('an unknown error');
|
|
}),
|
|
$queryRaw: jest
|
|
.fn()
|
|
.mockImplementationOnce(() => {
|
|
throw new PrismaClientKnownRequestError('unknown request', {
|
|
code: 'code',
|
|
clientVersion: 'version',
|
|
});
|
|
})
|
|
.mockImplementationOnce(() => {
|
|
return true;
|
|
})
|
|
.mockImplementation(() => {
|
|
throw new PrismaClientKnownRequestError('Database unavailable', {
|
|
code: 'code',
|
|
clientVersion: 'version',
|
|
});
|
|
}),
|
|
fake: {
|
|
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) => {
|
|
if (params?.where?.limit == 1) {
|
|
return Promise.resolve([fakeEntityCreated]);
|
|
}
|
|
|
|
return Promise.resolve(fakeEntities);
|
|
}),
|
|
count: jest.fn().mockResolvedValue(fakeEntities.length),
|
|
|
|
findUnique: jest.fn().mockImplementation(async (params?: any) => {
|
|
let entity;
|
|
|
|
if (params?.where?.uuid) {
|
|
entity = fakeEntities.find(
|
|
(entity) => entity.uuid === params?.where?.uuid,
|
|
);
|
|
}
|
|
|
|
if (!entity && params?.where?.uuid == 'unknown') {
|
|
throw new PrismaClientKnownRequestError('unknown request', {
|
|
code: 'code',
|
|
clientVersion: 'version',
|
|
});
|
|
} else if (!entity) {
|
|
throw new Error('no entity');
|
|
}
|
|
|
|
return entity;
|
|
}),
|
|
|
|
findFirst: jest
|
|
.fn()
|
|
.mockImplementationOnce((params?: any) => {
|
|
if (params?.where?.name) {
|
|
return Promise.resolve(
|
|
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()
|
|
// 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(
|
|
(entity) => entity.uuid === params.where.uuid,
|
|
);
|
|
Object.entries(params.data).map(([key, value]) => {
|
|
entity[key] = value;
|
|
});
|
|
|
|
return Promise.resolve(entity);
|
|
}),
|
|
|
|
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;
|
|
|
|
fakeEntities.forEach((entity, index) => {
|
|
if (entity.uuid === params?.where?.uuid) {
|
|
found = true;
|
|
fakeEntities.splice(index, 1);
|
|
}
|
|
});
|
|
|
|
if (!found) {
|
|
throw new Error();
|
|
}
|
|
}),
|
|
},
|
|
};
|
|
|
|
describe('PrismaRepository', () => {
|
|
let fakeRepository: FakePrismaRepository;
|
|
let prisma: FakePrismaService;
|
|
|
|
beforeAll(async () => {
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
providers: [
|
|
FakePrismaRepository,
|
|
{
|
|
provide: PrismaService,
|
|
useValue: mockPrismaService,
|
|
},
|
|
],
|
|
}).compile();
|
|
|
|
fakeRepository = module.get<FakePrismaRepository>(FakePrismaRepository);
|
|
prisma = module.get<PrismaService>(PrismaService) as FakePrismaService;
|
|
});
|
|
|
|
it('should be defined', () => {
|
|
expect(fakeRepository).toBeDefined();
|
|
expect(prisma).toBeDefined();
|
|
});
|
|
|
|
describe('findAll', () => {
|
|
it('should return an array of entities', async () => {
|
|
jest.spyOn(prisma.fake, 'findMany');
|
|
jest.spyOn(prisma.fake, 'count');
|
|
jest.spyOn(prisma, '$transaction');
|
|
|
|
const entities = await fakeRepository.findAll();
|
|
expect(entities).toStrictEqual({
|
|
data: fakeEntities,
|
|
total: fakeEntities.length,
|
|
});
|
|
});
|
|
|
|
it('should return an array containing only one entity', async () => {
|
|
const entities = await fakeRepository.findAll(1, 10, { limit: 1 });
|
|
|
|
expect(prisma.fake.findMany).toHaveBeenCalledWith({
|
|
skip: 0,
|
|
take: 10,
|
|
where: { limit: 1 },
|
|
});
|
|
expect(entities).toEqual({
|
|
data: [fakeEntityCreated],
|
|
total: 1,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('create', () => {
|
|
it('should create an entity', async () => {
|
|
jest.spyOn(prisma.fake, 'create');
|
|
|
|
const newEntity = await fakeRepository.create(fakeEntityToCreate);
|
|
expect(newEntity).toBe(fakeEntityCreated);
|
|
expect(prisma.fake.create).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should throw a DatabaseException for client error', async () => {
|
|
await expect(
|
|
fakeRepository.create(fakeEntityToCreate),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
|
|
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 () => {
|
|
const entity = await fakeRepository.findOneByUuid(fakeEntities[0].uuid);
|
|
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 () => {
|
|
await expect(
|
|
fakeRepository.findOneByUuid('wrong-uuid'),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
});
|
|
|
|
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', () => {
|
|
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';
|
|
|
|
await fakeRepository.update(fakeEntities[0].uuid, {
|
|
name: newName,
|
|
});
|
|
expect(fakeEntities[0].name).toBe(newName);
|
|
});
|
|
|
|
it("should throw an exception if an entity doesn't exist", async () => {
|
|
await expect(
|
|
fakeRepository.update('fake-uuid', { name: 'error' }),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
await expect(
|
|
fakeRepository.updateWhere({ name: 'error' }, { name: 'new error' }),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
});
|
|
|
|
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 () => {
|
|
const savedUuid = fakeEntities[0].uuid;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
const res = await fakeRepository.delete(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.delete('fake-uuid')).rejects.toBeInstanceOf(
|
|
DatabaseException,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('deleteMany', () => {
|
|
it('should throw a DatabaseException for client error', async () => {
|
|
await expect(
|
|
fakeRepository.deleteMany({ uuid: 'fake-uuid' }),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
|
|
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);
|
|
});
|
|
});
|
|
|
|
describe('findAllByquery', () => {
|
|
it('should return an array of entities', async () => {
|
|
const entities = await fakeRepository.findAllByQuery(
|
|
['uuid', 'name'],
|
|
['name is not null'],
|
|
);
|
|
expect(entities).toStrictEqual({
|
|
data: fakeEntities,
|
|
total: fakeEntities.length,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('createWithFields', () => {
|
|
it('should create an entity', async () => {
|
|
jest.spyOn(prisma, '$queryRawUnsafe');
|
|
|
|
const newEntity = await fakeRepository.createWithFields({
|
|
uuid: '804319b3-a09b-4491-9f82-7976bfce0aff',
|
|
name: 'my-name',
|
|
});
|
|
expect(newEntity).toBe(fakeEntityCreated);
|
|
expect(prisma.$queryRawUnsafe).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should throw a DatabaseException for client error', async () => {
|
|
await expect(
|
|
fakeRepository.createWithFields({
|
|
uuid: '804319b3-a09b-4491-9f82-7976bfce0aff',
|
|
name: 'my-name',
|
|
}),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
|
|
it('should throw a DatabaseException if uuid is not found', async () => {
|
|
await expect(
|
|
fakeRepository.createWithFields({
|
|
name: 'my-name',
|
|
}),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
});
|
|
|
|
describe('updateWithFields', () => {
|
|
it('should update an entity', async () => {
|
|
jest.spyOn(prisma, '$queryRawUnsafe');
|
|
|
|
const updatedEntity = await fakeRepository.updateWithFields(
|
|
'804319b3-a09b-4491-9f82-7976bfce0aff',
|
|
{
|
|
name: 'my-name',
|
|
},
|
|
);
|
|
expect(updatedEntity).toBe(fakeEntityCreated);
|
|
expect(prisma.$queryRawUnsafe).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should throw a DatabaseException for client error', async () => {
|
|
await expect(
|
|
fakeRepository.updateWithFields(
|
|
'804319b3-a09b-4491-9f82-7976bfce0aff',
|
|
{
|
|
name: 'my-name',
|
|
},
|
|
),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
|
|
it('should throw a DatabaseException if uuid is not found', async () => {
|
|
await expect(
|
|
fakeRepository.updateWithFields(
|
|
'804319b3-a09b-4491-9f82-7976bfce0aff',
|
|
{
|
|
name: 'my-name',
|
|
},
|
|
),
|
|
).rejects.toBeInstanceOf(DatabaseException);
|
|
});
|
|
});
|
|
|
|
describe('healthCheck', () => {
|
|
it('should throw a DatabaseException for client error', async () => {
|
|
await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf(
|
|
DatabaseException,
|
|
);
|
|
});
|
|
|
|
it('should return a healthy result', async () => {
|
|
const res = await fakeRepository.healthCheck();
|
|
expect(res).toBeTruthy();
|
|
});
|
|
|
|
it('should throw an exception if database is not available', async () => {
|
|
await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf(
|
|
DatabaseException,
|
|
);
|
|
});
|
|
});
|
|
});
|