improve tests

This commit is contained in:
sbriat 2023-02-08 12:19:52 +01:00
parent a45d91edcc
commit 4e1fb9a8d6
9 changed files with 128 additions and 22 deletions

3
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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")
);

View File

@ -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")

View File

@ -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();
}
}

View File

@ -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;
}
}
}

View File

@ -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);
});
});
});

View File

@ -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',

View File

@ -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(