improve tests
This commit is contained in:
parent
a45d91edcc
commit
4e1fb9a8d6
|
@ -30,7 +30,8 @@
|
|||
"dotenv-cli": "^6.0.0",
|
||||
"ioredis": "^5.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.2.0"
|
||||
"rxjs": "^7.2.0",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
|
|
|
@ -48,7 +48,8 @@
|
|||
"dotenv-cli": "^6.0.0",
|
||||
"ioredis": "^5.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.2.0"
|
||||
"rxjs": "^7.2.0",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
|
|
|
@ -7,7 +7,7 @@ CREATE TABLE "territory" (
|
|||
"name" TEXT NOT NULL,
|
||||
"shape" geometry NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "territory_pkey" PRIMARY KEY ("uuid")
|
||||
);
|
||||
|
|
|
@ -17,7 +17,7 @@ model Territory {
|
|||
name String @unique
|
||||
shape Unsupported("geometry")
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
|
||||
@@index([shape], name: "shape_idx", type: Gist)
|
||||
@@map("territory")
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime';
|
||||
import { Point } from '../../domain/point.type';
|
||||
import { DatabaseException } from '../../exceptions/database.exception';
|
||||
import { ICollection } from '../../interfaces/collection.interface';
|
||||
import { IRepository } from '../../interfaces/repository.interface';
|
||||
import { PrismaService } from './prisma-service';
|
||||
import { Territory } from 'src/modules/territories/domain/entities/territory';
|
||||
|
||||
/**
|
||||
* Child classes MUST redefined _model property with appropriate model name
|
||||
|
@ -185,23 +183,36 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
|||
}
|
||||
}
|
||||
|
||||
async findForPoint(point: Point): Promise<ICollection<T>> {
|
||||
const strPoint = `SELECT uuid, name FROM ${this._model} WHERE ST_Intersects(ST_GeomFromText('POINT(${point.lon} ${point.lat})',4326),shape) = true`;
|
||||
const territories: Array<T> = await this._prisma.$queryRawUnsafe(strPoint);
|
||||
async findAllByQuery(
|
||||
include: Array<string>,
|
||||
where: Array<string>,
|
||||
): Promise<ICollection<T>> {
|
||||
const query = `SELECT ${include.join(',')} FROM ${
|
||||
this._model
|
||||
} WHERE ${where.join(' AND ')}`;
|
||||
const data: Array<T> = await this._prisma.$queryRawUnsafe(query);
|
||||
return Promise.resolve({
|
||||
data: territories,
|
||||
total: territories.length,
|
||||
data,
|
||||
total: data.length,
|
||||
});
|
||||
}
|
||||
|
||||
async createTerritory(territory: Territory): Promise<T> {
|
||||
const command = `INSERT INTO ${this._model} VALUES ('bb281075-1b98-4456-89d6-c643d3044a91','${territory.name}', ST_GeomFromGeoJSON('{"type":"MultiPolygon","coordinates":${territory.shape}}'),'2023-02-07 15:58:00','2023-02-07 15:58:00')`;
|
||||
const affectedRowNumber = await this._prisma.$executeRawUnsafe(command);
|
||||
if (affectedRowNumber == 1) {
|
||||
return this.findOne({
|
||||
name: territory.name,
|
||||
});
|
||||
async createWithFields(fields: object): Promise<number> {
|
||||
try {
|
||||
const command = `INSERT INTO ${this._model} (${Object.keys(fields).join(
|
||||
',',
|
||||
)}) VALUES (${Object.values(fields).join(',')})`;
|
||||
return await this._prisma.$executeRawUnsafe(command);
|
||||
} catch (e) {
|
||||
if (e instanceof PrismaClientKnownRequestError) {
|
||||
throw new DatabaseException(
|
||||
PrismaClientKnownRequestError.name,
|
||||
e.code,
|
||||
e.message,
|
||||
);
|
||||
} else {
|
||||
throw new DatabaseException();
|
||||
}
|
||||
}
|
||||
throw new DatabaseException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,35 @@
|
|||
import { PrismaRepository } from '../adapters/secondaries/prisma-repository.abstract';
|
||||
import { ICollection } from '../interfaces/collection.interface';
|
||||
import { Point } from './point.type';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { Territory } from 'src/modules/territories/domain/entities/territory';
|
||||
import { DatabaseException } from '../exceptions/database.exception';
|
||||
|
||||
export class TerritoryRepository<T> extends PrismaRepository<T> {}
|
||||
export class TerritoryRepository<T> extends PrismaRepository<T> {
|
||||
async findForPoint(point: Point): Promise<ICollection<T>> {
|
||||
return await this.findAllByQuery(
|
||||
['uuid', 'name'],
|
||||
[
|
||||
`ST_Intersects(ST_GeomFromText('POINT(${point.lon} ${point.lat})',4326),shape) = true`,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
async createTerritory(territory: Territory): Promise<T> {
|
||||
try {
|
||||
const affectedRowNumber = await this.createWithFields({
|
||||
uuid: `'${uuidv4()}'`,
|
||||
name: `'${territory.name}'`,
|
||||
shape: `ST_GeomFromGeoJSON('{"type":"MultiPolygon","coordinates":${territory.shape}}')`,
|
||||
});
|
||||
if (affectedRowNumber == 1) {
|
||||
return this.findOne({
|
||||
name: territory.name,
|
||||
});
|
||||
}
|
||||
throw new DatabaseException();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,24 @@ const mockPrismaService = {
|
|||
|
||||
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');
|
||||
}),
|
||||
fake: {
|
||||
create: jest
|
||||
.fn()
|
||||
|
@ -422,4 +440,47 @@ describe('PrismaRepository', () => {
|
|||
).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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -104,7 +104,7 @@ export class TerritoriesController {
|
|||
return this._mapper.map(territory, Territory, TerritoryPresenter);
|
||||
} catch (e) {
|
||||
if (e instanceof DatabaseException) {
|
||||
if (e.message.includes('Already exists')) {
|
||||
if (e.message.includes('already exists')) {
|
||||
throw new RpcException({
|
||||
code: 6,
|
||||
message: 'Territory already exists',
|
||||
|
|
|
@ -34,7 +34,7 @@ export class CreateTerritoryUseCase {
|
|||
return territory;
|
||||
} catch (error) {
|
||||
let key = 'territory.create.crit';
|
||||
if (error.message.includes('Already exists')) {
|
||||
if (error.message.includes('already exists')) {
|
||||
key = 'territory.create.warning';
|
||||
}
|
||||
this._loggingMessager.publish(
|
||||
|
|
Loading…
Reference in New Issue