pgsql-test 0.0.1 → 2.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Dan Lynch <pyramation@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,61 +1,257 @@
1
1
  # pgsql-test
2
2
 
3
- <p align="center">
4
- <img src="https://user-images.githubusercontent.com/545047/188804067-28e67e5e-0214-4449-ab04-2e0c564a6885.svg" width="80"><br />
5
- PostgreSQL Testing in TypeScript
3
+ <p align="center" width="100%">
4
+ <img height="250" src="https://github.com/user-attachments/assets/d0456af5-b6e9-422e-a45d-2574d5be490f" />
6
5
  </p>
7
6
 
8
- ## install
7
+ <p align="center" width="100%">
8
+ <a href="https://github.com/launchql/launchql-2.0/actions/workflows/run-tests.yaml">
9
+ <img height="20" src="https://github.com/launchql/launchql-2.0/actions/workflows/run-tests.yaml/badge.svg" />
10
+ </a>
11
+ <a href="https://github.com/launchql/launchql-2.0/blob/main/LICENSE-MIT">
12
+ <img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/>
13
+ </a>
14
+ <a href="https://www.npmjs.com/package/pgsql-test">
15
+ <img height="20" src="https://img.shields.io/github/package-json/v/launchql/launchql-2.0?filename=packages%2Fpgsql-test%2Fpackage.json"/>
16
+ </a>
17
+ </p>
18
+
19
+
20
+ `pgsql-test` provides an isolated PostgreSQL testing environment with per-test transaction rollback, ideal for integration tests involving SQL, roles, simulations, and complex migrations. With automatic rollbacks and isolated contexts, it eliminates test interference while delivering tight feedback loops for happier developers. We made database testing simple so you can focus on writing good tests instead of fighting your environment.
21
+
22
+ ## Install
9
23
 
10
24
  ```sh
11
25
  npm install pgsql-test
12
26
  ```
13
- ## Table of contents
14
27
 
