From 26123daf4e275bba180ccc5e292c364c5888f1c8 Mon Sep 17 00:00:00 2001 From: Max Isom Date: Mon, 8 Apr 2024 17:11:58 -0700 Subject: [PATCH] Fix docs --- README.md | 56 +++++++++++------ src/public-types.ts | 79 +++++++++++------------- src/tests/cleanup/does-database-exist.ts | 35 ----------- src/tests/hooks.test.ts | 6 +- 4 files changed, 75 insertions(+), 101 deletions(-) delete mode 100644 src/tests/cleanup/does-database-exist.ts diff --git a/README.md b/README.md index 2538bf0..5327cd7 100644 --- a/README.md +++ b/README.md @@ -148,40 +148,56 @@ This works across the entire test suite. Note that if unique parameters are passed to the `beforeTemplateIsBaked` (`null` in the above example), separate databases will still be created. -### "Nested" `beforeTemplateIsBaked` calls +### Manual template creation -In some cases, if you do extensive setup in your `beforeTemplateIsBaked` hook, you might want to obtain a separate, additional database within it if your application uses several databases for different purposes. This is possible by using the passed `beforeTemplateIsBaked` to your hook callback: +In some cases, if you do extensive setup in your `beforeTemplateIsBaked` hook, you might want to obtain a separate, additional database within it if your application uses several databases for different purposes. This is possible by using the `manuallyBuildAdditionalTemplate()` function passed to your hook callback: ```ts -type DatabaseParams = { - type: "foo" | "bar" -} +import test from "ava" -const getTestServer = getTestPostgresDatabaseFactory({ +const getTestDatabase = getTestPostgresDatabaseFactory({ beforeTemplateIsBaked: async ({ params, connection: { pool }, - beforeTemplateIsBaked, + manuallyBuildAdditionalTemplate, }) => { - if (params.type === "foo") { - await pool.query(`CREATE TABLE "foo" ("id" SERIAL PRIMARY KEY)`) - // Important: return early to avoid infinite loop - return - } - await pool.query(`CREATE TABLE "bar" ("id" SERIAL PRIMARY KEY)`) - // This created database will be torn down at the end of the top-level `beforeTemplateIsBaked` call - const fooDatabase = await beforeTemplateIsBaked({ - params: { type: "foo" }, - }) - // This works now - await fooDatabase.pool.query(`INSERT INTO "foo" DEFAULT VALUES`) + const fooTemplateBuilder = await manuallyBuildAdditionalTemplate() + await fooTemplateBuilder.connection.pool.query( + `CREATE TABLE "foo" ("id" SERIAL PRIMARY KEY)` + ) + const { templateName: fooTemplateName } = await fooTemplateBuilder.finish() + + return { fooTemplateName } }, }) + +test("foo", async (t) => { + const barDatabase = await getTestDatabase({ type: "bar" }) + + // the "bar" database has the "bar" table... + await t.notThrowsAsync(async () => { + await barDatabase.pool.query(`SELECT * FROM "bar"`) + }) + + // ...but not the "foo" table... + await t.throwsAsync(async () => { + await barDatabase.pool.query(`SELECT * FROM "foo"`) + }) + + // ...and we can obtain a separate database with the "foo" table + const fooDatabase = await getTestDatabase.fromTemplate( + t, + barDatabase.beforeTemplateIsBakedResult.fooTemplateName + ) + await t.notThrowsAsync(async () => { + await fooDatabase.pool.query(`SELECT * FROM "foo"`) + }) +}) ``` -Be very careful when using this to avoid infinite loops. +Although it's not shown in the above example, because of `ava-postgres`'s automatic de-duping by parameter combinations, any returned template name is "linked" to the parameters passed to the `getTestDatabase()` function. ### Bind mounts & `exec`ing in the container diff --git a/src/public-types.ts b/src/public-types.ts index 4ea8e54..92e4d2c 100644 --- a/src/public-types.ts +++ b/src/public-types.ts @@ -59,37 +59,52 @@ export interface GetTestPostgresDatabaseFactoryOptions< params: Params containerExec: (command: string[]) => Promise /** + * In some cases, if you do extensive setup in your `beforeTemplateIsBaked` hook, you might want to obtain a separate, additional database within it if your application uses several databases for different purposes. * - * In some cases, if you do extensive setup in your `beforeTemplateIsBaked` hook, you might want to obtain a separate, additional database within it if your application uses several databases for different purposes. This is possible by using the passed `beforeTemplateIsBaked` to your hook callback. - * Be very careful when using this to avoid infinite loops. * @example * ```ts - * type DatabaseParams = { - * type: "foo" | "bar" - * } - - * const getTestServer = getTestPostgresDatabaseFactory({ + * import test from "ava" + * + * const getTestDatabase = getTestPostgresDatabaseFactory({ * beforeTemplateIsBaked: async ({ * params, * connection: { pool }, - * beforeTemplateIsBaked, + * manuallyBuildAdditionalTemplate, * }) => { - * if (params.type === "foo") { - * await pool.query(`CREATE TABLE "foo" ("id" SERIAL PRIMARY KEY)`) - * // Important: return early to avoid infinite loop - * return - * } - * await pool.query(`CREATE TABLE "bar" ("id" SERIAL PRIMARY KEY)`) - * // This created database will be torn down at the end of the top-level `beforeTemplateIsBaked` call - * const fooDatabase = await beforeTemplateIsBaked({ - * params: { type: "foo" }, - * }) - - * // This works now - * await fooDatabase.pool.query(`INSERT INTO "foo" DEFAULT VALUES`) + * + * const fooTemplateBuilder = await manuallyBuildAdditionalTemplate() + * await fooTemplateBuilder.connection.pool.query( + * `CREATE TABLE "foo" ("id" SERIAL PRIMARY KEY)` + * ) + * const { templateName: fooTemplateName } = await fooTemplateBuilder.finish() + * + * return { fooTemplateName } * }, * }) + * + * test("foo", async (t) => { + * const barDatabase = await getTestDatabase({ type: "bar" }) + * + * // the "bar" database has the "bar" table... + * await t.notThrowsAsync(async () => { + * await barDatabase.pool.query(`SELECT * FROM "bar"`) + * }) + * + * // ...but not the "foo" table... + * await t.throwsAsync(async () => { + * await barDatabase.pool.query(`SELECT * FROM "foo"`) + * }) + * + * // ...and we can obtain a separate database with the "foo" table + * const fooDatabase = await getTestDatabase.fromTemplate( + * t, + * barDatabase.beforeTemplateIsBakedResult.fooTemplateName + * ) + * await t.notThrowsAsync(async () => { + * await fooDatabase.pool.query(`SELECT * FROM "foo"`) + * }) + * }) * ``` */ manuallyBuildAdditionalTemplate: () => Promise<{ @@ -104,28 +119,6 @@ export interface GetTestPostgresDatabaseResult extends ConnectionDetails { } export type GetTestPostgresDatabaseOptions = { - /** - * By default, `ava-postgres` will create a new database for each test. If you want to share a database between tests, you can use the `databaseDedupeKey` option. - * This works across the entire test suite. - * - * Note that if unique parameters are passed to the `beforeTemplateIsBaked` (`null` in the above example), separate databases will still be created. - * @example - * ```ts - * import test from "ava" - * - * const getTestPostgresDatabase = getTestPostgresDatabaseFactory({}) - * - * test("foo", async (t) => { - * const connection1 = await getTestPostgresDatabase(t, null, { - * databaseDedupeKey: "foo", - * }) - * const connection2 = await getTestPostgresDatabase(t, null, { - * databaseDedupeKey: "foo", - * }) - * t.is(connection1.database, connection2.database) - * }) - * ``` - */ /** * By default, `ava-postgres` will create a new database for each test. If you want to share a database between tests, you can use the `databaseDedupeKey` option. * This works across the entire test suite. diff --git a/src/tests/cleanup/does-database-exist.ts b/src/tests/cleanup/does-database-exist.ts deleted file mode 100644 index c21dc81..0000000 --- a/src/tests/cleanup/does-database-exist.ts +++ /dev/null @@ -1,35 +0,0 @@ -import test from "ava" -import pRetry from "p-retry" -import { getTestPostgresDatabaseFactory } from "~/index" - -const getTestServer = getTestPostgresDatabaseFactory({ - postgresVersion: process.env.POSTGRES_VERSION, -}) - -const NUM_OF_DEFAULT_POSTGRES_DATABASES = 1 - -test("database that first test worker created should have been dropped", async (t) => { - const { pool } = await getTestServer(t) - - await pRetry( - async () => { - const { - rows: [{ count }], - } = await pool.query( - 'SELECT COUNT(*) FROM "pg_database" WHERE "datistemplate" = false' - ) - - // (Add one since we create a database in this test) - if (Number(count) !== NUM_OF_DEFAULT_POSTGRES_DATABASES + 1) { - throw new Error("Database was not dropped") - } - }, - { - minTimeout: 100, - factor: 1, - maxRetryTime: 30_000, - } - ) - - t.pass() -}) diff --git a/src/tests/hooks.test.ts b/src/tests/hooks.test.ts index f05682f..6a034f1 100644 --- a/src/tests/hooks.test.ts +++ b/src/tests/hooks.test.ts @@ -147,10 +147,10 @@ test("beforeTemplateIsBaked (result isn't serializable)", async (t) => { ) }) -test("beforeTemplateIsBaked, get nested database", async (t) => { +test("beforeTemplateIsBaked with manual template build", async (t) => { const getTestDatabase = getTestPostgresDatabaseFactory({ postgresVersion: process.env.POSTGRES_VERSION, - workerDedupeKey: "beforeTemplateIsBakedHookNestedDatabase", + workerDedupeKey: "beforeTemplateIsBakedHookManualTemplateBuild", beforeTemplateIsBaked: async ({ connection: { pool }, manuallyBuildAdditionalTemplate, @@ -178,7 +178,7 @@ test("beforeTemplateIsBaked, get nested database", async (t) => { await t.notThrowsAsync(async () => { await fooDatabase.pool.query('SELECT * FROM "foo"') - }) + }, "foo table should exist on database manually created from template") await t.throwsAsync(async () => { await fooDatabase.pool.query('SELECT * FROM "bar"')