pglite-test 0.1.1 → 0.2.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/README.md +34 -3
- package/esm/index.js +18 -3
- package/index.d.ts +17 -4
- package/index.js +17 -2
- package/package.json +9 -8
package/README.md
CHANGED
|
@@ -92,10 +92,10 @@ await getConnections(
|
|
|
92
92
|
{
|
|
93
93
|
pglite: {
|
|
94
94
|
dataDir: undefined, // in-memory by default
|
|
95
|
+
roles: true, // seed standard app roles (default) — see below
|
|
95
96
|
extensions: { vector }, // WASM extensions (e.g. pglite-pgvector)
|
|
96
97
|
extensionSql: [ // run once after ready
|
|
97
|
-
'CREATE EXTENSION IF NOT EXISTS vector;'
|
|
98
|
-
'CREATE ROLE authenticated;'
|
|
98
|
+
'CREATE EXTENSION IF NOT EXISTS vector;'
|
|
99
99
|
]
|
|
100
100
|
}
|
|
101
101
|
},
|
|
@@ -103,12 +103,43 @@ await getConnections(
|
|
|
103
103
|
);
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
+
## Roles
|
|
107
|
+
|
|
108
|
+
On a server, `pgsql-test` bootstraps the standard app roles at `createdb`. PGlite
|
|
109
|
+
has no `createdb` — it boots as a lone superuser — so by default `getConnections()`
|
|
110
|
+
creates the same roles for you before seeding: `anonymous`, `authenticated`,
|
|
111
|
+
`administrator` (with `BYPASSRLS`), and `authenticated_client`, using the same
|
|
112
|
+
attributes as the server bootstrap. That's why `db.setContext({ role: 'authenticated' })`
|
|
113
|
+
just works with no manual `CREATE ROLE`. Custom role *names* come from `db.roles`
|
|
114
|
+
(a `RoleMapping`), exactly like `pgsql-test`.
|
|
115
|
+
|
|
116
|
+
### Bring your own roles/users
|
|
117
|
+
|
|
118
|
+
Want a clean superuser-only instance and full control over your own roles? Opt
|
|
119
|
+
out with `pglite: { roles: false }` and create them in `extensionSql` (real
|
|
120
|
+
Postgres DDL — the same statements you'd run on a server):
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
await getConnections({
|
|
124
|
+
pglite: {
|
|
125
|
+
roles: false, // skip the built-in bootstrap
|
|
126
|
+
extensionSql: [
|
|
127
|
+
"CREATE ROLE app_reader NOLOGIN;",
|
|
128
|
+
"CREATE ROLE app_writer NOLOGIN;",
|
|
129
|
+
// a login user, if a test needs one (no password needed in-process):
|
|
130
|
+
"CREATE ROLE app_user LOGIN;",
|
|
131
|
+
"GRANT app_writer TO app_user;"
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
106
137
|
## Single-session model
|
|
107
138
|
|
|
108
139
|
PGlite is one in-process session, so `pg` and `db` share it. That differs from `pgsql-test` (two authenticated connections on a real server):
|
|
109
140
|
|
|
110
141
|
- Transaction control is **ref-counted** (`SharedTxn`) so the standard two-client `beforeEach`/`afterEach` harness emits exactly one `BEGIN`/`SAVEPOINT`/`ROLLBACK`/`COMMIT` per test. The single-client (`db` only) pattern also works.
|
|
111
|
-
- Role-based RLS uses `setContext({ role })` (i.e. `SET LOCAL role`) on the shared session rather than separate authenticated connections.
|
|
142
|
+
- Role-based RLS uses `setContext({ role })` (i.e. `SET LOCAL role`) on the shared session rather than separate authenticated connections. The standard app roles are created for you by default (see [Roles](#roles)); any *extra* role you switch to must be created via `pglite.extensionSql`.
|
|
112
143
|
- `publish()` (commit-and-continue) is not supported under the shared-session coordinator.
|
|
113
144
|
|
|
114
145
|
## Related
|
package/esm/index.js
CHANGED
|
@@ -28,25 +28,40 @@
|
|
|
28
28
|
* afterEach(async () => { await db.afterEach(); await pg.afterEach(); });
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
|
+
import { generateCreateBaseRolesSQL, generateCreateClientRoleSQL } from '@pgpmjs/core';
|
|
31
32
|
import { createPgliteClient, registerPglite } from '@pgpmjs/pglite-adapter';
|
|
32
33
|
import { getPgEnvOptions } from 'pg-env';
|
|
33
|
-
import { getActivePgClientFactory, registerPgClientFactory } from 'pgsql-client';
|
|
34
|
+
import { getActivePgClientFactory, getRoleMapping, registerPgClientFactory } from 'pgsql-client';
|
|
34
35
|
import { seed } from 'pgsql-test';
|
|
35
36
|
import { PgliteTestClient, SharedTxn } from './txn';
|
|
36
37
|
/**
|
|
37
38
|
* Create an isolated PGlite-backed test environment and return `pg`/`db` clients
|
|
38
39
|
* plus a `teardown()`. Defaults to seeding via `seed.pgpm()` (deploys the pgpm
|
|
39
|
-
* module in `cwd`), matching pgsql-test
|
|
40
|
+
* module in `cwd`) and creating the standard app roles, matching pgsql-test — so
|
|
41
|
+
* a bare `getConnections()` is enough to deploy and switch roles with no manual
|
|
42
|
+
* `CREATE ROLE`. Opt out of the role bootstrap with `pglite: { roles: false }`.
|
|
40
43
|
*/
|
|
41
44
|
export const getConnections = async (cn = {}, seedAdapters = [seed.pgpm()]) => {
|
|
42
45
|
// Capture the previously-active client factory so teardown restores it
|
|
43
46
|
// (rather than clobbering to the default), mirroring how registerPglite
|
|
44
47
|
// restores the previous pool factory. Keeps nested/sequential suites clean.
|
|
45
48
|
const previousClientFactory = getActivePgClientFactory();
|
|
49
|
+
// Seed the standard app roles by default so `db.setContext({ role })` works
|
|
50
|
+
// out of the box — PGlite boots as a lone superuser with no app roles, unlike
|
|
51
|
+
// a server where pgsql-test bootstraps them at createdb. Opt out with
|
|
52
|
+
// `pglite: { roles: false }` to manage your own (see README).
|
|
53
|
+
const bootstrapRoles = cn.pglite?.roles !== false;
|
|
54
|
+
const roleMapping = getRoleMapping({ roles: cn.db?.roles });
|
|
55
|
+
const roleSql = bootstrapRoles
|
|
56
|
+
? [
|
|
57
|
+
generateCreateBaseRolesSQL(roleMapping),
|
|
58
|
+
generateCreateClientRoleSQL(roleMapping)
|
|
59
|
+
]
|
|
60
|
+
: [];
|
|
46
61
|
const handle = await registerPglite({
|
|
47
62
|
dataDir: cn.pglite?.dataDir,
|
|
48
63
|
extensions: cn.pglite?.extensions,
|
|
49
|
-
extensionSql: cn.pglite?.extensionSql,
|
|
64
|
+
extensionSql: [...roleSql, ...(cn.pglite?.extensionSql ?? [])],
|
|
50
65
|
instance: cn.pglite?.instance
|
|
51
66
|
});
|
|
52
67
|
// Route pgsql-client's PgClient at the same in-process PGlite session.
|
package/index.d.ts
CHANGED
|
@@ -38,11 +38,22 @@ export interface PgliteConnectionOpts extends GetConnectionOpts {
|
|
|
38
38
|
/** WASM extensions registered at construction, e.g. `{ vector }`. */
|
|
39
39
|
extensions?: Record<string, any>;
|
|
40
40
|
/**
|
|
41
|
-
* SQL run once after the instance is ready —
|
|
42
|
-
* `CREATE
|
|
43
|
-
* superuser, so roles used via `setContext({ role })` must be created here).
|
|
41
|
+
* SQL run once after the instance is ready — the place for
|
|
42
|
+
* `CREATE EXTENSION ...` (and any extra roles/objects you want).
|
|
44
43
|
*/
|
|
45
44
|
extensionSql?: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Auto-create the standard app roles (`anonymous` / `authenticated` /
|
|
47
|
+
* `administrator` / `authenticated_client`) with the same attributes
|
|
48
|
+
* pgsql-test's server bootstrap uses, before seeding. This is what lets a
|
|
49
|
+
* bare `getConnections()` switch into an app role via `db.setContext()`
|
|
50
|
+
* with no manual `CREATE ROLE`. Defaults to `true`.
|
|
51
|
+
*
|
|
52
|
+
* Set `false` to boot as a lone superuser and manage your own roles/users
|
|
53
|
+
* through `extensionSql` (see the README for the pattern). Role *names* can
|
|
54
|
+
* be customized via `db.roles` (a `RoleMapping`), mirroring pgsql-test.
|
|
55
|
+
*/
|
|
56
|
+
roles?: boolean;
|
|
46
57
|
/** Reuse an already-created PGlite instance instead of creating one. */
|
|
47
58
|
instance?: PGlite;
|
|
48
59
|
};
|
|
@@ -60,7 +71,9 @@ export interface PgliteConnectionResult {
|
|
|
60
71
|
/**
|
|
61
72
|
* Create an isolated PGlite-backed test environment and return `pg`/`db` clients
|
|
62
73
|
* plus a `teardown()`. Defaults to seeding via `seed.pgpm()` (deploys the pgpm
|
|
63
|
-
* module in `cwd`), matching pgsql-test
|
|
74
|
+
* module in `cwd`) and creating the standard app roles, matching pgsql-test — so
|
|
75
|
+
* a bare `getConnections()` is enough to deploy and switch roles with no manual
|
|
76
|
+
* `CREATE ROLE`. Opt out of the role bootstrap with `pglite: { roles: false }`.
|
|
64
77
|
*/
|
|
65
78
|
export declare const getConnections: (cn?: PgliteConnectionOpts, seedAdapters?: SeedAdapter[]) => Promise<PgliteConnectionResult>;
|
|
66
79
|
export { PgliteTestClient, SharedTxn } from './txn';
|
package/index.js
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
*/
|
|
32
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
33
|
exports.seed = exports.PgTestClient = exports.SharedTxn = exports.PgliteTestClient = exports.getConnections = void 0;
|
|
34
|
+
const core_1 = require("@pgpmjs/core");
|
|
34
35
|
const pglite_adapter_1 = require("@pgpmjs/pglite-adapter");
|
|
35
36
|
const pg_env_1 = require("pg-env");
|
|
36
37
|
const pgsql_client_1 = require("pgsql-client");
|
|
@@ -39,17 +40,31 @@ const txn_1 = require("./txn");
|
|
|
39
40
|
/**
|
|
40
41
|
* Create an isolated PGlite-backed test environment and return `pg`/`db` clients
|
|
41
42
|
* plus a `teardown()`. Defaults to seeding via `seed.pgpm()` (deploys the pgpm
|
|
42
|
-
* module in `cwd`), matching pgsql-test
|
|
43
|
+
* module in `cwd`) and creating the standard app roles, matching pgsql-test — so
|
|
44
|
+
* a bare `getConnections()` is enough to deploy and switch roles with no manual
|
|
45
|
+
* `CREATE ROLE`. Opt out of the role bootstrap with `pglite: { roles: false }`.
|
|
43
46
|
*/
|
|
44
47
|
const getConnections = async (cn = {}, seedAdapters = [pgsql_test_1.seed.pgpm()]) => {
|
|
45
48
|
// Capture the previously-active client factory so teardown restores it
|
|
46
49
|
// (rather than clobbering to the default), mirroring how registerPglite
|
|
47
50
|
// restores the previous pool factory. Keeps nested/sequential suites clean.
|
|
48
51
|
const previousClientFactory = (0, pgsql_client_1.getActivePgClientFactory)();
|
|
52
|
+
// Seed the standard app roles by default so `db.setContext({ role })` works
|
|
53
|
+
// out of the box — PGlite boots as a lone superuser with no app roles, unlike
|
|
54
|
+
// a server where pgsql-test bootstraps them at createdb. Opt out with
|
|
55
|
+
// `pglite: { roles: false }` to manage your own (see README).
|
|
56
|
+
const bootstrapRoles = cn.pglite?.roles !== false;
|
|
57
|
+
const roleMapping = (0, pgsql_client_1.getRoleMapping)({ roles: cn.db?.roles });
|
|
58
|
+
const roleSql = bootstrapRoles
|
|
59
|
+
? [
|
|
60
|
+
(0, core_1.generateCreateBaseRolesSQL)(roleMapping),
|
|
61
|
+
(0, core_1.generateCreateClientRoleSQL)(roleMapping)
|
|
62
|
+
]
|
|
63
|
+
: [];
|
|
49
64
|
const handle = await (0, pglite_adapter_1.registerPglite)({
|
|
50
65
|
dataDir: cn.pglite?.dataDir,
|
|
51
66
|
extensions: cn.pglite?.extensions,
|
|
52
|
-
extensionSql: cn.pglite?.extensionSql,
|
|
67
|
+
extensionSql: [...roleSql, ...(cn.pglite?.extensionSql ?? [])],
|
|
53
68
|
instance: cn.pglite?.instance
|
|
54
69
|
});
|
|
55
70
|
// Route pgsql-client's PgClient at the same in-process PGlite session.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pglite-test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "Drop-in pgsql-test getConnections backed by an in-process PGlite instance — no Postgres server, instance-per-suite isolation",
|
|
6
6
|
"main": "index.js",
|
|
@@ -46,15 +46,16 @@
|
|
|
46
46
|
"makage": "^0.3.0"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@pgpmjs/
|
|
50
|
-
"@pgpmjs/
|
|
51
|
-
"
|
|
52
|
-
"pg-
|
|
53
|
-
"
|
|
54
|
-
"pgsql-
|
|
49
|
+
"@pgpmjs/core": "^6.28.1",
|
|
50
|
+
"@pgpmjs/env": "^2.26.4",
|
|
51
|
+
"@pgpmjs/pglite-adapter": "^0.1.3",
|
|
52
|
+
"pg-cache": "^3.14.3",
|
|
53
|
+
"pg-env": "^1.17.1",
|
|
54
|
+
"pgsql-client": "^3.20.3",
|
|
55
|
+
"pgsql-test": "^4.18.6"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
57
58
|
"@electric-sql/pglite": ">=0.5.0"
|
|
58
59
|
},
|
|
59
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "43b73b3014cb9388ca3218611455c7e0f202f042"
|
|
60
61
|
}
|