15
- - [pgsql-test](#pgsql-test)
16
- - [Install](#install)
17
- - [Table of contents](#table-of-contents)
18
- - [Developing](#developing)
19
- - [Credits](#credits)
28
+ ## Features
20
29
 
21
- ## Developing
30
+ * ⚡ Quick-start setup with `getConnections()`
31
+ * 🧹 Easy teardown and cleanup
32
+ * 🔄 Per-test isolation using transactions and savepoints
33
+ * 🛡️ Role-based context for RLS testing
34
+ * 🌱 Flexible seed support via `.sql` files and programmatic functions
35
+ * 🧪 Auto-generated test databases with `UUID` suffix
36
+ * 📦 Built for tools like `sqitch`, supporting full schema initialization workflows
37
+ * 🧰 Designed for `Jest`, `Mocha`, or any async test runner
22
38
 
23
- When first cloning the repo:
24
39
 
25
- ```sh
26
- yarn
27
- # build the prod packages. When devs would like to navigate to the source code, this will only navigate from references to their definitions (.d.ts files) between packages.
28
- yarn build
40
+ ## Table of Contents
41
+
42
+ 1. [Install](#install)
43
+ 2. [Features](#features)
44
+ 3. [Quick Start](#quick-start)
45
+ 4. [getConnections() Overview](#getconnections-overview)
46
+ 5. [PgTestClient API Overview](#pgtestclient-api-overview)
47
+ 6. [Usage Examples](#usage-examples)
48
+ * [Basic Setup](#basic-setup)
49
+ * [Role-Based Context](#role-based-context)
50
+ * [SQL File Seeding](#sql-file-seeding)
51
+ * [Programmatic Seeding](#programmatic-seeding)
52
+ * [Composed Seeding](#composed-seeding)
53
+ 7. [Environment Overrides](#environment-overrides)
54
+ 8. [Disclaimer](#disclaimer)
55
+
56
+
57
+ ## ✨ Quick Start
58
+
59
+ ```ts
60
+ import { getConnections } from 'pgsql-test';
61
+
62
+ let db, teardown;
63
+
64
+ beforeAll(async () => {
65
+ ({ db, teardown } = await getConnections());
66
+ await db.query(`SELECT 1`); // ✅ Ready to run queries
67
+ });
68
+
69
+ afterAll(() => teardown());
29
70
  ```
30
71
 
31
- Or if you want to make your dev process smoother, you can run:
72
+ ## `getConnections()` Overview
32
73
 
33
- ```sh
34
- yarn
35
- # build the dev packages with .map files, this enables navigation from references to their source code between packages.
36
- yarn build:dev
74
+ The `getConnections()` helper sets up a fresh PostgreSQL test database and returns a structured object with:
75
+
76
+ * `pg`: a `PgTestClient` connected as the root or superuser useful for administrative setup or introspection
77
+ * `db`: a `PgTestClient` connected as the app-level user — used for running tests with RLS and granted permissions
78
+ * `admin`: a `DbAdmin` utility for managing database state, extensions, roles, and templates
79
+ * `teardown()`: a function that shuts down the test environment and database pool
80
+ * `manager`: a shared connection pool manager (`PgTestConnector`) behind both clients
81
+
82
+ Together, these allow fast, isolated, role-aware test environments with per-test rollback and full control over setup and teardown.
83
+
84
+ The `PgTestClient` returned by `getConnections()` is a fully-featured wrapper around `pg.Pool`. It provides:
85
+
86
+ * Automatic transaction and savepoint management for test isolation
87
+ * Easy switching of role-based contexts for RLS testing
88
+ * A clean, high-level API for integration testing PostgreSQL systems
89
+
90
+ ## `PgTestClient` API Overview
91
+
92
+ The `PgTestClient` returned by `getConnections()` wraps a `pg.Client` and provides convenient helpers for query execution, test isolation, and context switching.
93
+
94
+ ### Common Methods
95
+
96
+ * `query(sql, values?)` – Run a raw SQL query and get the `QueryResult`
97
+ * `beforeEach()` – Begins a transaction and sets a savepoint (called at the start of each test)
98
+ * `afterEach()` – Rolls back to the savepoint and commits the outer transaction (cleans up test state)
99
+ * `setContext({ key: value })` – Sets PostgreSQL config variables (like `role`) to simulate RLS contexts
100
+ * `any`, `one`, `oneOrNone`, `many`, `manyOrNone`, `none`, `result` – Typed query helpers for specific result expectations
101
+
102
+ These methods make it easier to build expressive and isolated integration tests with strong typing and error handling.
103
+
104
+ The `PgTestClient` returned by `getConnections()` is a fully-featured wrapper around `pg.Pool`. It provides:
105
+
106
+ * Automatic transaction and savepoint management for test isolation
107
+ * Easy switching of role-based contexts for RLS testing
108
+ * A clean, high-level API for integration testing PostgreSQL systems
109
+
110
+ ## Usage Examples
111
+
112
+ ### ⚡ Basic Setup
113
+
114
+ ```ts
115
+ import { getConnections } from 'pgsql-test';
116
+
117
+ let db; // A fully wrapped PgTestClient using pg.Pool with savepoint-based rollback per test
118
+ let teardown;
119
+
120
+ beforeAll(async () => {
121
+ ({ db, teardown } = await getConnections());
122
+
123
+ await db.query(`
124
+ CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT);
125
+ CREATE TABLE posts (id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id), content TEXT);
126
+
127
+ INSERT INTO users (name) VALUES ('Alice'), ('Bob');
128
+ INSERT INTO posts (user_id, content) VALUES (1, 'Hello world!'), (2, 'Graphile is cool!');
129
+ `);
130
+ });
131
+
132
+ afterAll(() => teardown());
133
+
134
+ beforeEach(() => db.beforeEach());
135
+ afterEach(() => db.afterEach());
136
+
137
+ test('user count starts at 2', async () => {
138
+ const res = await db.query('SELECT COUNT(*) FROM users');
139
+ expect(res.rows[0].count).toBe('2');
140
+ });
141
+ ```
142
+
143
+ ### 🔐 Role-Based Context
144
+
145
+ ```ts
146
+ describe('authenticated role', () => {
147
+ beforeEach(async () => {
148
+ db.setContext({ role: 'authenticated' });
149
+ await db.beforeEach();
150
+ });
151
+
152
+ afterEach(() => db.afterEach());
153
+
154
+ it('runs as authenticated', async () => {
155
+ const res = await db.query(`SELECT current_setting('role', true) AS role`);
156
+ expect(res.rows[0].role).toBe('authenticated');
157
+ });
158
+ });
159
+ ```
160
+
161
+ ### 🔌 SQL File Seeding
162
+
163
+ Use `.sql` files to set up your database state before tests:
164
+
165
+ ```ts
166
+ import path from 'path';
167
+ import { getConnections, seed } from 'pgsql-test';
168
+
169
+ const sql = (f: string) => path.join(__dirname, 'sql', f);
170
+
171
+ let db;
172
+ let teardown;
173
+
174
+ beforeAll(async () => {
175
+ ({ db, teardown } = await getConnections({}, seed.sqlfile([
176
+ sql('schema.sql'),
177
+ sql('fixtures.sql')
178
+ ])));
179
+ });
180
+
181
+ afterAll(async () => {
182
+ await teardown();
183
+ });
184
+ ```
185
+
186
+ ### 🧠 Programmatic Seeding
187
+
188
+ Use JavaScript functions to insert seed data:
189
+
190
+ ```ts
191
+ import { getConnections, seed } from 'pgsql-test';
192
+
193
+ let db;
194
+ let teardown;
195
+
196
+ beforeAll(async () => {
197
+ ({ db, teardown } = await getConnections({}, seed.fn(async ({ pg }) => {
198
+ await pg.query(`
199
+ INSERT INTO users (name) VALUES ('Seeded User');
200
+ `);
201
+ })));
202
+ });
203
+ ```
204
+
205
+ ### 🧬 Composed Seeding
206
+
207
+ Combine multiple seeders with `seed.compose()`:
208
+
209
+ ```ts
210
+ import path from 'path';
211
+ import { getConnections, seed } from 'pgsql-test';
212
+
213
+ const sql = (f: string) => path.join(__dirname, 'sql', f);
214
+
215
+ let db;
216
+ let teardown;
217
+
218
+ beforeAll(async () => {
219
+ ({ db, teardown } = await getConnections({}, seed.compose([
220
+ seed.sqlfile([
221
+ sql('schema.sql'),
222
+ sql('roles.sql')
223
+ ]),
224
+ seed.fn(async ({ pg }) => {
225
+ await pg.query(`INSERT INTO users (name) VALUES ('Composed');`);
226
+ })
227
+ ])));
228
+ });
37
229
  ```
38
230
 
39
- ## Interchain JavaScript Stack
231
+ ---
40
232
 
41
- A unified toolkit for building applications and smart contracts in the Interchain ecosystem ⚛️
233
+ These examples show how flexible `pgsql-test` is for composing repeatable and transactional test database environments.
42
234
 
43
- | Category | Tools | Description |
44
- |----------------------|------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
45
- | **Chain Information** | [**Chain Registry**](https://github.com/hyperweb-io/chain-registry), [**Utils**](https://www.npmjs.com/package/@chain-registry/utils), [**Client**](https://www.npmjs.com/package/@chain-registry/client) | Everything from token symbols, logos, and IBC denominations for all assets you want to support in your application. |
46
- | **Wallet Connectors**| [**Interchain Kit**](https://github.com/hyperweb-io/interchain-kit)<sup>beta</sup>, [**Cosmos Kit**](https://github.com/hyperweb.io/cosmos-kit) | Experience the convenience of connecting with a variety of web3 wallets through a single, streamlined interface. |
47
- | **Signing Clients** | [**InterchainJS**](https://github.com/hyperweb-io/interchainjs)<sup>beta</sup>, [**CosmJS**](https://github.com/cosmos/cosmjs) | A single, universal signing interface for any network |
48
- | **SDK Clients** | [**Telescope**](https://github.com/hyperweb.io/telescope) | Your Frontend Companion for Building with TypeScript with Cosmos SDK Modules. |
49
- | **Starter Kits** | [**Create Interchain App**](https://github.com/hyperweb-io/create-interchain-app)<sup>beta</sup>, [**Create Cosmos App**](https://github.com/hyperweb.io/create-cosmos-app) | Set up a modern Interchain app by running one command. |
50
- | **UI Kits** | [**Interchain UI**](https://github.com/hyperweb.io/interchain-ui) | The Interchain Design System, empowering developers with a flexible, easy-to-use UI kit. |
51
- | **Testing Frameworks** | [**Starship**](https://github.com/hyperweb.io/starship) | Unified Testing and Development for the Interchain. |
52
- | **TypeScript Smart Contracts** | [**Create Hyperweb App**](https://github.com/hyperweb-io/create-hyperweb-app) | Build and deploy full-stack blockchain applications with TypeScript |
53
- | **CosmWasm Contracts** | [**CosmWasm TS Codegen**](https://github.com/CosmWasm/ts-codegen) | Convert your CosmWasm smart contracts into dev-friendly TypeScript classes. |
54
235
 
55
- ## Credits
56
236
 
57
- 🛠 Built by Hyperweb (formerly Cosmology) — if you like our tools, please checkout and contribute to [our github ⚛️](https://github.com/hyperweb-io)
237
+ ## Environment Overrides
58
238
 
239
+ `pgsql-test` respects the following env vars for DB connectivity:
240
+
241
+ * `PGHOST`
242
+ * `PGPORT`
243
+ * `PGUSER`
244
+ * `PGPASSWORD`
245
+
246
+ Override them in your test runner or CI config:
247
+
248
+ ```yaml
249
+ env:
250
+ PGHOST: localhost
251
+ PGPORT: 5432
252
+ PGUSER: postgres
253
+ PGPASSWORD: password
254
+ ```
59
255
 
60
256
  ## Disclaimer
61
257
 
package/admin.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ import { PgConfig } from '@launchql/types';
2
+ import { SeedAdapter } from './seed';
3
+ export declare class DbAdmin {
4
+ private config;
5
+ private verbose;
6
+ constructor(config: PgConfig, verbose?: boolean);
7
+ private getEnv;
8
+ private run;
9
+ private safeDropDb;
10
+ drop(dbName?: string): void;
11
+ dropTemplate(dbName: string): void;
12
+ create(dbName?: string): void;
13
+ createFromTemplate(template: string, dbName?: string): void;
14
+ installExtensions(extensions: string[] | string, dbName?: string): void;
15
+ connectionString(dbName?: string): string;
16
+ createTemplateFromBase(base: string, template: string): void;
17
+ cleanupTemplate(template: string): void;
18
+ grantRole(role: string, user: string, dbName?: string): Promise<void>;
19
+ grantConnect(role: string, dbName?: string): Promise<void>;
20
+ createUserRole(user: string, password: string, dbName: string): Promise<void>;
21
+ loadSql(file: string, dbName: string): void;
22
+ streamSql(sql: string, dbName: string): Promise<void>;
23
+ createSeededTemplate(templateName: string, adapter: SeedAdapter): Promise<void>;
24
+ }
package/admin.js ADDED
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DbAdmin = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const types_1 = require("@launchql/types");
6
+ const fs_1 = require("fs");
7
+ const stream_1 = require("./stream");
8
+ class DbAdmin {
9
+ config;
10
+ verbose;
11
+ constructor(config, verbose = false) {
12
+ this.config = config;
13
+ this.verbose = verbose;
14
+ this.config = (0, types_1.getPgEnvOptions)(config);
15
+ }
16
+ getEnv() {
17
+ return {
18
+ PGHOST: this.config.host,
19
+ PGPORT: String(this.config.port),
20
+ PGUSER: this.config.user,
21
+ PGPASSWORD: this.config.password,
22
+ };
23
+ }
24
+ run(command) {
25
+ (0, child_process_1.execSync)(command, {
26
+ stdio: this.verbose ? 'inherit' : 'pipe',
27
+ env: {
28
+ ...process.env,
29
+ ...this.getEnv(),
30
+ },
31
+ });
32
+ }
33
+ safeDropDb(name) {
34
+ try {
35
+ this.run(`dropdb "${name}"`);
36
+ }
37
+ catch (err) {
38
+ const message = err instanceof Error ? err.message : String(err);
39
+ if (!message.includes('does not exist')) {
40
+ console.warn(`⚠️ Could not drop database ${name}: ${message}`);
41
+ }
42
+ }
43
+ }
44
+ drop(dbName) {
45
+ this.safeDropDb(dbName ?? this.config.database);
46
+ }
47
+ dropTemplate(dbName) {
48
+ this.run(`psql -c "UPDATE pg_database SET datistemplate='false' WHERE datname='${dbName}';"`);
49
+ this.drop(dbName);
50
+ }
51
+ create(dbName) {
52
+ const db = dbName ?? this.config.database;
53
+ this.run(`createdb -U ${this.config.user} -h ${this.config.host} -p ${this.config.port} "${db}"`);
54
+ }
55
+ createFromTemplate(template, dbName) {
56
+ const db = dbName ?? this.config.database;
57
+ this.run(`createdb -U ${this.config.user} -h ${this.config.host} -p ${this.config.port} -e "${db}" -T "${template}"`);
58
+ }
59
+ installExtensions(extensions, dbName) {
60
+ const db = dbName ?? this.config.database;
61
+ const extList = typeof extensions === 'string' ? extensions.split(',') : extensions;
62
+ for (const extension of extList) {
63
+ this.run(`psql --dbname "${db}" -c 'CREATE EXTENSION IF NOT EXISTS "${extension}" CASCADE;'`);
64
+ }
65
+ }
66
+ connectionString(dbName) {
67
+ const { user, password, host, port } = this.config;
68
+ const db = dbName ?? this.config.database;
69
+ return `postgres://${user}:${password}@${host}:${port}/${db}`;
70
+ }
71
+ createTemplateFromBase(base, template) {
72
+ this.run(`createdb -T "${base}" "${template}"`);
73
+ this.run(`psql -c "UPDATE pg_database SET datistemplate = true WHERE datname = '${template}';"`);
74
+ }
75
+ cleanupTemplate(template) {
76
+ try {
77
+ this.run(`psql -c "UPDATE pg_database SET datistemplate = false WHERE datname = '${template}'"`);
78
+ }
79
+ catch { }
80
+ this.safeDropDb(template);
81
+ }
82
+ async grantRole(role, user, dbName) {
83
+ const db = dbName ?? this.config.database;
84
+ const sql = `GRANT ${role} TO ${user};`;
85
+ await this.streamSql(sql, db);
86
+ }
87
+ async grantConnect(role, dbName) {
88
+ const db = dbName ?? this.config.database;
89
+ const sql = `GRANT CONNECT ON DATABASE "${db}" TO ${role};`;
90
+ await this.streamSql(sql, db);
91
+ }
92
+ async createUserRole(user, password, dbName) {
93
+ const sql = `
94
+ DO $$
95
+ BEGIN
96
+ IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${user}') THEN
97
+ CREATE ROLE ${user} LOGIN PASSWORD '${password}';
98
+ GRANT anonymous TO ${user};
99
+ GRANT authenticated TO ${user};
100
+ END IF;
101
+ END $$;
102
+ `.trim();
103
+ this.streamSql(sql, dbName);
104
+ }
105
+ loadSql(file, dbName) {
106
+ if (!(0, fs_1.existsSync)(file)) {
107
+ throw new Error(`Missing SQL file: ${file}`);
108
+ }
109
+ this.run(`psql -f ${file} ${dbName}`);
110
+ }
111
+ async streamSql(sql, dbName) {
112
+ await (0, stream_1.streamSql)({
113
+ ...this.config,
114
+ database: dbName
115
+ }, sql);
116
+ }
117
+ async createSeededTemplate(templateName, adapter) {
118
+ const seedDb = this.config.database;
119
+ this.create(seedDb);
120
+ await adapter.seed({
121
+ admin: this,
122
+ config: this.config,
123
+ pg: null // sorry!
124
+ });
125
+ this.cleanupTemplate(templateName);
126
+ this.createTemplateFromBase(seedDb, templateName);
127
+ this.drop(seedDb);
128
+ }
129
+ }
130
+ exports.DbAdmin = DbAdmin;
package/connect.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { DbAdmin } from './admin';
2
+ import { TestConnectionOptions, PgConfig } from '@launchql/types';
3
+ import { PgTestConnector } from './manager';
4
+ import { SeedAdapter } from './seed';
5
+ import { PgTestClient } from './test-client';
6
+ export declare const getPgRootAdmin: (connOpts?: TestConnectionOptions) => DbAdmin;
7
+ export interface GetConnectionOpts {
8
+ pg?: Partial<PgConfig>;
9
+ db?: Partial<TestConnectionOptions>;
10
+ }
11
+ export interface GetConnectionResult {
12
+ pg: PgTestClient;
13
+ db: PgTestClient;
14
+ admin: DbAdmin;
15
+ teardown: () => Promise<void>;
16
+ manager: PgTestConnector;
17
+ }
18
+ export declare const getConnections: (cn?: GetConnectionOpts, seedAdapter?: SeedAdapter) => Promise<GetConnectionResult>;
package/connect.js ADDED
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getConnections = exports.getPgRootAdmin = void 0;
4
+ const admin_1 = require("./admin");
5
+ const types_1 = require("@launchql/types");
6
+ const migrate_1 = require("@launchql/migrate");
7
+ const manager_1 = require("./manager");
8
+ const crypto_1 = require("crypto");
9
+ const server_utils_1 = require("@launchql/server-utils");
10
+ let manager;
11
+ const getPgRootAdmin = (connOpts = {}) => {
12
+ const opts = (0, types_1.getPgEnvOptions)({
13
+ database: connOpts.rootDb
14
+ });
15
+ const admin = new admin_1.DbAdmin(opts);
16
+ return admin;
17
+ };
18
+ exports.getPgRootAdmin = getPgRootAdmin;
19
+ const getConnOopts = (cn = {}) => {
20
+ const connect = (0, types_1.getConnEnvOptions)(cn.db);
21
+ const config = (0, types_1.getPgEnvOptions)({
22
+ database: `${connect.prefix}${(0, crypto_1.randomUUID)()}`,
23
+ ...cn.pg
24
+ });
25
+ return {
26
+ pg: config,
27
+ db: connect
28
+ };
29
+ };
30
+ const getConnections = async (cn = {}, seedAdapter) => {
31
+ cn = getConnOopts(cn);
32
+ const config = cn.pg;
33
+ const connOpts = cn.db;
34
+ const root = (0, exports.getPgRootAdmin)(connOpts);
35
+ await root.createUserRole(connOpts.connection.user, connOpts.connection.password, connOpts.rootDb);
36
+ const admin = new admin_1.DbAdmin(config);
37
+ const proj = new migrate_1.LaunchQLProject(connOpts.cwd);
38
+ if (proj.isInModule()) {
39
+ admin.create(config.database);
40
+ admin.installExtensions(connOpts.extensions);
41
+ const opts = (0, types_1.getEnvOptions)({
42
+ pg: config
43
+ });
44
+ if (connOpts.deployFast) {
45
+ await (0, migrate_1.deployFast)({
46
+ opts,
47
+ name: proj.getModuleName(),
48
+ database: config.database,
49
+ dir: proj.modulePath,
50
+ usePlan: true,
51
+ verbose: false
52
+ });
53
+ }
54
+ else {
55
+ await (0, migrate_1.deploy)(opts, proj.getModuleName(), config.database, proj.modulePath);
56
+ }
57
+ }
58
+ else {
59
+ // Create the test database
60
+ if (process.env.TEST_DB) {
61
+ config.database = process.env.TEST_DB;
62
+ }
63
+ else if (connOpts.template) {
64
+ admin.createFromTemplate(connOpts.template, config.database);
65
+ }
66
+ else {
67
+ admin.create(config.database);
68
+ admin.installExtensions(connOpts.extensions);
69
+ }
70
+ }
71
+ await admin.grantConnect(connOpts.connection.user, config.database);
72
+ // Main admin client (optional unless needed elsewhere)
73
+ manager = manager_1.PgTestConnector.getInstance();
74
+ const pg = manager.getClient(config);
75
+ if (seedAdapter) {
76
+ await seedAdapter.seed({
77
+ admin,
78
+ config: config,
79
+ pg: manager.getClient(config)
80
+ });
81
+ }
82
+ // App user connection
83
+ const db = manager.getClient({
84
+ ...config,
85
+ user: connOpts.connection.user,
86
+ password: connOpts.connection.password
87
+ });
88
+ db.setContext({ role: 'anonymous' });
89
+ const teardown = async () => {
90
+ await (0, server_utils_1.teardownPgPools)();
91
+ await manager.closeAll();
92
+ };
93
+ return { pg, db, teardown, manager, admin };
94
+ };
95
+ exports.getConnections = getConnections;