From 4e503bfa98387027291a334ede0120f8f8fe3b21 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Sat, 19 Mar 2022 23:04:22 +0545 Subject: [PATCH 01/31] test: authcontroller login e2e test added --- package.json | 1 + test/e2e/auth/auth.e2e-spec.ts | 67 ++++++++++++++++++++++++++++++++++ test/factories/role.factory.ts | 23 ++++++++++++ test/factories/user.factory.ts | 54 +++++++++++++++++++++++++++ test/utility/extract-cookie.ts | 19 ++++++++++ yarn.lock | 5 +++ 6 files changed, 169 insertions(+) create mode 100644 test/e2e/auth/auth.e2e-spec.ts create mode 100644 test/factories/role.factory.ts create mode 100644 test/factories/user.factory.ts create mode 100644 test/utility/extract-cookie.ts diff --git a/package.json b/package.json index f2d5bd6..275364f 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "@aws-sdk/client-cloudwatch-logs": "^3.54.1", + "@faker-js/faker": "^6.0.0", "@nestjs-modules/mailer": "^1.6.1", "@nestjs/bull": "^0.5.3", "@nestjs/common": "^8.4.1", diff --git a/test/e2e/auth/auth.e2e-spec.ts b/test/e2e/auth/auth.e2e-spec.ts new file mode 100644 index 0000000..77eedfd --- /dev/null +++ b/test/e2e/auth/auth.e2e-spec.ts @@ -0,0 +1,67 @@ +import { HttpStatus } from '@nestjs/common'; +import * as request from 'supertest'; + +import { AppFactory } from 'test/factories/app'; +import { RoleFactory } from 'test/factories/role.factory'; +import { UserFactory } from 'test/factories/user.factory'; +import { extractCookies } from 'test/utility/extract-cookie'; + +describe('AuthController (e2e)', () => { + let app: AppFactory; + + beforeAll(async () => { + app = await AppFactory.new(); + }); + + beforeEach(async () => { + await app.refreshDatabase(); + }); + + it('POST /auth/login requires valid username and password', async () => { + await request(app.instance.getHttpServer()) + .post(`/auth/login`) + .send({ + username: 'email' + }) + .expect(HttpStatus.UNPROCESSABLE_ENTITY); + }); + + it('POST /auth/login should throw unauthorized error if wrong username and password provided', async () => { + await request(app.instance.getHttpServer()) + .post(`/auth/login`) + .send({ + username: 'john@example.com', + password: 'wrongPassword', + remember: true + }) + .expect(HttpStatus.UNAUTHORIZED); + }); + + it('POST /auth/login should login if provided with valid username and password', async () => { + let cookie; + const role = await RoleFactory.new().create(); + const user = await UserFactory.new() + .withRole(role) + .create({ password: 'password' }); + + await request(app.instance.getHttpServer()) + .post(`/auth/login`) + .send({ + username: user.email, + password: 'password', + remember: true + }) + .expect(HttpStatus.NO_CONTENT) + .then((res) => { + cookie = extractCookies(res.headers); + }); + expect(cookie).toBeDefined(); + expect(cookie).toHaveProperty('Authentication'); + expect(cookie).toHaveProperty('Refresh'); + expect(cookie).toHaveProperty('ExpiresIn'); + }); + + afterAll(async () => { + await app.close(); + }); +}); diff --git a/test/factories/role.factory.ts b/test/factories/role.factory.ts new file mode 100644 index 0000000..bad547e --- /dev/null +++ b/test/factories/role.factory.ts @@ -0,0 +1,23 @@ +import { getRepository } from 'typeorm'; +import { faker } from '@faker-js/faker'; + +import { RoleEntity } from 'src/role/entities/role.entity'; + +export class RoleFactory { + static new() { + return new RoleFactory(); + } + + async create(role: Partial = {}) { + const roleRepository = getRepository(RoleEntity); + return roleRepository.save({ + name: faker.name.jobTitle(), + description: faker.lorem.sentence(), + ...role + }); + } + + async createMany(roles: Partial[]) { + return Promise.all([roles.map((role) => this.create(role))]); + } +} diff --git a/test/factories/user.factory.ts b/test/factories/user.factory.ts new file mode 100644 index 0000000..31ef903 --- /dev/null +++ b/test/factories/user.factory.ts @@ -0,0 +1,54 @@ +import { getRepository } from 'typeorm'; +import { faker } from '@faker-js/faker'; +import * as bcrypt from 'bcrypt'; + +import { UserEntity } from 'src/auth/entity/user.entity'; +import { UserStatusEnum } from 'src/auth/user-status.enum'; +import { RoleEntity } from 'src/role/entities/role.entity'; + +export class UserFactory { + private role: RoleEntity; + + static new() { + return new UserFactory(); + } + + withRole(role: RoleEntity) { + this.role = role; + return this; + } + + async create(user: Partial = {}) { + const userRepository = getRepository(UserEntity); + const salt = await bcrypt.genSalt(); + const password = await this.hashPassword( + user.password || faker.internet.password(), + salt + ); + const payload = { + username: faker.internet.userName().toLowerCase(), + email: faker.internet.email().toLowerCase(), + name: `${faker.name.firstName()} ${faker.name.lastName()}`, + address: faker.address.streetAddress(), + contact: faker.phone.phoneNumber(), + avatar: faker.image.avatar(), + salt, + token: faker.datatype.uuid(), + status: UserStatusEnum.ACTIVE, + isTwoFAEnabled: false, + ...user, + password + }; + + if (this.role) payload.role = this.role; + return userRepository.save(payload); + } + + async createMany(users: Partial[]) { + return Promise.all([users.map((user) => this.create(user))]); + } + + private hashPassword(password, salt) { + return bcrypt.hash(password, salt); + } +} diff --git a/test/utility/extract-cookie.ts b/test/utility/extract-cookie.ts new file mode 100644 index 0000000..cd64511 --- /dev/null +++ b/test/utility/extract-cookie.ts @@ -0,0 +1,19 @@ +const shapeFlags = (flags) => + flags.reduce((shapedFlags, flag) => { + const [flagName, rawValue] = flag.split('='); + const value = rawValue ? rawValue.replace(';', '') : true; + return { ...shapedFlags, [flagName]: value }; + }, {}); + +export const extractCookies = (headers) => { + const cookies = headers['set-cookie']; + + return cookies.reduce((shapedCookies, cookieString) => { + const [rawCookie, ...flags] = cookieString.split('; '); + const [cookieName, value] = rawCookie.split('='); + return { + ...shapedCookies, + [cookieName]: { value, flags: shapeFlags(flags) } + }; + }, {}); +}; diff --git a/yarn.lock b/yarn.lock index 72db1dd..8006914 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1168,6 +1168,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@faker-js/faker@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-6.0.0.tgz#b613ebf5f5ebb2ab987afb567d8b7fe860199c13" + integrity sha512-10zLCKhp3YEmBuko71ivcMoIZcCLXgQVck6aNswX+AWwaek/L8S3yz9i8m3tHigRkcF6F2vI+qtdtyySHK+bGA== + "@humanwhocodes/config-array@^0.9.2": version "0.9.2" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.2.tgz#68be55c737023009dfc5fe245d51181bb6476914" From 9281faa73ae132472894999a504b03f4941cf470 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Tue, 26 Jul 2022 18:53:47 +0545 Subject: [PATCH 02/31] fix: validation issue fixed --- src/common/pipes/custom-validation.pipe.ts | 2 +- .../pipes/i18n-exception-filter.pipe.ts | 114 +++++++++++------- 2 files changed, 70 insertions(+), 46 deletions(-) diff --git a/src/common/pipes/custom-validation.pipe.ts b/src/common/pipes/custom-validation.pipe.ts index b7368f3..b523310 100644 --- a/src/common/pipes/custom-validation.pipe.ts +++ b/src/common/pipes/custom-validation.pipe.ts @@ -15,7 +15,7 @@ export class CustomValidationPipe implements PipeTransform { } const object = plainToClass(metatype, value); const errors = await validate(object); - if (errors.length > 0) { + if (errors && errors.length > 0) { const translatedError = await this.transformError(errors); throw new UnprocessableEntityException(translatedError); } diff --git a/src/common/pipes/i18n-exception-filter.pipe.ts b/src/common/pipes/i18n-exception-filter.pipe.ts index e9e1269..b5bc3e4 100644 --- a/src/common/pipes/i18n-exception-filter.pipe.ts +++ b/src/common/pipes/i18n-exception-filter.pipe.ts @@ -3,16 +3,22 @@ import { Catch, ExceptionFilter, HttpException, - HttpStatus + HttpStatus, + Inject } from '@nestjs/common'; import { I18nService } from 'nestjs-i18n'; import { ValidationErrorInterface } from 'src/common/interfaces/validation-error.interface'; import { StatusCodesList } from 'src/common/constants/status-codes-list.constants'; +import { Logger } from 'winston'; +import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; @Catch(HttpException) export class I18nExceptionFilterPipe implements ExceptionFilter { - constructor(private readonly i18n: I18nService) {} + constructor( + @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, + private readonly i18n: I18nService + ) {} async catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); @@ -28,49 +34,57 @@ export class I18nExceptionFilterPipe implements ExceptionFilter { } async getMessage(exception: HttpException, lang: string) { - const exceptionResponse = exception.getResponse() as any; - if (!exceptionResponse.message && typeof exceptionResponse === 'string') { - return await this.i18n.translate(`exception.${exceptionResponse}`, { - lang, - args: {} - }); - } - if (exceptionResponse.statusCode === HttpStatus.UNPROCESSABLE_ENTITY) { - if ( - exceptionResponse.hasOwnProperty('message') && - exceptionResponse.message instanceof Array + try { + const exceptionResponse = exception.getResponse() as any; + if (!exceptionResponse.message && typeof exceptionResponse === 'string') { + return await this.i18n.translate(`exception.${exceptionResponse}`, { + lang, + args: {} + }); + } + if (exceptionResponse.statusCode === HttpStatus.UNPROCESSABLE_ENTITY) { + if ( + exceptionResponse.hasOwnProperty('message') && + exceptionResponse.message instanceof Array + ) { + exceptionResponse.code = StatusCodesList.ValidationError; + exceptionResponse.message = await this.translateArray( + exceptionResponse.message, + lang + ); + } + return exceptionResponse; + } + let errorMessage = 'internalError'; + if (exceptionResponse.message instanceof Array) { + errorMessage = exceptionResponse.message[0]; + } else if (typeof exceptionResponse.message === 'string') { + errorMessage = exceptionResponse.message; + } else if ( + !exceptionResponse.message && + typeof exceptionResponse === 'string' ) { - exceptionResponse.code = StatusCodesList.ValidationError; - exceptionResponse.message = await this.translateArray( - exceptionResponse.message, - lang - ); + errorMessage = exceptionResponse; } - return exceptionResponse; - } - let errorMessage = 'internalError'; - if (exceptionResponse.message instanceof Array) { - errorMessage = exceptionResponse.message[0]; - } else if (typeof exceptionResponse.message === 'string') { - errorMessage = exceptionResponse.message; - } else if ( - !exceptionResponse.message && - typeof exceptionResponse === 'string' - ) { - errorMessage = exceptionResponse; - } - const { title, argument } = this.checkIfConstraintAvailable(errorMessage); - exceptionResponse.message = await this.i18n.translate( - `exception.${title}`, - { - lang, - args: { - ...argument + const { title, argument } = this.checkIfConstraintAvailable(errorMessage); + exceptionResponse.message = await this.i18n.translate( + `exception.${title}`, + { + lang, + args: { + ...argument + } } - } - ); - return exceptionResponse; + ); + return exceptionResponse; + } catch (error) { + this.logger.error('Error in I18nExceptionFilterPipe: ', { + meta: { + error + } + }); + } } checkIfConstraintAvailable(message: string): { @@ -106,7 +120,8 @@ export class I18nExceptionFilterPipe implements ExceptionFilter { 'isIn', 'matches', 'maxLength', - 'minLength' + 'minLength', + 'isLength' ]; const item = errors[i]; let message = []; @@ -122,13 +137,22 @@ export class I18nExceptionFilterPipe implements ExceptionFilter { validationKey = title; validationArgument = argument; } - return this.i18n.translate(`validation.${validationKey}`, { + const args: Record = { lang, args: { - ...validationArgument, property: item.property } - }); + }; + if ( + validationArgument && + Object.keys(validationArgument).length > 0 + ) { + args.args = { + ...validationArgument, + property: item.property + }; + } + return this.i18n.translate(`validation.${validationKey}`, args); }) ); } From 652429e71e5844e5d096618e3707e09aae744fd0 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 11:38:56 +0545 Subject: [PATCH 03/31] fix: update github action add env variables --- .github/workflows/test.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index db06b8f..760e911 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,15 @@ jobs: test: runs-on: ubuntu-latest + env: + NODE_ENV=test + DB_TYPE=postgres + DB_HOST=localhost + DB_PORT=5432 + DB_DATABASE_NAME=truthy_db + DB_USERNAME=truthy_user + DB_PASSWORD=truthypwd + services: postgres: image: postgres:14 From 528c59442fef135e8e64121318de5875baa5ac12 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 11:44:44 +0545 Subject: [PATCH 04/31] fix: update github action add env variables --- .github/workflows/test.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 760e911..cc27040 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,19 +2,19 @@ name: test on: push +env: + NODE_ENV=test + DB_TYPE=postgres + DB_HOST=localhost + DB_PORT=5432 + DB_DATABASE_NAME=truthy_db + DB_USERNAME=truthy_user + DB_PASSWORD=truthypwd + jobs: test: runs-on: ubuntu-latest - env: - NODE_ENV=test - DB_TYPE=postgres - DB_HOST=localhost - DB_PORT=5432 - DB_DATABASE_NAME=truthy_db - DB_USERNAME=truthy_user - DB_PASSWORD=truthypwd - services: postgres: image: postgres:14 From b0681ec94137d96aa1f162909a9bb6de238b191e Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 11:53:12 +0545 Subject: [PATCH 05/31] fix: update github action add env variables --- .github/workflows/test.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cc27040..be9bed1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,13 +3,13 @@ name: test on: push env: - NODE_ENV=test - DB_TYPE=postgres - DB_HOST=localhost - DB_PORT=5432 - DB_DATABASE_NAME=truthy_db - DB_USERNAME=truthy_user - DB_PASSWORD=truthypwd + NODE_ENV: test + DB_TYPE: postgres + DB_HOST: localhost + DB_PORT: 5432 + DB_DATABASE_NAME: truthy_db + DB_USERNAME: truthy_user + DB_PASSWORD: truthypwd jobs: test: From 1c7d5ace37e56eebb5665ab65f70c87338c5e47b Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 12:03:31 +0545 Subject: [PATCH 06/31] test: debug env variable issue in github action --- src/config/ormconfig.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/config/ormconfig.ts b/src/config/ormconfig.ts index a0b932a..a695a02 100644 --- a/src/config/ormconfig.ts +++ b/src/config/ormconfig.ts @@ -1,5 +1,13 @@ import { ConnectionOptions } from 'typeorm'; import * as config from 'config'; +console.log('🚀 ~ file: ormconfig.ts ~ line 3 ~ config', { + type: process.env.DB_TYPE, + host: process.env.DB_HOST, + port: process.env.DB_PORT, + username: process.env.DB_USERNAME, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE_NAME +}); const dbConfig = config.get('db'); const ormConfig: ConnectionOptions = { From d5bfb29a7e102b09a5a002c2e15258a0d37fdb44 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 12:17:16 +0545 Subject: [PATCH 07/31] test: debug env variable issue in github action --- .github/workflows/test.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be9bed1..8ca1a7a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,15 +2,6 @@ name: test on: push -env: - NODE_ENV: test - DB_TYPE: postgres - DB_HOST: localhost - DB_PORT: 5432 - DB_DATABASE_NAME: truthy_db - DB_USERNAME: truthy_user - DB_PASSWORD: truthypwd - jobs: test: runs-on: ubuntu-latest @@ -54,6 +45,9 @@ jobs: - name: Run linter run: npm run lint + - name: Run migration + run: npm run migrate + - name: Run unit test run: npm run test:unit From 276063a4d85f245b246aefdec026d0d2b8a31356 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 12:31:06 +0545 Subject: [PATCH 08/31] test: debug env variable issue in github action --- .github/workflows/test.yml | 3 --- src/config/ormconfig.ts | 8 -------- test/factories/app.ts | 17 ++--------------- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ca1a7a..db06b8f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,9 +45,6 @@ jobs: - name: Run linter run: npm run lint - - name: Run migration - run: npm run migrate - - name: Run unit test run: npm run test:unit diff --git a/src/config/ormconfig.ts b/src/config/ormconfig.ts index a695a02..a0b932a 100644 --- a/src/config/ormconfig.ts +++ b/src/config/ormconfig.ts @@ -1,13 +1,5 @@ import { ConnectionOptions } from 'typeorm'; import * as config from 'config'; -console.log('🚀 ~ file: ormconfig.ts ~ line 3 ~ config', { - type: process.env.DB_TYPE, - host: process.env.DB_HOST, - port: process.env.DB_PORT, - username: process.env.DB_USERNAME, - password: process.env.DB_PASSWORD, - database: process.env.DB_DATABASE_NAME -}); const dbConfig = config.get('db'); const ormConfig: ConnectionOptions = { diff --git a/test/factories/app.ts b/test/factories/app.ts index 4dd8ac8..f9024ec 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -4,7 +4,6 @@ import { Test } from '@nestjs/testing'; import { Connection, ConnectionManager, QueryRunner } from 'typeorm'; import { TypeOrmModule } from '@nestjs/typeorm'; import { useContainer as classValidatorUseContainer } from 'class-validator'; -import { v4 as uuid4 } from 'uuid'; import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis'; import * as Redis from 'ioredis'; import * as config from 'config'; @@ -47,18 +46,6 @@ export class AppFactory { }; } }) - // createMockModule([ - // { - // provide: ThrottlerModule, - // useFactory: () => { - // return { - // ttl: jest.fn(), - // limit: jest.fn(), - // storage: jest.fn() - // }; - // } - // } - // ]) ] }) .overrideProvider('LOGIN_THROTTLE') @@ -123,12 +110,12 @@ const setupRedis = async () => { }; const setupTestDatabase = async () => { - const database = `test_${uuid4()}`; + const database = process.env.DB_DATABASE_NAME || dbConfig.database; const manager = new ConnectionManager().create({ type: dbConfig.type, host: process.env.DB_HOST || dbConfig.host, port: parseInt(process.env.DB_PORT) || dbConfig.port, - database: process.env.DB_DATABASE_NAME || dbConfig.database, + database, username: process.env.DB_USERNAME || dbConfig.username, password: process.env.DB_PASSWORD || dbConfig.password, logging: false, From f4f5a55240050f596dc8095fefa9f3991854d99c Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 12:44:12 +0545 Subject: [PATCH 09/31] test: debug env variable issue in github action --- test/factories/app.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index f9024ec..3e9c94b 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -122,8 +122,8 @@ const setupTestDatabase = async () => { synchronize: false, migrationsRun: true, migrationsTableName: 'migrations', - migrations: [__dirname + '/../../src/database/migrations/**/*{.ts,.js}'], - entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] + entities: [__dirname + '/../../**/*.entity.{js,ts}'], + migrations: [__dirname + '/../../database/migrations/**/*{.ts,.js}'] }); const queryRunner = manager.createQueryRunner(); From a9dcf324e766308d7270b7a1805caeba31db87ec Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 12:53:22 +0545 Subject: [PATCH 10/31] test: debug github action --- test/factories/app.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index 3e9c94b..49d21d0 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -34,7 +34,7 @@ export class AppFactory { AppModule, TypeOrmModule.forRoot({ ...connection.options, - type: dbConfig.type, + type: dbConfig.type || 'postgres', database }), ThrottlerModule.forRootAsync({ @@ -129,7 +129,7 @@ const setupTestDatabase = async () => { const queryRunner = manager.createQueryRunner(); const connection = await manager.connect(); - await queryRunner.createDatabase(database, true); + // await queryRunner.createDatabase(database, true); return { database, connection, queryRunner }; }; From b6e6698aa829bd6782d489ae03623533d5a1c5b0 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 13:10:28 +0545 Subject: [PATCH 11/31] test: debug github action --- test/factories/app.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index 49d21d0..2f8772a 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -94,7 +94,7 @@ export class AppFactory { async close() { await this.appInstance.close(); - await this.queryRunner.query(`DROP DATABASE "${this.database}"`); + // await this.queryRunner.query(`DROP DATABASE "${this.database}"`); await this.teardown(this.redis); await this.connection.close(); } @@ -122,8 +122,8 @@ const setupTestDatabase = async () => { synchronize: false, migrationsRun: true, migrationsTableName: 'migrations', - entities: [__dirname + '/../../**/*.entity.{js,ts}'], - migrations: [__dirname + '/../../database/migrations/**/*{.ts,.js}'] + migrations: [__dirname + '/../../src/database/migrations/**/*{.ts,.js}'], + entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] }); const queryRunner = manager.createQueryRunner(); From e9b501f067f578e35426a4bb70a5dab50a552989 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 13:22:45 +0545 Subject: [PATCH 12/31] test: debug github action --- test/factories/app.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index 2f8772a..0885d6d 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -122,8 +122,8 @@ const setupTestDatabase = async () => { synchronize: false, migrationsRun: true, migrationsTableName: 'migrations', - migrations: [__dirname + '/../../src/database/migrations/**/*{.ts,.js}'], - entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] + entities: [__dirname + '/../../**/*.entity.{js,ts}'], + migrations: [__dirname + '/../../database/migrations/**/*{.ts,.js}'] }); const queryRunner = manager.createQueryRunner(); From 0991c9d1ac1b4bd934124382eb28d251c85cac60 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Wed, 27 Jul 2022 13:27:25 +0545 Subject: [PATCH 13/31] test: debug github action --- test/factories/app.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/factories/app.ts b/test/factories/app.ts index 0885d6d..7fa0151 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -111,6 +111,20 @@ const setupRedis = async () => { const setupTestDatabase = async () => { const database = process.env.DB_DATABASE_NAME || dbConfig.database; + console.log({ + type: dbConfig.type, + host: process.env.DB_HOST || dbConfig.host, + port: parseInt(process.env.DB_PORT) || dbConfig.port, + database, + username: process.env.DB_USERNAME || dbConfig.username, + password: process.env.DB_PASSWORD || dbConfig.password, + logging: false, + synchronize: false, + migrationsRun: true, + migrationsTableName: 'migrations', + entities: [__dirname + '/../../**/*.entity.{js,ts}'], + migrations: [__dirname + '/../../database/migrations/**/*{.ts,.js}'] + }) const manager = new ConnectionManager().create({ type: dbConfig.type, host: process.env.DB_HOST || dbConfig.host, From 274ed8ec12e857230e7f2aeef110e0105f451371 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Thu, 28 Jul 2022 23:15:12 +0545 Subject: [PATCH 14/31] chore: update github action workflow for e2e test --- .github/workflows/test.yml | 13 +++++++++---- test/factories/app.ts | 22 +++------------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index db06b8f..0f05afb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,7 @@ jobs: env: POSTGRES_DB: truthy_db POSTGRES_USER: truthy_user - POSTGRES_PASSWORD: truthypwd + POSTGRES_PASSWORD: truthy_password ports: - 5432:5432 @@ -34,10 +34,15 @@ jobs: with: node-version: 14.3.0 - - name: Set Environment Variables - uses: ./.github/actions/setvars + - name: Set environment variables + uses: allenevans/set-env@v2.0.0 with: - varFilePath: ./.github/variables/myvars.env + NODE_ENV: test + DB_HOST: 127.0.0.1 + DB_PORT: ${{ job.services.postgres.ports[5432] }} + DB_DATABASE_NAME: folio_db + DB_USERNAME: folio_user + DB_PASSWORD: folio_password - name: Install dependencies run: npm install diff --git a/test/factories/app.ts b/test/factories/app.ts index 7fa0151..59b08eb 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -94,7 +94,7 @@ export class AppFactory { async close() { await this.appInstance.close(); - // await this.queryRunner.query(`DROP DATABASE "${this.database}"`); + // await this.queryRunner.query(`TRUNCATE DATABASE "${this.database}"`); await this.teardown(this.redis); await this.connection.close(); } @@ -111,20 +111,6 @@ const setupRedis = async () => { const setupTestDatabase = async () => { const database = process.env.DB_DATABASE_NAME || dbConfig.database; - console.log({ - type: dbConfig.type, - host: process.env.DB_HOST || dbConfig.host, - port: parseInt(process.env.DB_PORT) || dbConfig.port, - database, - username: process.env.DB_USERNAME || dbConfig.username, - password: process.env.DB_PASSWORD || dbConfig.password, - logging: false, - synchronize: false, - migrationsRun: true, - migrationsTableName: 'migrations', - entities: [__dirname + '/../../**/*.entity.{js,ts}'], - migrations: [__dirname + '/../../database/migrations/**/*{.ts,.js}'] - }) const manager = new ConnectionManager().create({ type: dbConfig.type, host: process.env.DB_HOST || dbConfig.host, @@ -136,14 +122,12 @@ const setupTestDatabase = async () => { synchronize: false, migrationsRun: true, migrationsTableName: 'migrations', - entities: [__dirname + '/../../**/*.entity.{js,ts}'], - migrations: [__dirname + '/../../database/migrations/**/*{.ts,.js}'] + migrations: [__dirname + '/../../src/migrations/**/*{.ts,.js}'], + entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] }); const queryRunner = manager.createQueryRunner(); const connection = await manager.connect(); - // await queryRunner.createDatabase(database, true); - return { database, connection, queryRunner }; }; From e349bea8fa721b8951484575f51eb8e056b02b0f Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Thu, 28 Jul 2022 23:24:05 +0545 Subject: [PATCH 15/31] chore: update github action workflow for e2e test --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0f05afb..31810c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,9 +40,9 @@ jobs: NODE_ENV: test DB_HOST: 127.0.0.1 DB_PORT: ${{ job.services.postgres.ports[5432] }} - DB_DATABASE_NAME: folio_db - DB_USERNAME: folio_user - DB_PASSWORD: folio_password + DB_DATABASE_NAME: truthy_db + DB_USERNAME: truthy_user + DB_PASSWORD: truthy_password - name: Install dependencies run: npm install From 1578b333abb64518bcc897d0f7d8e6049210f425 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Sun, 31 Jul 2022 22:29:43 +0545 Subject: [PATCH 16/31] test: debug if github action working --- test/factories/app.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index 4dd8ac8..4043000 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -141,8 +141,6 @@ const setupTestDatabase = async () => { const queryRunner = manager.createQueryRunner(); const connection = await manager.connect(); - await queryRunner.createDatabase(database, true); - return { database, connection, queryRunner }; }; From 46dec047ed7a50400c449bfa975cad767ca16e4f Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Sun, 31 Jul 2022 22:57:14 +0545 Subject: [PATCH 17/31] test: auth module e2e test added --- test/factories/app.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index 46dc1fb..c704cd2 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -5,11 +5,14 @@ import { Connection, ConnectionManager, QueryRunner } from 'typeorm'; import { TypeOrmModule } from '@nestjs/typeorm'; import { useContainer as classValidatorUseContainer } from 'class-validator'; import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis'; +import { RateLimiterRedis } from 'rate-limiter-flexible'; import * as Redis from 'ioredis'; import * as config from 'config'; import { AppModule } from 'src/app.module'; -import { RateLimiterRedis } from 'rate-limiter-flexible'; +import { PermissionEntity } from 'src/permission/entities/permission.entity'; +import { RoleEntity } from 'src/role/entities/role.entity'; +import { UserEntity } from 'src/auth/entity/user.entity'; const dbConfig = config.get('db'); @@ -94,9 +97,9 @@ export class AppFactory { async close() { await this.connection.close(); - await this.appInstance.close(); // await this.queryRunner.query(`TRUNCATE DATABASE "${this.database}"`); await this.teardown(this.redis); + await this.appInstance.close(); } } @@ -123,7 +126,8 @@ const setupTestDatabase = async () => { migrationsRun: true, migrationsTableName: 'migrations', migrations: [__dirname + '/../../src/migrations/**/*{.ts,.js}'], - entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] + // entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] + entities: [UserEntity, RoleEntity, PermissionEntity] }); const queryRunner = manager.createQueryRunner(); From 5a4581f2677beb4648beb2f74b33312ce48a6f9d Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Sun, 31 Jul 2022 23:49:41 +0545 Subject: [PATCH 18/31] test: update db connection close logic in e2e app factory --- test/factories/app.ts | 89 +++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index c704cd2..03cc9e0 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -1,8 +1,8 @@ import { ThrottlerModule } from '@nestjs/throttler'; import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { Connection, ConnectionManager, QueryRunner } from 'typeorm'; -import { TypeOrmModule } from '@nestjs/typeorm'; +import { getConnectionManager } from 'typeorm'; +import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { useContainer as classValidatorUseContainer } from 'class-validator'; import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis'; import { RateLimiterRedis } from 'rate-limiter-flexible'; @@ -10,18 +10,12 @@ import * as Redis from 'ioredis'; import * as config from 'config'; import { AppModule } from 'src/app.module'; -import { PermissionEntity } from 'src/permission/entities/permission.entity'; -import { RoleEntity } from 'src/role/entities/role.entity'; -import { UserEntity } from 'src/auth/entity/user.entity'; const dbConfig = config.get('db'); export class AppFactory { private constructor( private readonly appInstance: INestApplication, - private readonly database: string, - private readonly connection: Connection, - private readonly queryRunner: QueryRunner, private readonly redis: Redis.Redis ) {} @@ -30,15 +24,12 @@ export class AppFactory { } static async new() { - const { database, connection, queryRunner } = await setupTestDatabase(); const redis = await setupRedis(); const module = await Test.createTestingModule({ imports: [ AppModule, TypeOrmModule.forRoot({ - ...connection.options, - type: dbConfig.type || 'postgres', - database + ...dbConfiguration }), ThrottlerModule.forRootAsync({ useFactory: () => { @@ -73,17 +64,20 @@ export class AppFactory { await app.init(); - return new AppFactory(app, database, connection, queryRunner, redis); + return new AppFactory(app, redis); } async refreshDatabase() { - await Promise.all( - this.connection.entityMetadatas.map((entity) => { - return this.connection - .getRepository(entity.name) - .query(`TRUNCATE TABLE "${entity.tableName}" CASCADE`); - }) - ); + const connection = getConnectionManager().get(); + if (connection.isConnected) { + const tables = connection.entityMetadatas + .map((entity) => `"${entity.tableName}"`) + .join(', '); + + await connection.query( + `TRUNCATE TABLE ${tables} RESTART IDENTITY CASCADE;` + ); + } } async teardown(redis: Redis.Redis) { @@ -96,10 +90,22 @@ export class AppFactory { } async close() { - await this.connection.close(); - // await this.queryRunner.query(`TRUNCATE DATABASE "${this.database}"`); - await this.teardown(this.redis); await this.appInstance.close(); + if (this.redis) await this.teardown(this.redis); + const connection = getConnectionManager().get(); + + if (connection.isConnected) { + connection + .close() + .then(() => { + console.log('DB connection closed'); + }) + .catch((err: any) => { + console.error('Error clossing connection to DB, ', err); + }); + } else { + console.log('DB connection already closed.'); + } } } @@ -112,25 +118,18 @@ const setupRedis = async () => { return redis; }; -const setupTestDatabase = async () => { - const database = process.env.DB_DATABASE_NAME || dbConfig.database; - const manager = new ConnectionManager().create({ - type: dbConfig.type, - host: process.env.DB_HOST || dbConfig.host, - port: parseInt(process.env.DB_PORT) || dbConfig.port, - database, - username: process.env.DB_USERNAME || dbConfig.username, - password: process.env.DB_PASSWORD || dbConfig.password, - logging: false, - synchronize: false, - migrationsRun: true, - migrationsTableName: 'migrations', - migrations: [__dirname + '/../../src/migrations/**/*{.ts,.js}'], - // entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] - entities: [UserEntity, RoleEntity, PermissionEntity] - }); - - const queryRunner = manager.createQueryRunner(); - const connection = await manager.connect(); - return { database, connection, queryRunner }; -}; +const dbConfiguration = { + type: dbConfig.type || 'postgres', + host: process.env.DB_HOST || dbConfig.host, + port: parseInt(process.env.DB_PORT) || dbConfig.port, + database: process.env.DB_DATABASE_NAME || dbConfig.database, + username: process.env.DB_USERNAME || dbConfig.username, + password: process.env.DB_PASSWORD || dbConfig.password, + logging: false, + keepConnectionAlive: true, + synchronize: false, + migrationsRun: false, + migrationsTableName: 'migrations', + migrations: [__dirname + '/../../src/migrations/**/*{.ts,.js}'], + entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] +} as TypeOrmModuleOptions; From c84cb92d6124010c7d9d1f2dde674a2679b1df8e Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Sun, 31 Jul 2022 23:54:11 +0545 Subject: [PATCH 19/31] test: update db connection close logic in e2e app factory --- test/e2e/app/app.e2e-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/app/app.e2e-spec.ts b/test/e2e/app/app.e2e-spec.ts index 51a6ee6..8298840 100644 --- a/test/e2e/app/app.e2e-spec.ts +++ b/test/e2e/app/app.e2e-spec.ts @@ -21,6 +21,6 @@ describe('AppController (e2e)', () => { }); afterAll(async () => { - await app.close(); + if (app) await app.close(); }); }); From 951fd8274574965dd64760db9c430468d87249b6 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Sun, 31 Jul 2022 23:58:34 +0545 Subject: [PATCH 20/31] test: update db connection close logic in e2e app factory --- test/e2e/auth/auth.e2e-spec.ts | 2 +- test/e2e/example.e2e-spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/auth/auth.e2e-spec.ts b/test/e2e/auth/auth.e2e-spec.ts index 77eedfd..d327a28 100644 --- a/test/e2e/auth/auth.e2e-spec.ts +++ b/test/e2e/auth/auth.e2e-spec.ts @@ -62,6 +62,6 @@ describe('AuthController (e2e)', () => { }); afterAll(async () => { - await app.close(); + if (app) await app.close(); }); }); diff --git a/test/e2e/example.e2e-spec.ts b/test/e2e/example.e2e-spec.ts index 3787385..ac39094 100644 --- a/test/e2e/example.e2e-spec.ts +++ b/test/e2e/example.e2e-spec.ts @@ -16,6 +16,6 @@ describe('Example Test (e2e)', () => { }); afterAll(async () => { - await app.close(); + if (app) await app.close(); }); }); From 5effca18de7a917bb64084eecb9d9608d11b4b43 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 00:22:54 +0545 Subject: [PATCH 21/31] test: update db connection close logic in e2e app factory --- src/refresh-token/entities/refresh-token.entity.ts | 12 +++++++++--- test/factories/app.ts | 6 ++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/refresh-token/entities/refresh-token.entity.ts b/src/refresh-token/entities/refresh-token.entity.ts index 655e698..d4da93e 100644 --- a/src/refresh-token/entities/refresh-token.entity.ts +++ b/src/refresh-token/entities/refresh-token.entity.ts @@ -1,4 +1,4 @@ -import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { BaseEntity, Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; @Entity({ name: 'refresh_token' @@ -16,10 +16,16 @@ export class RefreshToken extends BaseEntity { @Column() userAgent: string; - @Column() + @Index() + @Column({ + nullable: true + }) browser: string; - @Column() + @Index() + @Column({ + nullable: true + }) os: string; @Column() diff --git a/test/factories/app.ts b/test/factories/app.ts index 03cc9e0..6bae54b 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -127,9 +127,7 @@ const dbConfiguration = { password: process.env.DB_PASSWORD || dbConfig.password, logging: false, keepConnectionAlive: true, - synchronize: false, - migrationsRun: false, - migrationsTableName: 'migrations', - migrations: [__dirname + '/../../src/migrations/**/*{.ts,.js}'], + synchronize: process.env.NODE_ENV === 'test', + dropSchema: process.env.NODE_ENV === 'test', entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] } as TypeOrmModuleOptions; From 9d2162f857ebfddecadab01200ab126a047d1688 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 01:00:24 +0545 Subject: [PATCH 22/31] test: update e2e configuration --- src/config/ormconfig.ts | 6 +- .../entities/refresh-token.entity.ts | 8 +- test/e2e/app/app.e2e-spec.ts | 5 +- test/e2e/auth/auth.e2e-spec.ts | 5 +- test/e2e/example.e2e-spec.ts | 5 +- test/factories/app.ts | 92 ++++++++----------- 6 files changed, 61 insertions(+), 60 deletions(-) diff --git a/src/config/ormconfig.ts b/src/config/ormconfig.ts index a0b932a..0ad3102 100644 --- a/src/config/ormconfig.ts +++ b/src/config/ormconfig.ts @@ -11,7 +11,11 @@ const ormConfig: ConnectionOptions = { database: process.env.DB_DATABASE_NAME || dbConfig.database, migrationsTransactionMode: 'each', entities: [__dirname + '/../**/*.entity.{js,ts}'], - synchronize: process.env.TYPE_ORM_SYNC || dbConfig.synchronize, + logging: false, + synchronize: false, + migrationsRun: process.env.NODE_ENV === 'test', + dropSchema: process.env.NODE_ENV === 'test', + migrationsTableName: 'migrations', migrations: [__dirname + '/../database/migrations/**/*{.ts,.js}'], cli: { migrationsDir: 'src/database/migrations' diff --git a/src/refresh-token/entities/refresh-token.entity.ts b/src/refresh-token/entities/refresh-token.entity.ts index d4da93e..fc449a2 100644 --- a/src/refresh-token/entities/refresh-token.entity.ts +++ b/src/refresh-token/entities/refresh-token.entity.ts @@ -1,4 +1,10 @@ -import { BaseEntity, Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'; +import { + BaseEntity, + Column, + Entity, + Index, + PrimaryGeneratedColumn +} from 'typeorm'; @Entity({ name: 'refresh_token' diff --git a/test/e2e/app/app.e2e-spec.ts b/test/e2e/app/app.e2e-spec.ts index 8298840..780f822 100644 --- a/test/e2e/app/app.e2e-spec.ts +++ b/test/e2e/app/app.e2e-spec.ts @@ -6,11 +6,12 @@ describe('AppController (e2e)', () => { let app: AppFactory; beforeAll(async () => { + await AppFactory.dropTables(); app = await AppFactory.new(); }); beforeEach(async () => { - await app.refreshDatabase(); + await AppFactory.cleanupDB(); }); it('/ (GET)', () => { @@ -21,6 +22,6 @@ describe('AppController (e2e)', () => { }); afterAll(async () => { - if (app) await app.close(); + await app.close(); }); }); diff --git a/test/e2e/auth/auth.e2e-spec.ts b/test/e2e/auth/auth.e2e-spec.ts index d327a28..93c12fa 100644 --- a/test/e2e/auth/auth.e2e-spec.ts +++ b/test/e2e/auth/auth.e2e-spec.ts @@ -10,11 +10,12 @@ describe('AuthController (e2e)', () => { let app: AppFactory; beforeAll(async () => { + await AppFactory.dropTables(); app = await AppFactory.new(); }); beforeEach(async () => { - await app.refreshDatabase(); + await AppFactory.cleanupDB(); }); it('POST /auth/login requires valid username and password', async () => { @@ -62,6 +63,6 @@ describe('AuthController (e2e)', () => { }); afterAll(async () => { - if (app) await app.close(); + await app.close(); }); }); diff --git a/test/e2e/example.e2e-spec.ts b/test/e2e/example.e2e-spec.ts index ac39094..c21fa8f 100644 --- a/test/e2e/example.e2e-spec.ts +++ b/test/e2e/example.e2e-spec.ts @@ -4,11 +4,12 @@ describe('Example Test (e2e)', () => { let app: AppFactory; beforeAll(async () => { + await AppFactory.dropTables(); app = await AppFactory.new(); }); beforeEach(async () => { - await app.refreshDatabase(); + await AppFactory.cleanupDB(); }); it('it passes', async () => { @@ -16,6 +17,6 @@ describe('Example Test (e2e)', () => { }); afterAll(async () => { - if (app) await app.close(); + await app.close(); }); }); diff --git a/test/factories/app.ts b/test/factories/app.ts index 6bae54b..d772ea6 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -1,7 +1,7 @@ import { ThrottlerModule } from '@nestjs/throttler'; import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { getConnectionManager } from 'typeorm'; +import { createConnection, getConnection, getConnectionManager } from 'typeorm'; import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { useContainer as classValidatorUseContainer } from 'class-validator'; import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis'; @@ -25,12 +25,9 @@ export class AppFactory { static async new() { const redis = await setupRedis(); - const module = await Test.createTestingModule({ + const moduleBuilder = Test.createTestingModule({ imports: [ AppModule, - TypeOrmModule.forRoot({ - ...dbConfiguration - }), ThrottlerModule.forRootAsync({ useFactory: () => { return { @@ -53,13 +50,12 @@ export class AppFactory { blockDuration: 3000 }); } - }) - .compile(); + }); - const app = module.createNestApplication(); + const module = await moduleBuilder.compile(); - classValidatorUseContainer(app.select(AppModule), { - fallbackOnErrors: true + const app = module.createNestApplication(undefined, { + logger: false }); await app.init(); @@ -67,19 +63,44 @@ export class AppFactory { return new AppFactory(app, redis); } - async refreshDatabase() { - const connection = getConnectionManager().get(); - if (connection.isConnected) { - const tables = connection.entityMetadatas - .map((entity) => `"${entity.tableName}"`) - .join(', '); + async close() { + await getConnection().dropDatabase(); + if (this.redis) await this.teardown(this.redis); + if (this.appInstance) await this.appInstance.close(); + } + + static async cleanupDB() { + const connection = getConnection(); + const tables = connection.entityMetadatas.map( + (entity) => `"${entity.tableName}"` + ); - await connection.query( - `TRUNCATE TABLE ${tables} RESTART IDENTITY CASCADE;` - ); + for (const table of tables) { + await connection.query(`DELETE FROM ${table};`); } } + static async dropTables() { + const connection = await createConnection({ + type: dbConfig.type || 'postgres', + host: process.env.DB_HOST || dbConfig.host, + port: parseInt(process.env.DB_PORT) || dbConfig.port, + database: process.env.DB_DATABASE_NAME || dbConfig.database, + username: process.env.DB_USERNAME || dbConfig.username, + password: process.env.DB_PASSWORD || dbConfig.password + }); + + await connection.query(`SET session_replication_role = 'replica';`); + const tables = connection.entityMetadatas.map( + (entity) => `"${entity.tableName}"` + ); + for (const tableName of tables) { + await connection.query(`DROP TABLE IF EXISTS ${tableName};`); + } + + await connection.close(); + } + async teardown(redis: Redis.Redis) { return new Promise((resolve) => { redis.quit(); @@ -88,25 +109,6 @@ export class AppFactory { }); }); } - - async close() { - await this.appInstance.close(); - if (this.redis) await this.teardown(this.redis); - const connection = getConnectionManager().get(); - - if (connection.isConnected) { - connection - .close() - .then(() => { - console.log('DB connection closed'); - }) - .catch((err: any) => { - console.error('Error clossing connection to DB, ', err); - }); - } else { - console.log('DB connection already closed.'); - } - } } const setupRedis = async () => { @@ -117,17 +119,3 @@ const setupRedis = async () => { await redis.flushall(); return redis; }; - -const dbConfiguration = { - type: dbConfig.type || 'postgres', - host: process.env.DB_HOST || dbConfig.host, - port: parseInt(process.env.DB_PORT) || dbConfig.port, - database: process.env.DB_DATABASE_NAME || dbConfig.database, - username: process.env.DB_USERNAME || dbConfig.username, - password: process.env.DB_PASSWORD || dbConfig.password, - logging: false, - keepConnectionAlive: true, - synchronize: process.env.NODE_ENV === 'test', - dropSchema: process.env.NODE_ENV === 'test', - entities: [__dirname + '/../../src/**/*.entity{.ts,.js}'] -} as TypeOrmModuleOptions; From cd8b2a0ad2a1df0d594fa45625c30d3862aae0ba Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 01:44:16 +0545 Subject: [PATCH 23/31] test: github action workflow updated --- .github/workflows/ci.yml | 47 +++++++++++++++++++++++++++++++ .github/workflows/test.yml | 57 -------------------------------------- docker-compose-test.yml | 14 ++++++++++ package.json | 2 +- 4 files changed, 62 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/test.yml create mode 100644 docker-compose-test.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3ad817e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: Continuous Integration Testing + +on: push + +jobs: + unit-tests: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [14.x, 16.x] + test: + [ + truthy-cms, + ] + + steps: + - uses: actions/checkout@v2 + - name: Use NodeJS ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + - name: Install dependencies + run: npm install + + - name: Run unit test + run: npm run test:unit + + e2e-test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [14.x, 16.x] + needs: [unit-tests] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + - name: Start Docker-Compose + run: docker-compose -f docker-compose-test.yml up -d + - name: Install dependencies + run: npm install + - name: Run tests + run: npm run test:e2e + - name: Stop Docker-Compose + run: docker-compose down \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 31810c6..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: test - -on: push - -jobs: - test: - runs-on: ubuntu-latest - - services: - postgres: - image: postgres:14 - env: - POSTGRES_DB: truthy_db - POSTGRES_USER: truthy_user - POSTGRES_PASSWORD: truthy_password - ports: - - 5432:5432 - - redis: - image: redis - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 6379:6379 - - steps: - - uses: actions/checkout@v2 - - - name: Setup Node.js - uses: actions/setup-node@v2 - with: - node-version: 14.3.0 - - - name: Set environment variables - uses: allenevans/set-env@v2.0.0 - with: - NODE_ENV: test - DB_HOST: 127.0.0.1 - DB_PORT: ${{ job.services.postgres.ports[5432] }} - DB_DATABASE_NAME: truthy_db - DB_USERNAME: truthy_user - DB_PASSWORD: truthy_password - - - name: Install dependencies - run: npm install - - - name: Run linter - run: npm run lint - - - name: Run unit test - run: npm run test:unit - - - name: Run e2e test - run: npm run test:e2e \ No newline at end of file diff --git a/docker-compose-test.yml b/docker-compose-test.yml new file mode 100644 index 0000000..18918b1 --- /dev/null +++ b/docker-compose-test.yml @@ -0,0 +1,14 @@ +version: '3' +services: + truthy-postgres: + image: postgres + ports: + - '5432:5432' + environment: + - POSTGRES_USER=truthy_user + - POSTGRES_PASSWORD=truthy_password + - POSTGRES_DB=truthy_db + redis: + image: redis + ports: + - '6379:6379' \ No newline at end of file diff --git a/package.json b/package.json index b43cd9f..ddb1f1a 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "migrate": "npm run typeorm migration:run", "migration:rollback": "npm run typeorm migration:revert", "seed": "ts-node -r tsconfig-paths/register ./node_modules/typeorm-seeding/dist/cli.js seed -n src/config/ormconfig.ts", - "precommit": "lint-staged && npm run lint && npm run test:unit && npm run test:e2e", + "precommit": "lint-staged && npm run lint", "prepush": "npm run test:unit && npm run test:e2e", "prepare": "husky install" }, From dbdf0de054e3c4340ffdd3a374cb8e82f16f1542 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 01:51:35 +0545 Subject: [PATCH 24/31] test: github action workflow updated --- .github/workflows/ci.yml | 4 ++++ package.json | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ad817e..01d4858 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,10 @@ jobs: uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} + - name: Set Environment Variables + uses: ./.github/actions/setvars + with: + varFilePath: ./.github/variables/myvars.env - name: Start Docker-Compose run: docker-compose -f docker-compose-test.yml up -d - name: Install dependencies diff --git a/package.json b/package.json index ddb1f1a..4e1d4d4 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "test:watch": "jest --config ./test/unit/jest-unit.json --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/e2e/jest-e2e.json --runInBand --detectOpenHandles", - "test:unit": "jest --config ./test/unit/jest-unit.json --runInBand", + "test:e2e": "NODE_ENV=test jest --config ./test/e2e/jest-e2e.json --runInBand --detectOpenHandles", + "test:unit": "NODE_ENV=test jest --config ./test/unit/jest-unit.json --runInBand", "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --config src/config/ormconfig.ts", "orm-create": "npm run typeorm migration:create -- -n", "migrate": "npm run typeorm migration:run", From 107a02f90c2a985d036561d778a6a28040034388 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 02:00:10 +0545 Subject: [PATCH 25/31] test: github action workflow updated --- docker-compose-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 18918b1..7ac2688 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -7,7 +7,7 @@ services: environment: - POSTGRES_USER=truthy_user - POSTGRES_PASSWORD=truthy_password - - POSTGRES_DB=truthy_db + - POSTGRES_DB=truthy_test redis: image: redis ports: From 14c698197bb6db5d68fc523a4f1b5660f73d9833 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 02:04:50 +0545 Subject: [PATCH 26/31] test: github action workflow updated --- docker-compose-test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 7ac2688..d092cbd 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -6,8 +6,8 @@ services: - '5432:5432' environment: - POSTGRES_USER=truthy_user - - POSTGRES_PASSWORD=truthy_password - - POSTGRES_DB=truthy_test + - POSTGRES_PASSWORD=truthypwd + - POSTGRES_DB=truthy_db redis: image: redis ports: From 14d8fbfcb35a9bfb2acdd7ee7d3be811e621b8f0 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 09:58:27 +0545 Subject: [PATCH 27/31] test: github action workflow updated --- .github/workflows/ci.yml | 2 +- docker-compose-test.yml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01d4858..5fa5433 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,4 +48,4 @@ jobs: - name: Run tests run: npm run test:e2e - name: Stop Docker-Compose - run: docker-compose down \ No newline at end of file + run: docker-compose -f docker-compose-test.yml down \ No newline at end of file diff --git a/docker-compose-test.yml b/docker-compose-test.yml index d092cbd..565a86b 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -7,7 +7,8 @@ services: environment: - POSTGRES_USER=truthy_user - POSTGRES_PASSWORD=truthypwd - - POSTGRES_DB=truthy_db + - POSTGRES_DB= + redis: image: redis ports: From 98033ed89281ab19dbef8adc80bcc0dee46c0439 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 10:21:13 +0545 Subject: [PATCH 28/31] test: github action workflow envars updated --- docker-compose-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 565a86b..9c375c9 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -7,7 +7,7 @@ services: environment: - POSTGRES_USER=truthy_user - POSTGRES_PASSWORD=truthypwd - - POSTGRES_DB= + - POSTGRES_DB=truthy_db redis: image: redis From 7fb821c6e8bb4276123f5e6c074ce156f6e99a4e Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 11:16:28 +0545 Subject: [PATCH 29/31] test: github action e2e env nodejs version fixed --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fa5433..a0038d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [14.x, 16.x] + node-version: [16.x] test: [ truthy-cms, @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [14.x, 16.x] + node-version: [16.x] needs: [unit-tests] steps: - uses: actions/checkout@v1 From 16c5f5173151812c440aa49eb354a941338b2f62 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 11:29:42 +0545 Subject: [PATCH 30/31] chore: fix lint issue --- test/factories/app.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/factories/app.ts b/test/factories/app.ts index d772ea6..bff51b2 100644 --- a/test/factories/app.ts +++ b/test/factories/app.ts @@ -1,9 +1,7 @@ import { ThrottlerModule } from '@nestjs/throttler'; import { INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { createConnection, getConnection, getConnectionManager } from 'typeorm'; -import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; -import { useContainer as classValidatorUseContainer } from 'class-validator'; +import { createConnection, getConnection } from 'typeorm'; import { ThrottlerStorageRedisService } from 'nestjs-throttler-storage-redis'; import { RateLimiterRedis } from 'rate-limiter-flexible'; import * as Redis from 'ioredis'; From 200ddef8b471b4be0f7ed2db047ca12c1609d450 Mon Sep 17 00:00:00 2001 From: Roshan Ranabhat Date: Mon, 1 Aug 2022 11:46:49 +0545 Subject: [PATCH 31/31] chore: fix lint issue --- .github/workflows/ci.yml | 11 ++++------- docker-compose-test.yml | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0038d3..bf23663 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,16 +2,13 @@ name: Continuous Integration Testing on: push -jobs: +jobs: unit-tests: runs-on: ubuntu-latest strategy: matrix: node-version: [16.x] - test: - [ - truthy-cms, - ] + test: [truthy-cms] steps: - uses: actions/checkout@v2 @@ -24,7 +21,7 @@ jobs: - name: Run unit test run: npm run test:unit - + e2e-test: runs-on: ubuntu-latest strategy: @@ -48,4 +45,4 @@ jobs: - name: Run tests run: npm run test:e2e - name: Stop Docker-Compose - run: docker-compose -f docker-compose-test.yml down \ No newline at end of file + run: docker-compose -f docker-compose-test.yml down diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 9c375c9..8e88eae 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -8,8 +8,8 @@ services: - POSTGRES_USER=truthy_user - POSTGRES_PASSWORD=truthypwd - POSTGRES_DB=truthy_db - + redis: image: redis ports: - - '6379:6379' \ No newline at end of file + - '6379:6379'