add e2e with docker-compose support
This commit is contained in:
parent
7bf514b2aa
commit
bdf16a3189
6
.env.e2e
Normal file
6
.env.e2e
Normal file
@ -0,0 +1,6 @@
|
||||
DB_HOST=localhost
|
||||
DB_PORT=4401
|
||||
DB_USERNAME=test
|
||||
DB_PASSWORD=test
|
||||
DB_DATABASE=test
|
||||
JWT_SECRET=secret
|
||||
11
environment/e2e/docker-compose.yaml
Normal file
11
environment/e2e/docker-compose.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:18
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: test
|
||||
POSTGRES_PASSWORD: test
|
||||
POSTGRES_DB: test
|
||||
ports:
|
||||
- '4401:5432'
|
||||
1360
package-lock.json
generated
1360
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -52,6 +52,7 @@
|
||||
"@types/node": "^22.10.7",
|
||||
"@types/passport-jwt": "^4.0.0",
|
||||
"@types/supertest": "^6.0.2",
|
||||
"dotenv": "^16.4.5",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-prettier": "^5.2.2",
|
||||
@ -60,6 +61,7 @@
|
||||
"prettier": "^3.4.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"supertest": "^7.0.0",
|
||||
"testcontainers": "^10.9.0",
|
||||
"ts-jest": "^29.2.5",
|
||||
"ts-loader": "^9.5.2",
|
||||
"ts-node": "^10.9.2",
|
||||
|
||||
41
test/global-setup.ts
Normal file
41
test/global-setup.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { exec } from 'child_process';
|
||||
import * as path from 'path';
|
||||
import { DockerComposeEnvironment, Wait } from 'testcontainers';
|
||||
|
||||
export default async () => {
|
||||
const composeFilePath = path.resolve(__dirname, '../environment/e2e');
|
||||
|
||||
const environment = await new DockerComposeEnvironment(
|
||||
composeFilePath,
|
||||
'docker-compose.yaml',
|
||||
)
|
||||
.withWaitStrategy('postgres_1', Wait.forHealthCheck())
|
||||
.up();
|
||||
|
||||
// Store the environment details for teardown
|
||||
(global as any).__TESTCONTAINERS_ENVIRONMENT__ = environment;
|
||||
|
||||
// Run migrations
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
console.info("running migration")
|
||||
exec(
|
||||
'env && npm run migration:run',
|
||||
{ env: { ...process.env, ...readEnvFile() } },
|
||||
(err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.error(stderr);
|
||||
return reject(err);
|
||||
}
|
||||
console.log(stdout);
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
function readEnvFile() {
|
||||
const fs = require('fs');
|
||||
const dotenv = require('dotenv');
|
||||
const envConfig = dotenv.parse(fs.readFileSync('.env.e2e'));
|
||||
return envConfig;
|
||||
}
|
||||
10
test/global-teardown.ts
Normal file
10
test/global-teardown.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { DockerComposeEnvironment } from 'testcontainers';
|
||||
|
||||
export default async () => {
|
||||
const environment: DockerComposeEnvironment = (global as any)
|
||||
.__TESTCONTAINERS_ENVIRONMENT__;
|
||||
|
||||
if (environment) {
|
||||
await environment.down();
|
||||
}
|
||||
};
|
||||
@ -5,5 +5,7 @@
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
}
|
||||
},
|
||||
"globalSetup": "<rootDir>/global-setup.ts",
|
||||
"globalTeardown": "<rootDir>/global-teardown.ts"
|
||||
}
|
||||
|
||||
133
test/user.e2e-spec.ts
Normal file
133
test/user.e2e-spec.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication, ValidationPipe } from '@nestjs/common';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { CreateUserDto } from '../src/user/dto/create-user.dto';
|
||||
import { Role } from '../src/auth/role.enum';
|
||||
import { UserService } from '../src/user/user.service';
|
||||
import { User } from '../src/entity/user';
|
||||
import { UpdateUserDto } from '../src/user/dto/update-user.dto';
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
dotenv.config({ path: '.env.e2e' });
|
||||
|
||||
describe('UserController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
let jwtToken: string;
|
||||
let adminUserId: number;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
await app.init();
|
||||
|
||||
const userService = moduleFixture.get<UserService>(UserService);
|
||||
const adminUser = await userService.create({
|
||||
username: 'e2e_admin',
|
||||
password: 'password',
|
||||
roles: [Role.Admin],
|
||||
});
|
||||
adminUserId = adminUser.id;
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/auth/login')
|
||||
.send({ username: 'e2e_admin', password: 'password' });
|
||||
|
||||
jwtToken = response.body.access_token;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
const userService = app.get<UserService>(UserService);
|
||||
await userService.remove(adminUserId);
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('/users', () => {
|
||||
it('(GET) should get all users', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get('/users')
|
||||
.set('Authorization', `Bearer ${jwtToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(Array.isArray(response.body)).toBe(true);
|
||||
});
|
||||
|
||||
it('(POST) should create a user', async () => {
|
||||
const createUserDto: CreateUserDto = {
|
||||
username: 'e2e_user',
|
||||
password: 'password',
|
||||
roles: [Role.User],
|
||||
};
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/users')
|
||||
.set('Authorization', `Bearer ${jwtToken}`)
|
||||
.send(createUserDto);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.username).toEqual(createUserDto.username);
|
||||
|
||||
const userService = app.get<UserService>(UserService);
|
||||
await userService.remove(response.body.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('/users/:id', () => {
|
||||
let user: User;
|
||||
let userService: UserService;
|
||||
|
||||
beforeEach(async () => {
|
||||
userService = app.get<UserService>(UserService);
|
||||
user = await userService.create({
|
||||
username: 'e2e_test_user',
|
||||
password: 'password',
|
||||
roles: [Role.User],
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const userExists = await userService.findOne(user.id);
|
||||
if (userExists) {
|
||||
await userService.remove(user.id);
|
||||
}
|
||||
});
|
||||
|
||||
it('(GET) should get a user by id', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/users/${user.id}`)
|
||||
.set('Authorization', `Bearer ${jwtToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.id).toEqual(user.id);
|
||||
});
|
||||
|
||||
it('(PATCH) should update a user', async () => {
|
||||
const updateUserDto: UpdateUserDto = {
|
||||
username: 'e2e_updated_user',
|
||||
};
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.patch(`/users/${user.id}`)
|
||||
.set('Authorization', `Bearer ${jwtToken}`)
|
||||
.send(updateUserDto);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.username).toEqual(updateUserDto.username);
|
||||
});
|
||||
|
||||
it('(DELETE) should delete a user', async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.delete(`/users/${user.id}`)
|
||||
.set('Authorization', `Bearer ${jwtToken}`);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
|
||||
const deletedUser = await userService.findOne(user.id);
|
||||
expect(deletedUser).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user