From f0e6f6150de7b9aa4bbc1eb02a1d72418ea26faa Mon Sep 17 00:00:00 2001 From: David Mehren Date: Fri, 4 Mar 2022 13:13:24 +0100 Subject: [PATCH] test: allow running e2e tests with different DBs This commits adds infrastructure for running the E2E tests with databases other than sqlite. Initially, support for Postgres is added. Signed-off-by: David Mehren --- package.json | 2 + test/test-setup.ts | 102 +++++++++++++++++++++++++++++++--- yarn.lock | 136 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index d2a49b4961..dc91ced40e 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "passport": "0.5.2", "passport-http-bearer": "1.0.1", "passport-local": "1.0.0", + "pg": "^8.7.3", "raw-body": "2.5.1", "reflect-metadata": "0.1.13", "rimraf": "3.0.2", @@ -73,6 +74,7 @@ "@types/jest": "27.4.1", "@types/node": "16.11.26", "@types/passport-local": "1.0.34", + "@types/pg": "^8", "@types/source-map-support": "0.5.4", "@types/supertest": "2.0.11", "@typescript-eslint/eslint-plugin": "5.13.0", diff --git a/test/test-setup.ts b/test/test-setup.ts index d2b929c330..bc0e696ed8 100644 --- a/test/test-setup.ts +++ b/test/test-setup.ts @@ -6,8 +6,9 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { NestExpressApplication } from '@nestjs/platform-express'; import { Test, TestingModule, TestingModuleBuilder } from '@nestjs/testing'; -import { TypeOrmModule } from '@nestjs/typeorm'; +import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; import { RouterModule, Routes } from 'nest-router'; +import { Connection, createConnection } from 'typeorm'; import { PrivateApiModule } from '../src/api/private/private-api.module'; import { PublicApiModule } from '../src/api/public/public-api.module'; @@ -32,6 +33,7 @@ import { HistoryModule } from '../src/history/history.module'; import { HistoryService } from '../src/history/history.service'; import { IdentityModule } from '../src/identity/identity.module'; import { IdentityService } from '../src/identity/identity.service'; +import { ConsoleLoggerService } from '../src/logger/console-logger.service'; import { LoggerModule } from '../src/logger/logger.module'; import { MediaModule } from '../src/media/media.module'; import { MediaService } from '../src/media/media.service'; @@ -46,6 +48,7 @@ import { User } from '../src/users/user.entity'; import { UsersModule } from '../src/users/users.module'; import { UsersService } from '../src/users/users.service'; import { setupSessionMiddleware } from '../src/utils/session'; +import { setupValidationPipe } from '../src/utils/setup-pipes'; export class TestSetup { moduleRef: TestingModule; @@ -65,6 +68,36 @@ export class TestSetup { authTokens: AuthTokenWithSecretDto[] = []; anonymousNotes: Note[] = []; ownedNotes: Note[] = []; + + /** + * Cleans up remnants from a test run from the database + */ + public async cleanup() { + const appConnection = this.app.get(Connection); + const connectionOptions = appConnection.options; + if (!connectionOptions.database) { + throw new Error('Database name not set in connection options'); + } + if (connectionOptions.type === 'sqlite') { + // Bail out early, as SQLite runs from memory anyway + return; + } + if (appConnection.isConnected) { + await appConnection.close(); + } + switch (connectionOptions.type) { + case 'postgres': { + const connection = await createConnection({ + type: 'postgres', + username: 'hedgedoc', + password: 'hedgedoc', + database: 'postgres', + }); + await connection.query(`DROP DATABASE ${connectionOptions.database}`); + await connection.close(); + } + } + } } /** @@ -80,11 +113,66 @@ export class TestSetupBuilder { private testingModuleBuilder: TestingModuleBuilder; private testSetup = new TestSetup(); + private testId: string; + + /** + * Prepares a test database + * @param dbName The name of the database to use + * @private + */ + private static async setupTestDB(dbName: string) { + switch (process.env.HEDGEDOC_TEST_DB_TYPE || 'sqlite') { + case 'sqlite': + return; + case 'postgres': { + // Create a connection to internal postgres database to then create a test db + const connection = await createConnection({ + type: 'postgres', + username: 'hedgedoc', + password: 'hedgedoc', + database: 'postgres', + }); + await connection.query(`CREATE DATABASE ${dbName}`); + await connection.close(); + return; + } + default: + throw new Error('Unknown database type in HEDGEDOC_TEST_DB_TYPE'); + } + } + + private static getTestDBConf(dbName: string): TypeOrmModuleOptions { + switch (process.env.HEDGEDOC_TEST_DB_TYPE || 'sqlite') { + case 'sqlite': + return { + type: 'sqlite', + database: ':memory:', + autoLoadEntities: true, + synchronize: true, + dropSchema: true, + }; + case 'postgres': + return { + type: 'postgres', + database: dbName, + username: 'hedgedoc', + password: 'hedgedoc', + autoLoadEntities: true, + synchronize: true, + dropSchema: true, + }; + default: + throw new Error('Unknown database type in HEDGEDOC_TEST_DB_TYPE'); + } + } + /** * Creates a new instance of TestSetupBuilder */ public static create(): TestSetupBuilder { const testSetupBuilder = new TestSetupBuilder(); + testSetupBuilder.testId = + 'hedgedoc_test_' + Math.random().toString(36).substring(2, 15); const routes: Routes = [ { path: '/api/v2', @@ -98,13 +186,9 @@ export class TestSetupBuilder { testSetupBuilder.testingModuleBuilder = Test.createTestingModule({ imports: [ RouterModule.forRoutes(routes), - TypeOrmModule.forRoot({ - type: 'sqlite', - database: ':memory:', - autoLoadEntities: true, - synchronize: true, - dropSchema: true, - }), + TypeOrmModule.forRoot( + TestSetupBuilder.getTestDBConf(testSetupBuilder.testId), + ), ConfigModule.forRoot({ isGlobal: true, load: [ @@ -146,6 +230,8 @@ export class TestSetupBuilder { * Builds the final TestSetup from the configured builder */ public async build(): Promise { + await TestSetupBuilder.setupTestDB(this.testId); + for (const setupFunction of this.setupPreCompile) { await setupFunction(); } diff --git a/yarn.lock b/yarn.lock index 058392e6ce..0f0ba23647 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1796,6 +1796,17 @@ __metadata: languageName: node linkType: hard +"@types/pg@npm:^8": + version: 8.6.4 + resolution: "@types/pg@npm:8.6.4" + dependencies: + "@types/node": "*" + pg-protocol: "*" + pg-types: ^2.2.0 + checksum: cebc40d6f034ff5651a8415e38aeacf86925d6a7eebfd3d20a7db9e7935b0a376a81ecdfffcbeb54425344ecc9a5a8886955bcbb3501226521209900d7a57aef + languageName: node + linkType: hard + "@types/prettier@npm:^2.1.5": version: 2.4.4 resolution: "@types/prettier@npm:2.4.4" @@ -3022,6 +3033,13 @@ __metadata: languageName: node linkType: hard +"buffer-writer@npm:2.0.0": + version: 2.0.0 + resolution: "buffer-writer@npm:2.0.0" + checksum: 11736b48bb75106c52ca8ec9f025e7c1b3b25ce31875f469d7210eabd5c576c329e34f6b805d4a8d605ff3f0db1e16342328802c4c963e9c826b0e43a4e631c2 + languageName: node + linkType: hard + "buffer-xor@npm:^1.0.3": version: 1.0.3 resolution: "buffer-xor@npm:1.0.3" @@ -5361,6 +5379,7 @@ __metadata: "@types/node-fetch": 2.6.1 "@types/passport-http-bearer": 1.0.37 "@types/passport-local": 1.0.34 + "@types/pg": ^8 "@types/source-map-support": 0.5.4 "@types/supertest": 2.0.11 "@typescript-eslint/eslint-plugin": 5.13.0 @@ -5389,6 +5408,7 @@ __metadata: passport: 0.5.2 passport-http-bearer: 1.0.1 passport-local: 1.0.0 + pg: ^8.7.3 prettier: 2.5.1 raw-body: 2.5.1 reflect-metadata: 0.1.13 @@ -7918,6 +7938,13 @@ __metadata: languageName: node linkType: hard +"packet-reader@npm:1.0.0": + version: 1.0.0 + resolution: "packet-reader@npm:1.0.0" + checksum: 0b7516f0cbf3e322aad591bed29ba544220088c53943145c0d9121a6f59182ad811f7fd6785a8979a34356aca69d97653689029964c5998dc02645633d88ffd7 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -8107,6 +8134,78 @@ __metadata: languageName: node linkType: hard +"pg-connection-string@npm:^2.5.0": + version: 2.5.0 + resolution: "pg-connection-string@npm:2.5.0" + checksum: a6f3a068f7c9416a5b33a326811caf0dfaaee045c225b7c628b4c9b4e9a2b25bdd12a21e4c48940e1000ea223a4e608ca122d2ff3dd08c8b1db0fc9f5705133a + languageName: node + linkType: hard + +"pg-int8@npm:1.0.1": + version: 1.0.1 + resolution: "pg-int8@npm:1.0.1" + checksum: a1e3a05a69005ddb73e5f324b6b4e689868a447c5fa280b44cd4d04e6916a344ac289e0b8d2695d66e8e89a7fba023affb9e0e94778770ada5df43f003d664c9 + languageName: node + linkType: hard + +"pg-pool@npm:^3.5.1": + version: 3.5.1 + resolution: "pg-pool@npm:3.5.1" + peerDependencies: + pg: ">=8.0" + checksum: 42833c25f18fee41a1b2d955978f1403e93164762a7e57d3a870429103d302f1899b393ab021bb8144272037eb3f13bdb9f16a4c4afaa3efd3d2c3689738038f + languageName: node + linkType: hard + +"pg-protocol@npm:*, pg-protocol@npm:^1.5.0": + version: 1.5.0 + resolution: "pg-protocol@npm:1.5.0" + checksum: b839d12cafe942ef9cbc5b13c174eb2356804fb4fe8ead8279f46a36be90722d19a91409955beb8a3d5301639c44854e49749de4aef02dc361fee3e2a61fb1e4 + languageName: node + linkType: hard + +"pg-types@npm:^2.1.0, pg-types@npm:^2.2.0": + version: 2.2.0 + resolution: "pg-types@npm:2.2.0" + dependencies: + pg-int8: 1.0.1 + postgres-array: ~2.0.0 + postgres-bytea: ~1.0.0 + postgres-date: ~1.0.4 + postgres-interval: ^1.1.0 + checksum: bf4ec3f594743442857fb3a8dfe5d2478a04c98f96a0a47365014557cbc0b4b0cee01462c79adca863b93befbf88f876299b75b72c665b5fb84a2c94fbd10316 + languageName: node + linkType: hard + +"pg@npm:^8.7.3": + version: 8.7.3 + resolution: "pg@npm:8.7.3" + dependencies: + buffer-writer: 2.0.0 + packet-reader: 1.0.0 + pg-connection-string: ^2.5.0 + pg-pool: ^3.5.1 + pg-protocol: ^1.5.0 + pg-types: ^2.1.0 + pgpass: 1.x + peerDependencies: + pg-native: ">=2.0.0" + peerDependenciesMeta: + pg-native: + optional: true + checksum: d0e7040967779b9ccea16897f099510bcaf6bc86f77a6d8fa7e293c24d8bd2fd2ec46d99d6d1adc9be4cc8f254aa909361346b693088c1ba4501414f7afb2fe3 + languageName: node + linkType: hard + +"pgpass@npm:1.x": + version: 1.0.5 + resolution: "pgpass@npm:1.0.5" + dependencies: + split2: ^4.1.0 + checksum: 947ac096c031eebdf08d989de2e9f6f156b8133d6858c7c2c06c041e1e71dda6f5f3bad3c0ec1e96a09497bbc6ef89e762eefe703b5ef9cb2804392ec52ec400 + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -8144,6 +8243,36 @@ __metadata: languageName: node linkType: hard +"postgres-array@npm:~2.0.0": + version: 2.0.0 + resolution: "postgres-array@npm:2.0.0" + checksum: 0e1e659888147c5de579d229a2d95c0d83ebdbffc2b9396d890a123557708c3b758a0a97ed305ce7f58edfa961fa9f0bbcd1ea9f08b6e5df73322e683883c464 + languageName: node + linkType: hard + +"postgres-bytea@npm:~1.0.0": + version: 1.0.0 + resolution: "postgres-bytea@npm:1.0.0" + checksum: d844ae4ca7a941b70e45cac1261a73ee8ed39d72d3d74ab1d645248185a1b7f0ac91a3c63d6159441020f4e1f7fe64689ac56536a307b31cef361e5187335090 + languageName: node + linkType: hard + +"postgres-date@npm:~1.0.4": + version: 1.0.7 + resolution: "postgres-date@npm:1.0.7" + checksum: 5745001d47e51cd767e46bcb1710649cd705d91a24d42fa661c454b6dcbb7353c066a5047983c90a626cd3bbfea9e626cc6fa84a35ec57e5bbb28b49f78e13ed + languageName: node + linkType: hard + +"postgres-interval@npm:^1.1.0": + version: 1.2.0 + resolution: "postgres-interval@npm:1.2.0" + dependencies: + xtend: ^4.0.0 + checksum: 746b71f93805ae33b03528e429dc624706d1f9b20ee81bf743263efb6a0cd79ae02a642a8a480dbc0f09547b4315ab7df6ce5ec0be77ed700bac42730f5c76b2 + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -8972,6 +9101,13 @@ __metadata: languageName: node linkType: hard +"split2@npm:^4.1.0": + version: 4.1.0 + resolution: "split2@npm:4.1.0" + checksum: ec581597cb74c13cdfb5e2047543dd40cb1e8e9803c7b1e0c29ede05f2b4f049b2d6e7f2788a225d544549375719658b8f38e9366364dec35dc7a12edfda5ee5 + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3"