@prairielearn/postgres 4.4.3 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @prairielearn/postgres
2
2
 
3
+ ## 4.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3a09ac8: Update function name for test utils
8
+ - 3a09ac8: Return user/database/host from `createDatabase` in test utils
9
+
3
10
  ## 4.4.3
4
11
 
5
12
  ### Patch Changes
@@ -20,10 +20,10 @@ interface DropDatabaseOptions {
20
20
  closePool?: boolean;
21
21
  }
22
22
  export interface PostgresTestUtils {
23
- createDatabase: (options?: CreateDatabaseOptions) => Promise<void>;
23
+ createDatabase: (options?: CreateDatabaseOptions) => Promise<pg.PoolConfig>;
24
24
  resetDatabase: () => Promise<void>;
25
25
  dropDatabase: (options?: DropDatabaseOptions) => Promise<void>;
26
- getDatabaseNameForCurrentMochaWorker: () => string;
26
+ getDatabaseNameForCurrentTestWorker: () => string;
27
27
  getPoolConfig: () => pg.PoolConfig;
28
28
  }
29
29
  export declare function makePostgresTestUtils(options: PostgresTestUtilsOptions): PostgresTestUtils;
@@ -1 +1 @@
1
- {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAQpB,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,GAAG,mBAAmB,CAAC,CAAC;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAED,UAAU,qBAAqB;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAgHD,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,oCAAoC,EAAE,MAAM,MAAM,CAAC;IACnD,aAAa,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC;CACpC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAU1F"}
1
+ {"version":3,"file":"test-utils.d.ts","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAQpB,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,KAAK,GAAG,mBAAmB,CAAC,CAAC;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAID,UAAU,qBAAqB;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAmHD,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,qBAAqB,KAAK,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;IAC5E,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,mCAAmC,EAAE,MAAM,MAAM,CAAC;IAClD,aAAa,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC;CACpC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAU1F"}
@@ -9,7 +9,7 @@ async function createDatabase(options, { dropExistingDatabase = true, configureP
9
9
  database: options.defaultDatabase ?? POSTGRES_DATABASE,
10
10
  });
11
11
  await client.connect();
12
- const escapedDatabase = client.escapeIdentifier(database ?? getDatabaseNameForCurrentMochaWorker(options.database));
12
+ const escapedDatabase = client.escapeIdentifier(database ?? getDatabaseNameForCurrentTestWorker(options.database));
13
13
  if (dropExistingDatabase ?? true) {
14
14
  await client.query(`DROP DATABASE IF EXISTS ${escapedDatabase}`);
15
15
  }
@@ -22,11 +22,10 @@ async function createDatabase(options, { dropExistingDatabase = true, configureP
22
22
  }
23
23
  await client.end();
24
24
  await prepare?.(client);
25
+ const poolConfig = getPoolConfig(options);
25
26
  if (configurePool) {
26
27
  await defaultPool.initAsync({
27
- user: options.user ?? POSTGRES_USER,
28
- host: options.host ?? POSTGRES_HOST,
29
- database: getDatabaseNameForCurrentMochaWorker(options.database),
28
+ ...poolConfig,
30
29
  // Offer sensible default, but these can be overridden by `options.poolConfig`.
31
30
  max: 10,
32
31
  idleTimeoutMillis: 30000,
@@ -36,6 +35,7 @@ async function createDatabase(options, { dropExistingDatabase = true, configureP
36
35
  throw err;
37
36
  });
38
37
  }
38
+ return poolConfig;
39
39
  }
40
40
  async function resetDatabase(options) {
41
41
  const client = new pg.Client(getPoolConfig(options));
@@ -60,7 +60,7 @@ async function dropDatabase(options, { closePool = true, force = false, database
60
60
  if (closePool) {
61
61
  await defaultPool.closeAsync();
62
62
  }
63
- const databaseName = database ?? getDatabaseNameForCurrentMochaWorker(options.database);
63
+ const databaseName = database ?? getDatabaseNameForCurrentTestWorker(options.database);
64
64
  if ('PL_KEEP_TEST_DB' in process.env && !force) {
65
65
  // eslint-disable-next-line no-console
66
66
  console.log(`PL_KEEP_TEST_DB environment variable set, not dropping database ${databaseName}`);
@@ -74,15 +74,16 @@ async function dropDatabase(options, { closePool = true, force = false, database
74
74
  await client.query(`DROP DATABASE IF EXISTS ${client.escapeIdentifier(databaseName)}`);
75
75
  await client.end();
76
76
  }
77
- function getDatabaseNameForCurrentMochaWorker(namespace) {
78
- const workerId = process.env.MOCHA_WORKER_ID ?? process.env.VITEST_POOL_ID ?? '1';
77
+ function getDatabaseNameForCurrentTestWorker(namespace) {
78
+ // https://playwright.dev/docs/test-parallel#isolate-test-data-between-parallel-workers
79
+ const workerId = process.env.TEST_WORKER_INDEX ?? process.env.VITEST_POOL_ID ?? '1';
79
80
  return `${namespace}_${workerId}`;
80
81
  }
81
82
  function getPoolConfig(options) {
82
83
  return {
83
84
  user: options.user ?? POSTGRES_USER,
84
85
  host: options.host ?? POSTGRES_HOST,
85
- database: getDatabaseNameForCurrentMochaWorker(options.database),
86
+ database: getDatabaseNameForCurrentTestWorker(options.database),
86
87
  };
87
88
  }
88
89
  export function makePostgresTestUtils(options) {
@@ -90,7 +91,7 @@ export function makePostgresTestUtils(options) {
90
91
  createDatabase: (createOptions) => createDatabase(options, createOptions),
91
92
  resetDatabase: () => resetDatabase(options),
92
93
  dropDatabase: (dropOptions) => dropDatabase(options, dropOptions),
93
- getDatabaseNameForCurrentMochaWorker: () => getDatabaseNameForCurrentMochaWorker(options.database),
94
+ getDatabaseNameForCurrentTestWorker: () => getDatabaseNameForCurrentTestWorker(options.database),
94
95
  getPoolConfig: () => getPoolConfig(options),
95
96
  };
96
97
  }
@@ -1 +1 @@
1
- {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import pg from 'pg';\n\nimport * as defaultPool from './default-pool.js';\n\nconst POSTGRES_USER = 'postgres';\nconst POSTGRES_HOST = 'localhost';\nconst POSTGRES_DATABASE = 'postgres';\n\nexport interface PostgresTestUtilsOptions {\n database: string;\n user?: string;\n host?: string;\n poolConfig?: Pick<pg.PoolConfig, 'max' | 'idleTimeoutMillis'>;\n defaultDatabase?: string;\n prepareAfterReset?: (client: pg.Client) => Promise<void>;\n}\n\ninterface CreateDatabaseOptions {\n dropExistingDatabase?: boolean;\n database?: string;\n templateDatabase?: string;\n configurePool?: boolean;\n prepare?: (client: pg.Client) => Promise<void>;\n}\n\ninterface DropDatabaseOptions {\n database?: string;\n force?: boolean;\n closePool?: boolean;\n}\n\nasync function createDatabase(\n options: PostgresTestUtilsOptions,\n {\n dropExistingDatabase = true,\n configurePool = true,\n database,\n templateDatabase,\n prepare,\n }: CreateDatabaseOptions = {},\n): Promise<void> {\n const client = new pg.Client({\n ...getPoolConfig(options),\n database: options.defaultDatabase ?? POSTGRES_DATABASE,\n });\n await client.connect();\n\n const escapedDatabase = client.escapeIdentifier(\n database ?? getDatabaseNameForCurrentMochaWorker(options.database),\n );\n if (dropExistingDatabase ?? true) {\n await client.query(`DROP DATABASE IF EXISTS ${escapedDatabase}`);\n }\n\n if (templateDatabase) {\n const escapedTemplateDatabase = client.escapeIdentifier(templateDatabase);\n await client.query(`CREATE DATABASE ${escapedDatabase} TEMPLATE ${escapedTemplateDatabase}`);\n } else {\n await client.query(`CREATE DATABASE ${escapedDatabase}`);\n }\n\n await client.end();\n\n await prepare?.(client);\n\n if (configurePool) {\n await defaultPool.initAsync(\n {\n user: options.user ?? POSTGRES_USER,\n host: options.host ?? POSTGRES_HOST,\n database: getDatabaseNameForCurrentMochaWorker(options.database),\n // Offer sensible default, but these can be overridden by `options.poolConfig`.\n max: 10,\n idleTimeoutMillis: 30000,\n errorOnUnusedParameters: true,\n ...options.poolConfig,\n },\n (err) => {\n throw err;\n },\n );\n }\n}\n\nasync function resetDatabase(options: PostgresTestUtilsOptions): Promise<void> {\n const client = new pg.Client(getPoolConfig(options));\n await client.connect();\n await client.query(`\n DO\n $func$\n BEGIN\n EXECUTE (\n SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' RESTART IDENTITY CASCADE'\n FROM pg_class\n WHERE relkind = 'r'\n AND relnamespace = 'public'::regnamespace\n );\n END\n $func$;\n `);\n await options.prepareAfterReset?.(client);\n await client.end();\n}\n\nasync function dropDatabase(\n options: PostgresTestUtilsOptions,\n { closePool = true, force = false, database }: DropDatabaseOptions = {},\n): Promise<void> {\n if (closePool) {\n await defaultPool.closeAsync();\n }\n\n const databaseName = database ?? getDatabaseNameForCurrentMochaWorker(options.database);\n if ('PL_KEEP_TEST_DB' in process.env && !force) {\n // eslint-disable-next-line no-console\n console.log(`PL_KEEP_TEST_DB environment variable set, not dropping database ${databaseName}`);\n return;\n }\n\n const client = new pg.Client({\n ...getPoolConfig(options),\n database: options.defaultDatabase ?? POSTGRES_DATABASE,\n });\n await client.connect();\n await client.query(`DROP DATABASE IF EXISTS ${client.escapeIdentifier(databaseName)}`);\n await client.end();\n}\n\nfunction getDatabaseNameForCurrentMochaWorker(namespace: string): string {\n const workerId = process.env.MOCHA_WORKER_ID ?? process.env.VITEST_POOL_ID ?? '1';\n return `${namespace}_${workerId}`;\n}\n\nfunction getPoolConfig(options: PostgresTestUtilsOptions): pg.PoolConfig {\n return {\n user: options.user ?? POSTGRES_USER,\n host: options.host ?? POSTGRES_HOST,\n database: getDatabaseNameForCurrentMochaWorker(options.database),\n };\n}\n\nexport interface PostgresTestUtils {\n createDatabase: (options?: CreateDatabaseOptions) => Promise<void>;\n resetDatabase: () => Promise<void>;\n dropDatabase: (options?: DropDatabaseOptions) => Promise<void>;\n getDatabaseNameForCurrentMochaWorker: () => string;\n getPoolConfig: () => pg.PoolConfig;\n}\n\nexport function makePostgresTestUtils(options: PostgresTestUtilsOptions): PostgresTestUtils {\n return {\n createDatabase: (createOptions?: CreateDatabaseOptions) =>\n createDatabase(options, createOptions),\n resetDatabase: () => resetDatabase(options),\n dropDatabase: (dropOptions?: DropDatabaseOptions) => dropDatabase(options, dropOptions),\n getDatabaseNameForCurrentMochaWorker: () =>\n getDatabaseNameForCurrentMochaWorker(options.database),\n getPoolConfig: () => getPoolConfig(options),\n };\n}\n"]}
1
+ {"version":3,"file":"test-utils.js","sourceRoot":"","sources":["../src/test-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import pg from 'pg';\n\nimport * as defaultPool from './default-pool.js';\n\nconst POSTGRES_USER = 'postgres';\nconst POSTGRES_HOST = 'localhost';\nconst POSTGRES_DATABASE = 'postgres';\n\nexport interface PostgresTestUtilsOptions {\n database: string;\n user?: string;\n host?: string;\n poolConfig?: Pick<pg.PoolConfig, 'max' | 'idleTimeoutMillis'>;\n defaultDatabase?: string;\n prepareAfterReset?: (client: pg.Client) => Promise<void>;\n}\n\ntype PostgresTestPoolConfig = Required<Pick<pg.PoolConfig, 'user' | 'host' | 'database'>>;\n\ninterface CreateDatabaseOptions {\n dropExistingDatabase?: boolean;\n database?: string;\n templateDatabase?: string;\n configurePool?: boolean;\n prepare?: (client: pg.Client) => Promise<void>;\n}\n\ninterface DropDatabaseOptions {\n database?: string;\n force?: boolean;\n closePool?: boolean;\n}\n\nasync function createDatabase(\n options: PostgresTestUtilsOptions,\n {\n dropExistingDatabase = true,\n configurePool = true,\n database,\n templateDatabase,\n prepare,\n }: CreateDatabaseOptions = {},\n): Promise<PostgresTestPoolConfig> {\n const client = new pg.Client({\n ...getPoolConfig(options),\n database: options.defaultDatabase ?? POSTGRES_DATABASE,\n });\n await client.connect();\n\n const escapedDatabase = client.escapeIdentifier(\n database ?? getDatabaseNameForCurrentTestWorker(options.database),\n );\n if (dropExistingDatabase ?? true) {\n await client.query(`DROP DATABASE IF EXISTS ${escapedDatabase}`);\n }\n\n if (templateDatabase) {\n const escapedTemplateDatabase = client.escapeIdentifier(templateDatabase);\n await client.query(`CREATE DATABASE ${escapedDatabase} TEMPLATE ${escapedTemplateDatabase}`);\n } else {\n await client.query(`CREATE DATABASE ${escapedDatabase}`);\n }\n\n await client.end();\n\n await prepare?.(client);\n\n const poolConfig = getPoolConfig(options);\n\n if (configurePool) {\n await defaultPool.initAsync(\n {\n ...poolConfig,\n // Offer sensible default, but these can be overridden by `options.poolConfig`.\n max: 10,\n idleTimeoutMillis: 30000,\n errorOnUnusedParameters: true,\n ...options.poolConfig,\n },\n (err) => {\n throw err;\n },\n );\n }\n\n return poolConfig;\n}\n\nasync function resetDatabase(options: PostgresTestUtilsOptions): Promise<void> {\n const client = new pg.Client(getPoolConfig(options));\n await client.connect();\n await client.query(`\n DO\n $func$\n BEGIN\n EXECUTE (\n SELECT 'TRUNCATE TABLE ' || string_agg(oid::regclass::text, ', ') || ' RESTART IDENTITY CASCADE'\n FROM pg_class\n WHERE relkind = 'r'\n AND relnamespace = 'public'::regnamespace\n );\n END\n $func$;\n `);\n await options.prepareAfterReset?.(client);\n await client.end();\n}\n\nasync function dropDatabase(\n options: PostgresTestUtilsOptions,\n { closePool = true, force = false, database }: DropDatabaseOptions = {},\n): Promise<void> {\n if (closePool) {\n await defaultPool.closeAsync();\n }\n\n const databaseName = database ?? getDatabaseNameForCurrentTestWorker(options.database);\n if ('PL_KEEP_TEST_DB' in process.env && !force) {\n // eslint-disable-next-line no-console\n console.log(`PL_KEEP_TEST_DB environment variable set, not dropping database ${databaseName}`);\n return;\n }\n\n const client = new pg.Client({\n ...getPoolConfig(options),\n database: options.defaultDatabase ?? POSTGRES_DATABASE,\n });\n await client.connect();\n await client.query(`DROP DATABASE IF EXISTS ${client.escapeIdentifier(databaseName)}`);\n await client.end();\n}\n\nfunction getDatabaseNameForCurrentTestWorker(namespace: string): string {\n // https://playwright.dev/docs/test-parallel#isolate-test-data-between-parallel-workers\n const workerId = process.env.TEST_WORKER_INDEX ?? process.env.VITEST_POOL_ID ?? '1';\n return `${namespace}_${workerId}`;\n}\n\nfunction getPoolConfig(options: PostgresTestUtilsOptions): PostgresTestPoolConfig {\n return {\n user: options.user ?? POSTGRES_USER,\n host: options.host ?? POSTGRES_HOST,\n database: getDatabaseNameForCurrentTestWorker(options.database),\n };\n}\n\nexport interface PostgresTestUtils {\n createDatabase: (options?: CreateDatabaseOptions) => Promise<pg.PoolConfig>;\n resetDatabase: () => Promise<void>;\n dropDatabase: (options?: DropDatabaseOptions) => Promise<void>;\n getDatabaseNameForCurrentTestWorker: () => string;\n getPoolConfig: () => pg.PoolConfig;\n}\n\nexport function makePostgresTestUtils(options: PostgresTestUtilsOptions): PostgresTestUtils {\n return {\n createDatabase: (createOptions?: CreateDatabaseOptions) =>\n createDatabase(options, createOptions),\n resetDatabase: () => resetDatabase(options),\n dropDatabase: (dropOptions?: DropDatabaseOptions) => dropDatabase(options, dropOptions),\n getDatabaseNameForCurrentTestWorker: () =>\n getDatabaseNameForCurrentTestWorker(options.database),\n getPoolConfig: () => getPoolConfig(options),\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prairielearn/postgres",
3
- "version": "4.4.3",
3
+ "version": "4.5.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,10 +27,10 @@
27
27
  "devDependencies": {
28
28
  "@prairielearn/tsconfig": "^0.0.0",
29
29
  "@types/multipipe": "^3.0.5",
30
- "@types/node": "^22.18.13",
31
- "@vitest/coverage-v8": "^4.0.6",
30
+ "@types/node": "^22.19.0",
31
+ "@vitest/coverage-v8": "^4.0.7",
32
32
  "tsx": "^4.20.6",
33
33
  "typescript": "^5.9.3",
34
- "vitest": "^4.0.6"
34
+ "vitest": "^4.0.7"
35
35
  }
36
36
  }
package/src/test-utils.ts CHANGED
@@ -15,6 +15,8 @@ export interface PostgresTestUtilsOptions {
15
15
  prepareAfterReset?: (client: pg.Client) => Promise<void>;
16
16
  }
17
17
 
18
+ type PostgresTestPoolConfig = Required<Pick<pg.PoolConfig, 'user' | 'host' | 'database'>>;
19
+
18
20
  interface CreateDatabaseOptions {
19
21
  dropExistingDatabase?: boolean;
20
22
  database?: string;
@@ -38,7 +40,7 @@ async function createDatabase(
38
40
  templateDatabase,
39
41
  prepare,
40
42
  }: CreateDatabaseOptions = {},
41
- ): Promise<void> {
43
+ ): Promise<PostgresTestPoolConfig> {
42
44
  const client = new pg.Client({
43
45
  ...getPoolConfig(options),
44
46
  database: options.defaultDatabase ?? POSTGRES_DATABASE,
@@ -46,7 +48,7 @@ async function createDatabase(
46
48
  await client.connect();
47
49
 
48
50
  const escapedDatabase = client.escapeIdentifier(
49
- database ?? getDatabaseNameForCurrentMochaWorker(options.database),
51
+ database ?? getDatabaseNameForCurrentTestWorker(options.database),
50
52
  );
51
53
  if (dropExistingDatabase ?? true) {
52
54
  await client.query(`DROP DATABASE IF EXISTS ${escapedDatabase}`);
@@ -63,12 +65,12 @@ async function createDatabase(
63
65
 
64
66
  await prepare?.(client);
65
67
 
68
+ const poolConfig = getPoolConfig(options);
69
+
66
70
  if (configurePool) {
67
71
  await defaultPool.initAsync(
68
72
  {
69
- user: options.user ?? POSTGRES_USER,
70
- host: options.host ?? POSTGRES_HOST,
71
- database: getDatabaseNameForCurrentMochaWorker(options.database),
73
+ ...poolConfig,
72
74
  // Offer sensible default, but these can be overridden by `options.poolConfig`.
73
75
  max: 10,
74
76
  idleTimeoutMillis: 30000,
@@ -80,6 +82,8 @@ async function createDatabase(
80
82
  },
81
83
  );
82
84
  }
85
+
86
+ return poolConfig;
83
87
  }
84
88
 
85
89
  async function resetDatabase(options: PostgresTestUtilsOptions): Promise<void> {
@@ -110,7 +114,7 @@ async function dropDatabase(
110
114
  await defaultPool.closeAsync();
111
115
  }
112
116
 
113
- const databaseName = database ?? getDatabaseNameForCurrentMochaWorker(options.database);
117
+ const databaseName = database ?? getDatabaseNameForCurrentTestWorker(options.database);
114
118
  if ('PL_KEEP_TEST_DB' in process.env && !force) {
115
119
  // eslint-disable-next-line no-console
116
120
  console.log(`PL_KEEP_TEST_DB environment variable set, not dropping database ${databaseName}`);
@@ -126,24 +130,25 @@ async function dropDatabase(
126
130
  await client.end();
127
131
  }
128
132
 
129
- function getDatabaseNameForCurrentMochaWorker(namespace: string): string {
130
- const workerId = process.env.MOCHA_WORKER_ID ?? process.env.VITEST_POOL_ID ?? '1';
133
+ function getDatabaseNameForCurrentTestWorker(namespace: string): string {
134
+ // https://playwright.dev/docs/test-parallel#isolate-test-data-between-parallel-workers
135
+ const workerId = process.env.TEST_WORKER_INDEX ?? process.env.VITEST_POOL_ID ?? '1';
131
136
  return `${namespace}_${workerId}`;
132
137
  }
133
138
 
134
- function getPoolConfig(options: PostgresTestUtilsOptions): pg.PoolConfig {
139
+ function getPoolConfig(options: PostgresTestUtilsOptions): PostgresTestPoolConfig {
135
140
  return {
136
141
  user: options.user ?? POSTGRES_USER,
137
142
  host: options.host ?? POSTGRES_HOST,
138
- database: getDatabaseNameForCurrentMochaWorker(options.database),
143
+ database: getDatabaseNameForCurrentTestWorker(options.database),
139
144
  };
140
145
  }
141
146
 
142
147
  export interface PostgresTestUtils {
143
- createDatabase: (options?: CreateDatabaseOptions) => Promise<void>;
148
+ createDatabase: (options?: CreateDatabaseOptions) => Promise<pg.PoolConfig>;
144
149
  resetDatabase: () => Promise<void>;
145
150
  dropDatabase: (options?: DropDatabaseOptions) => Promise<void>;
146
- getDatabaseNameForCurrentMochaWorker: () => string;
151
+ getDatabaseNameForCurrentTestWorker: () => string;
147
152
  getPoolConfig: () => pg.PoolConfig;
148
153
  }
149
154
 
@@ -153,8 +158,8 @@ export function makePostgresTestUtils(options: PostgresTestUtilsOptions): Postgr
153
158
  createDatabase(options, createOptions),
154
159
  resetDatabase: () => resetDatabase(options),
155
160
  dropDatabase: (dropOptions?: DropDatabaseOptions) => dropDatabase(options, dropOptions),
156
- getDatabaseNameForCurrentMochaWorker: () =>
157
- getDatabaseNameForCurrentMochaWorker(options.database),
161
+ getDatabaseNameForCurrentTestWorker: () =>
162
+ getDatabaseNameForCurrentTestWorker(options.database),
158
163
  getPoolConfig: () => getPoolConfig(options),
159
164
  };
160
165
  }