integration tests
This commit is contained in:
parent
6a08a90f02
commit
ab9bb4897e
|
@ -1,18 +1,11 @@
|
|||
# SERVICE
|
||||
SERVICE_CONTAINER=v3-user
|
||||
SERVICE_URL=0.0.0.0
|
||||
SERVICE_PORT=5001
|
||||
|
||||
# PRISMA
|
||||
DATABASE_URL="postgresql://user:user@v3-user-db:5432/user?schema=public"
|
||||
|
||||
# RABBIT MQ
|
||||
RMQ_URI=amqp://localhost:5672
|
||||
RMQ_URI=amqp://v3-broker:5672
|
||||
|
||||
# POSTGRES
|
||||
POSTGRES_CONTAINER=v3-user-db
|
||||
POSTGRES_IMAGE=postgres:15.0
|
||||
POSTGRES_DB=user
|
||||
POSTGRES_PASSWORD=user
|
||||
POSTGRES_USER=user
|
||||
POSTGRES_PORT=5501
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# SERVICE
|
||||
SERVICE_URL=0.0.0.0
|
||||
|
||||
# PRISMA
|
||||
DATABASE_URL="postgresql://user:user@localhost:5601/user?schema=public"
|
||||
|
||||
# RABBIT MQ
|
||||
RMQ_URI=amqp://v3-broker:5672
|
||||
|
||||
# POSTGRES
|
||||
POSTGRES_IMAGE=postgres:15.0
|
57
README.md
57
README.md
|
@ -6,25 +6,35 @@ User-related data management.
|
|||
|
||||
You need [Docker](https://docs.docker.com/engine/) and its [compose](https://docs.docker.com/compose/) plugin.
|
||||
|
||||
You also need NodeJS installed locally : we **strongly** advise to install [Node Version Manager](https://github.com/nvm-sh/nvm) and use the latest LTS version of Node (check that your local version matches with the one used in the Dockerfile).
|
||||
|
||||
The API will run inside a docker container, **but** the install itself is made outside the container, because during development we need tools that need to be available locally (eg. ESLint, Prettier...).
|
||||
|
||||
A RabbitMQ instance is also required to send / receive messages when data has been inserted/updated/deleted.
|
||||
|
||||
## Installation
|
||||
|
||||
Copy `.env.dist` to `.env` :
|
||||
- copy `.env.dist` to `.env` :
|
||||
|
||||
```bash
|
||||
cp .env.dist .env
|
||||
```
|
||||
```bash
|
||||
cp .env.dist .env
|
||||
```
|
||||
|
||||
and modify it to suit your needs.
|
||||
Modify it if needed.
|
||||
|
||||
Then execute :
|
||||
- start the containers :
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The app runs automatically on the port defined in `SERVICE_PORT` of `.env` file (default : _5001_).
|
||||
The app runs automatically on the port defined in `SERVICE_PORT` of `.env` file (default : _5001_).
|
||||
|
||||
- install the dependencies :
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
## Database migration
|
||||
|
||||
|
@ -96,17 +106,30 @@ As mentionned earlier, RabbitMQ messages are sent after these events :
|
|||
|
||||
Various messages are also sent for logging purpose.
|
||||
|
||||
## Test
|
||||
## Tests
|
||||
|
||||
Tests are run outside the container for ease of use (switching between different environments inside containers using prisma is complicated and error prone).
|
||||
The integration tests use a dedicated database (see *db-test* section of *docker-compose.yml*).
|
||||
|
||||
```bash
|
||||
# unit tests
|
||||
docker exec v3-user sh -c "npm run test"
|
||||
|
||||
# test coverage
|
||||
docker exec v3-user sh -c "npm run test:cov"
|
||||
# run all tests (unit + integration)
|
||||
npm run test
|
||||
```
|
||||
|
||||
Note : you can run all npm commands directly _outside_ the container (see _scripts_ section of `package.json` for available commands), but you need NodeJS installed locally. We **strongly** advise to install [Node Version Manager](https://github.com/nvm-sh/nvm) and use the latest LTS version of Node.
|
||||
```bash
|
||||
# unit tests only
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
```bash
|
||||
# integration tests only
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
```bash
|
||||
# coverage
|
||||
npm run test:cov
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ version: '3.8'
|
|||
|
||||
services:
|
||||
api:
|
||||
container_name: ${SERVICE_CONTAINER}
|
||||
container_name: v3-user
|
||||
build:
|
||||
dockerfile: Dockerfile
|
||||
context: .
|
||||
|
@ -13,7 +13,7 @@ services:
|
|||
- .env
|
||||
command: npm run start:dev
|
||||
ports:
|
||||
- "${SERVICE_PORT:-5001}:${SERVICE_PORT:-5001}"
|
||||
- 5001:5001
|
||||
depends_on:
|
||||
- db
|
||||
networks:
|
||||
|
@ -22,14 +22,14 @@ services:
|
|||
- v3-user-api
|
||||
|
||||
db:
|
||||
container_name: ${POSTGRES_CONTAINER}
|
||||
container_name: v3-user-db
|
||||
image: ${POSTGRES_IMAGE}
|
||||
environment:
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: user
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: user
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5501}:5432"
|
||||
- 5501:5432
|
||||
volumes:
|
||||
- .postgresql:/var/lib/postgresql/data:rw
|
||||
networks:
|
||||
|
@ -37,6 +37,20 @@ services:
|
|||
aliases:
|
||||
- v3-user-db
|
||||
|
||||
db-test:
|
||||
container_name: v3-user-db-test
|
||||
image: ${POSTGRES_IMAGE}
|
||||
environment:
|
||||
POSTGRES_DB: user
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: user
|
||||
ports:
|
||||
- 5601:5432
|
||||
networks:
|
||||
v3-network:
|
||||
aliases:
|
||||
- v3-user-db-test
|
||||
|
||||
networks:
|
||||
v3-network:
|
||||
name: v3-network
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"dotenv-cli": "^6.0.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
|
@ -4075,6 +4076,21 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-cli": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-6.0.0.tgz",
|
||||
"integrity": "sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"dotenv-expand": "^8.0.1",
|
||||
"minimist": "^1.2.5"
|
||||
},
|
||||
"bin": {
|
||||
"dotenv": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-expand": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.3.tgz",
|
||||
|
@ -6374,9 +6390,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
|
@ -12115,6 +12131,18 @@
|
|||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
|
||||
"integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ=="
|
||||
},
|
||||
"dotenv-cli": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-6.0.0.tgz",
|
||||
"integrity": "sha512-qXlCOi3UMDhCWFKe0yq5sg3X+pJAz+RQDiFN38AMSbUrnY3uZshSfDJUAge951OS7J9gwLZGfsBlWRSOYz/TRg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"dotenv-expand": "^8.0.1",
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"dotenv-expand": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.3.tgz",
|
||||
|
@ -13844,9 +13872,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonc-parser": {
|
||||
|
|
25
package.json
25
package.json
|
@ -14,11 +14,12 @@
|
|||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
"test": "npm run migrate-test && dotenv -e .env.test jest",
|
||||
"test:unit": "jest --testPathIgnorePatterns 'integration' --verbose",
|
||||
"test:integration": "npm run migrate-test && dotenv -e .env.test -- jest --testPathPattern 'integration' --verbose",
|
||||
"test:cov": "npm run migrate-test && dotenv -e .env.test -- jest --coverage",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||
"migrate-test": "dotenv -e .env.test -- npx prisma migrate dev --name postgres-init"
|
||||
},
|
||||
"dependencies": {
|
||||
"@automapper/classes": "^8.7.7",
|
||||
|
@ -33,23 +34,11 @@
|
|||
"@nestjs/cqrs": "^9.0.1",
|
||||
"@nestjs/microservices": "^9.2.1",
|
||||
"@nestjs/platform-express": "^9.0.0",
|
||||
"@prisma/client": "^4.7.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
"@nestjs/schematics": "^9.0.0",
|
||||
"@nestjs/testing": "^9.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "28.1.8",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"dotenv-cli": "^6.0.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
import { TestingModule, Test } from '@nestjs/testing';
|
||||
import { DatabaseModule } from '../../../database/database.module';
|
||||
import { PrismaService } from '../../../database/src/adapters/secondaries/prisma-service';
|
||||
import { DatabaseException } from '../../../database/src/exceptions/DatabaseException';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { User } from '../../domain/entities/user';
|
||||
|
||||
describe('UsersRepository', () => {
|
||||
let prismaService: PrismaService;
|
||||
let usersRepository: UsersRepository;
|
||||
|
||||
const createUsers = async (nbToCreate = 10) => {
|
||||
for (let i = 0; i < nbToCreate; i++) {
|
||||
await prismaService.user.create({
|
||||
data: {
|
||||
firstName: `firstName-${i}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [DatabaseModule],
|
||||
providers: [UsersRepository, PrismaService],
|
||||
}).compile();
|
||||
|
||||
prismaService = module.get<PrismaService>(PrismaService);
|
||||
usersRepository = module.get<UsersRepository>(UsersRepository);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await prismaService.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await prismaService.user.deleteMany();
|
||||
});
|
||||
|
||||
describe('findAll', () => {
|
||||
it('should return an empty data array', async () => {
|
||||
const res = await usersRepository.findAll();
|
||||
expect(res).toEqual({
|
||||
data: [],
|
||||
total: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an data array with users', async () => {
|
||||
await createUsers(10);
|
||||
const users = await usersRepository.findAll();
|
||||
expect(users.data.length).toBe(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findOneByUuid', () => {
|
||||
it('should return a user', async () => {
|
||||
const userToFind = await prismaService.user.create({
|
||||
data: {
|
||||
firstName: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
const user = await usersRepository.findOneByUuid(userToFind.uuid);
|
||||
expect(user.uuid).toBe(userToFind.uuid);
|
||||
});
|
||||
|
||||
it('should return null', async () => {
|
||||
const user = await usersRepository.findOneByUuid(
|
||||
'544572be-11fb-4244-8235-587221fc9104',
|
||||
);
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('findOne', () => {
|
||||
it('should return a user according to its email', async () => {
|
||||
const userToFind = await prismaService.user.create({
|
||||
data: {
|
||||
email: 'test@test.com',
|
||||
},
|
||||
});
|
||||
|
||||
const user = await usersRepository.findOne({
|
||||
email: 'test@test.com',
|
||||
});
|
||||
|
||||
expect(user.uuid).toBe(userToFind.uuid);
|
||||
});
|
||||
|
||||
it('should return null with unknown email', async () => {
|
||||
const user = await usersRepository.findOne({
|
||||
email: 'wrong@email.com',
|
||||
});
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should create a user', async () => {
|
||||
const beforeCount = await prismaService.user.count();
|
||||
|
||||
const userToCreate: User = new User();
|
||||
userToCreate.firstName = 'test';
|
||||
const user = await usersRepository.create(userToCreate);
|
||||
|
||||
const afterCount = await prismaService.user.count();
|
||||
|
||||
expect(afterCount - beforeCount).toBe(1);
|
||||
expect(user.uuid).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
it('should update user firstName', async () => {
|
||||
const userToUpdate = await prismaService.user.create({
|
||||
data: {
|
||||
firstName: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
const toUpdate: User = new User();
|
||||
toUpdate.firstName = 'updated';
|
||||
const updateduser = await usersRepository.update(
|
||||
userToUpdate.uuid,
|
||||
toUpdate,
|
||||
);
|
||||
|
||||
expect(updateduser.uuid).toBe(userToUpdate.uuid);
|
||||
expect(updateduser.firstName).toBe('updated');
|
||||
});
|
||||
|
||||
it('should throw DatabaseException', async () => {
|
||||
const toUpdate: User = new User();
|
||||
toUpdate.firstName = 'updated';
|
||||
|
||||
await expect(
|
||||
usersRepository.update(
|
||||
'544572be-11fb-4244-8235-587221fc9104',
|
||||
toUpdate,
|
||||
),
|
||||
).rejects.toBeInstanceOf(DatabaseException);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should delete a user', async () => {
|
||||
const userToRemove = await prismaService.user.create({
|
||||
data: {
|
||||
firstName: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
await usersRepository.delete(userToRemove.uuid);
|
||||
|
||||
const count = await prismaService.user.count();
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
it('should throw DatabaseException', async () => {
|
||||
await expect(
|
||||
usersRepository.delete('544572be-11fb-4244-8235-587221fc9104'),
|
||||
).rejects.toBeInstanceOf(DatabaseException);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue