From 4629a387b317464d7dd4fe303055636c54ee8ea9 Mon Sep 17 00:00:00 2001 From: Jesse Hallett Date: Wed, 13 Apr 2022 11:38:44 -0400 Subject: [PATCH] feat: add option to getTestDatabase to pass TypeORM config object (#7) This feature supports projects that use a TypeScript `ormconfig.ts` file. Currently originate-scripts is not able to read TypeORM configuration from TypeScript modules which is required to get `getTestDatabase` to run migrations for you. If you use a TypeScript configuration module then you can import it in your test, and pass the exported value to `getTestDatabase`. For example: ```ts const { stop } = await getTestDatabase({ // We use a callback to delay evaluating `ormconfig` until after // `process.env.DATABASE_URL` is set by `getTestDatabase`. typeormConfig: async () => (await import('../../ormconfig')).default, }) ``` Note that it is important to delay evaluating `ormconfig.ts` until **after** `getTestDatabase` has set the `process.env.DATABASE_URL` environment variable. The example above demonstrates using the `await` operator to import configuration in a callback. To make that example work your `ormconfig.ts` module should export its configuration object as the default export using `export default config`. --- package.json | 4 +-- src/getTestDatabase.ts | 30 +++++++++++++++++++---- src/originate-scripts/commands/dbStart.ts | 8 +++--- src/originate-scripts/environment.ts | 3 ++- yarn.lock | 8 +++--- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 4f98156..28c1abc 100644 --- a/package.json +++ b/package.json @@ -53,11 +53,11 @@ "semantic-release": "^17.3.0", "ts-jest": "^26.4.4", "ts-node": "^9.1.1", - "typescript": "^4.1.3" + "typescript": "^4.6.3" }, "peerDependencies": { "@nestjs/common": ">=7.6.0", "supertest": ">=4.0.0", - "typeorm": ">=0.2.29" + "typeorm": "^0.2.29" } } diff --git a/src/getTestDatabase.ts b/src/getTestDatabase.ts index 4bef9e4..322bcbe 100644 --- a/src/getTestDatabase.ts +++ b/src/getTestDatabase.ts @@ -2,7 +2,7 @@ import { Config, startPostgresContainer, } from "@originate/docker-await-postgres"; -import { createConnection } from "typeorm"; +import type { ConnectionOptions } from "typeorm"; export interface Options { /** @@ -14,6 +14,17 @@ export interface Options { * If true, connect and run migrations according to configuration in `ormconfig.js` */ runMigrations?: boolean; + + /** + * Configuration options for TypeORM. TypeORM is invoked if `runMigrations` is + * set to `true`. If this configuration is not provided then configuration will + * be read from `ormconfig.js` instead. + * + * originate-scripts is currently unable to load TypeScript ormconfig modules. + * If you use TypeScript for your TypeORM configuration then source your + * `ormconfig.ts` file, and pass the exported object as `typeormConfig` here. + */ + typeormConfig?: () => Promise; } /** @@ -23,11 +34,13 @@ export interface Options { * * @param options.image Docker image to run; e.g. `"postgres:12"` (default: "postgres:latest") * @param options.runMigrations If true, connect and run migrations according to configuration in `ormconfig.js` (default: true) + * @param options.typeormConfig Async callback that returns configuration options for TypeORM. If you use an `ormconfig.ts` file you should import it in the callback using the `import` operator so that configuration is evaluated *after* `process.env.DATABASE_URL` is set. If not set config for migrations will be read from `ormconfig.js`. */ export async function getTestDatabase({ image = "postgres:latest", runMigrations = true, -}: Options): Promise<{ + typeormConfig, +}: Options = {}): Promise<{ stop: () => Promise; }> { const config: Config = { @@ -41,10 +54,17 @@ export async function getTestDatabase({ process.env.DATABASE_URL = `postgres://${config.user}:${config.password}@localhost:${port}/${config.database}`; if (runMigrations) { - const conn = await createConnection(); - await conn.runMigrations(); - await conn.close(); + await runTypeormMigrations(await typeormConfig?.()); } return { stop }; } + +async function runTypeormMigrations(typeormConfig?: ConnectionOptions) { + const typeorm = await import("typeorm"); + const conn = typeormConfig + ? await typeorm.createConnection(typeormConfig) + : await typeorm.createConnection(); + await conn.runMigrations(); + await conn.close(); +} diff --git a/src/originate-scripts/commands/dbStart.ts b/src/originate-scripts/commands/dbStart.ts index 7fbabb0..dc29c44 100644 --- a/src/originate-scripts/commands/dbStart.ts +++ b/src/originate-scripts/commands/dbStart.ts @@ -73,9 +73,8 @@ async function createContainer(opts: { Tty: false, }); } catch (err) { - throw new Error( - `failed to create a new database container: ${err.message}` - ); + const message = err instanceof Error ? err.message : ""; + throw new Error(`failed to create a new database container: ${message}`); } } @@ -83,8 +82,9 @@ async function startNew(container: Dockerode.Container): Promise { try { await container.start(); } catch (err) { + const message = err instanceof Error ? err.message : ""; throw new Error( - `created a new container, but there was an error starting it: ${err.message}` + `created a new container, but there was an error starting it: ${message}` ); } } diff --git a/src/originate-scripts/environment.ts b/src/originate-scripts/environment.ts index 4dbd2eb..15df4d8 100644 --- a/src/originate-scripts/environment.ts +++ b/src/originate-scripts/environment.ts @@ -24,8 +24,9 @@ export function databasePort(): string { const url = new URL(env.DATABASE_URL); return url.port || "5432"; } catch (err) { + const message = err instanceof Error ? err.message : ""; throw new Error( - `there was an error reading database information from your DATABASE_URL environment variable: ${err.message}` + `there was an error reading database information from your DATABASE_URL environment variable: ${message}` ); } } diff --git a/yarn.lock b/yarn.lock index a1b175e..e2705ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7196,10 +7196,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" - integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== +typescript@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" + integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== uglify-js@^3.1.4: version "3.12.3"