graphile-test 2.1.5 โ 2.1.6
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 +69 -41
- package/connect.d.ts +19 -0
- package/connect.js +26 -0
- package/context.d.ts +21 -0
- package/context.js +54 -0
- package/esm/connect.js +22 -0
- package/esm/context.js +46 -0
- package/esm/graphile-test.js +20 -163
- package/esm/index.js +1 -0
- package/esm/types.js +1 -0
- package/graphile-test.d.ts +4 -20
- package/graphile-test.js +20 -166
- package/index.d.ts +1 -0
- package/index.js +3 -0
- package/package.json +3 -3
- package/types.d.ts +12 -0
- package/types.js +2 -0
package/README.md
CHANGED
|
@@ -16,70 +16,98 @@
|
|
|
16
16
|
</a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
`graphile-test` builds on top of [`pgsql-test`](https://github.com/launchql/launchql/tree/main/packages/pgsql-test) to provide robust GraphQL testing utilities for PostGraphile-based projects.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
It provides a seamless setup for isolated, seeded, role-aware Postgres databases and injects GraphQL helpers for snapshot testing, role context, and mutation/query assertions.
|
|
22
|
+
|
|
23
|
+
## ๐ Features
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
* ๐ **Per-test rollback** via savepoints for isolation
|
|
26
|
+
* ๐ **RLS-aware context injection** (`setContext`)
|
|
27
|
+
* ๐งช **GraphQL integration testing** with `query()` and snapshot support
|
|
28
|
+
* ๐ฆ **Seed support** for `.sql`, JSON, CSV, LaunchQL, or Sqitch
|
|
29
|
+
* ๐ **Introspection query snapshotting**
|
|
30
|
+
* ๐ง **Raw SQL fallback** via `pg.client.query`
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
## ๐ฆ Install
|
|
28
33
|
|
|
29
|
-
```
|
|
30
|
-
|
|
34
|
+
```bash
|
|
35
|
+
npm install graphile-test
|
|
31
36
|
```
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
## โจ Quick Start
|
|
34
39
|
|
|
35
40
|
```ts
|
|
36
|
-
import {
|
|
37
|
-
import { MyGraphQLQuery } from '../src/queries';
|
|
38
|
-
|
|
39
|
-
const dbname = 'graphile_test_db';
|
|
40
|
-
const schemas = ['app_public'];
|
|
41
|
+
import { getConnections, seed } from 'graphile-test';
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
dbname,
|
|
44
|
-
schemas,
|
|
45
|
-
authRole: 'postgres',
|
|
46
|
-
});
|
|
43
|
+
let db, query, teardown;
|
|
47
44
|
|
|
48
45
|
beforeAll(async () => {
|
|
49
|
-
await
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
({ db, query, teardown } = await getConnections({
|
|
47
|
+
schemas: ['app_public'],
|
|
48
|
+
authRole: 'authenticated'
|
|
49
|
+
}, [
|
|
50
|
+
seed.sqlfile(['../sql/test.sql', '../sql/grants.sql'])
|
|
51
|
+
]));
|
|
53
52
|
});
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
beforeEach(() => db.beforeEach());
|
|
55
|
+
afterEach(() => db.afterEach());
|
|
56
|
+
afterAll(() => teardown());
|
|
57
|
+
|
|
58
|
+
it('runs a GraphQL mutation', async () => {
|
|
59
|
+
const res = await query(`mutation { ... }`);
|
|
60
|
+
expect(res.errors).toBeUndefined();
|
|
60
61
|
});
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
##
|
|
64
|
+
## ๐ API
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
### `getConnections(options, seeders)`
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
Returns an object with:
|
|
69
|
+
|
|
70
|
+
* `query` โ A GraphQL executor function: `query(gqlDoc, variables?)`
|
|
71
|
+
* `db`, `pg` โ `PgTestClient` instances
|
|
72
|
+
* `teardown()` โ Clean up temp DBs
|
|
73
|
+
|
|
74
|
+
### `PgTestClient`
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
Supports:
|
|
73
77
|
|
|
74
|
-
|
|
78
|
+
* `query`, `any`, `one`, etc. (via `pg-promise`-like helpers)
|
|
79
|
+
* `beforeEach()` / `afterEach()` โ for savepoint transaction handling
|
|
80
|
+
* `setContext({...})` โ sets Postgres config (e.g., `role`, `myapp.user_id`)
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
**See full `PgTestClient` API docs**: [pgsql-test โ PgTestClient API Overview](https://www.npmjs.com/package/pgsql-test#pgtestclient-api-overview)
|
|
83
|
+
|
|
84
|
+
## ๐งช Example Tests
|
|
85
|
+
|
|
86
|
+
### GraphQL mutation + snapshot
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const res = await query(MY_MUTATION, { input: { ... } });
|
|
90
|
+
expect(snapshot(res)).toMatchSnapshot();
|
|
80
91
|
```
|
|
81
92
|
|
|
82
|
-
|
|
93
|
+
### RLS testing with role switch
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
db.setContext({ role: 'anonymous' });
|
|
97
|
+
const res = await query(MY_PROTECTED_QUERY);
|
|
98
|
+
expect(res.errors[0].message).toMatch(/permission denied/);
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## ๐งฑ Under the Hood
|
|
102
|
+
|
|
103
|
+
`graphile-test` wraps and extends `pgsql-test` with GraphQL helpers like `query()` and introspection snapshot tools. You can drop into raw SQL testing anytime via `pg.client.query()` (superuser) or `db.client.query()` (RLS user).
|
|
104
|
+
|
|
105
|
+
## โ
Best Practices
|
|
106
|
+
|
|
107
|
+
* Use `db.setContext({ role, user_id })` to simulate authentication.
|
|
108
|
+
* Always wrap tests with `beforeEach` / `afterEach`.
|
|
109
|
+
* Use `snapshot()` to track GraphQL result changes.
|
|
110
|
+
* Use `useRoot: true` to test schema visibility without RLS.
|
|
83
111
|
|
|
84
112
|
## Related LaunchQL Tooling
|
|
85
113
|
|
package/connect.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { GetConnectionOpts } from 'pgsql-test';
|
|
2
|
+
import type { PgTestClient } from 'pgsql-test/test-client';
|
|
3
|
+
import type { SeedAdapter } from 'pgsql-test/seed/types';
|
|
4
|
+
import type { DocumentNode, ExecutionResult } from 'graphql';
|
|
5
|
+
export type GraphQLQueryFn<T = ExecutionResult> = (query: string | DocumentNode, variables?: Record<string, any>, commit?: boolean, reqOptions?: Record<string, any>) => Promise<T>;
|
|
6
|
+
export interface GetConnectionsInput {
|
|
7
|
+
useRoot?: boolean;
|
|
8
|
+
schemas: string[];
|
|
9
|
+
authRole?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Combines PostgreSQL test setup with GraphQL test context
|
|
13
|
+
*/
|
|
14
|
+
export declare const getConnections: (input: GetConnectionsInput & GetConnectionOpts, seedAdapters?: SeedAdapter[]) => Promise<{
|
|
15
|
+
pg: PgTestClient;
|
|
16
|
+
db: PgTestClient;
|
|
17
|
+
teardown: () => Promise<void>;
|
|
18
|
+
query: GraphQLQueryFn;
|
|
19
|
+
}>;
|
package/connect.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getConnections = void 0;
|
|
4
|
+
const pgsql_test_1 = require("pgsql-test");
|
|
5
|
+
const graphile_test_1 = require("./graphile-test");
|
|
6
|
+
/**
|
|
7
|
+
* Combines PostgreSQL test setup with GraphQL test context
|
|
8
|
+
*/
|
|
9
|
+
const getConnections = async (input, seedAdapters) => {
|
|
10
|
+
const conn = await (0, pgsql_test_1.getConnections)(input, seedAdapters);
|
|
11
|
+
const { pg, db, teardown: dbTeardown } = conn;
|
|
12
|
+
const gqlContext = (0, graphile_test_1.GraphQLTest)(input, conn);
|
|
13
|
+
await gqlContext.setup();
|
|
14
|
+
const teardown = async () => {
|
|
15
|
+
await gqlContext.teardown();
|
|
16
|
+
await dbTeardown();
|
|
17
|
+
};
|
|
18
|
+
const query = (query, variables, commit, reqOptions) => gqlContext.query({ query, variables, commit, reqOptions });
|
|
19
|
+
return {
|
|
20
|
+
pg,
|
|
21
|
+
db,
|
|
22
|
+
teardown,
|
|
23
|
+
query
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
exports.getConnections = getConnections;
|
package/context.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ExecutionResult, DocumentNode } from 'graphql';
|
|
2
|
+
import { PostGraphileOptions } from 'postgraphile';
|
|
3
|
+
import type { Client, Pool } from 'pg';
|
|
4
|
+
import { GetConnectionOpts, GetConnectionResult } from 'pgsql-test';
|
|
5
|
+
import { GetConnectionsInput } from './connect';
|
|
6
|
+
export declare const runGraphQLInContext: <T = ExecutionResult<{
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}, {
|
|
9
|
+
[key: string]: any;
|
|
10
|
+
}>>({ input, conn, pgPool, schema, options, authRole, query, variables, reqOptions }: {
|
|
11
|
+
input: GetConnectionsInput & GetConnectionOpts;
|
|
12
|
+
conn: GetConnectionResult;
|
|
13
|
+
pgPool: Pool;
|
|
14
|
+
schema: any;
|
|
15
|
+
options: PostGraphileOptions;
|
|
16
|
+
authRole: string;
|
|
17
|
+
query: string | DocumentNode;
|
|
18
|
+
variables?: Record<string, any>;
|
|
19
|
+
reqOptions?: Record<string, any>;
|
|
20
|
+
}) => Promise<T>;
|
|
21
|
+
export declare function setContextOnClient(pgClient: Client, pgSettings: Record<string, string>, role: string): Promise<void>;
|
package/context.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runGraphQLInContext = void 0;
|
|
7
|
+
exports.setContextOnClient = setContextOnClient;
|
|
8
|
+
const graphql_1 = require("graphql");
|
|
9
|
+
const postgraphile_1 = require("postgraphile");
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
const mock_req_1 = __importDefault(require("mock-req"));
|
|
12
|
+
const runGraphQLInContext = async ({ input, conn, pgPool, schema, options, authRole, query, variables, reqOptions = {} }) => {
|
|
13
|
+
if (!conn.pg.client) {
|
|
14
|
+
throw new Error('pgClient is required and must be provided externally.');
|
|
15
|
+
}
|
|
16
|
+
const req = new mock_req_1.default({
|
|
17
|
+
url: options.graphqlRoute || '/graphql',
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: {
|
|
20
|
+
Accept: 'application/json',
|
|
21
|
+
'Content-Type': 'application/json'
|
|
22
|
+
},
|
|
23
|
+
...reqOptions
|
|
24
|
+
});
|
|
25
|
+
const pgSettingsGenerator = options.pgSettings;
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
const pgSettings = typeof pgSettingsGenerator === 'function'
|
|
28
|
+
? await pgSettingsGenerator(req)
|
|
29
|
+
: pgSettingsGenerator || {};
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
return await (0, postgraphile_1.withPostGraphileContext)({ ...options, pgPool, pgSettings }, async (context) => {
|
|
32
|
+
const pgConn = input.useRoot ? conn.pg : conn.db;
|
|
33
|
+
const pgClient = pgConn.client;
|
|
34
|
+
// IS THIS BAD TO HAVE ROLE HERE
|
|
35
|
+
await setContextOnClient(pgClient, pgSettings, authRole);
|
|
36
|
+
await pgConn.ctxQuery();
|
|
37
|
+
const printed = typeof query === 'string' ? query : (0, graphql_1.print)(query);
|
|
38
|
+
const result = await (0, graphql_1.graphql)({
|
|
39
|
+
schema,
|
|
40
|
+
source: printed,
|
|
41
|
+
contextValue: { ...context, pgClient },
|
|
42
|
+
variableValues: variables ?? null
|
|
43
|
+
});
|
|
44
|
+
return result;
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
exports.runGraphQLInContext = runGraphQLInContext;
|
|
48
|
+
// IS THIS BAD TO HAVE ROLE HERE
|
|
49
|
+
async function setContextOnClient(pgClient, pgSettings, role) {
|
|
50
|
+
await pgClient.query(`select set_config('role', $1, true)`, [role]);
|
|
51
|
+
for (const [key, value] of Object.entries(pgSettings)) {
|
|
52
|
+
await pgClient.query(`select set_config($1, $2, true)`, [key, String(value)]);
|
|
53
|
+
}
|
|
54
|
+
}
|
package/esm/connect.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { getConnections as getPgConnections } from 'pgsql-test';
|
|
2
|
+
import { GraphQLTest } from './graphile-test';
|
|
3
|
+
/**
|
|
4
|
+
* Combines PostgreSQL test setup with GraphQL test context
|
|
5
|
+
*/
|
|
6
|
+
export const getConnections = async (input, seedAdapters) => {
|
|
7
|
+
const conn = await getPgConnections(input, seedAdapters);
|
|
8
|
+
const { pg, db, teardown: dbTeardown } = conn;
|
|
9
|
+
const gqlContext = GraphQLTest(input, conn);
|
|
10
|
+
await gqlContext.setup();
|
|
11
|
+
const teardown = async () => {
|
|
12
|
+
await gqlContext.teardown();
|
|
13
|
+
await dbTeardown();
|
|
14
|
+
};
|
|
15
|
+
const query = (query, variables, commit, reqOptions) => gqlContext.query({ query, variables, commit, reqOptions });
|
|
16
|
+
return {
|
|
17
|
+
pg,
|
|
18
|
+
db,
|
|
19
|
+
teardown,
|
|
20
|
+
query
|
|
21
|
+
};
|
|
22
|
+
};
|
package/esm/context.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { graphql, print } from 'graphql';
|
|
2
|
+
import { withPostGraphileContext } from 'postgraphile';
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import MockReq from 'mock-req';
|
|
5
|
+
export const runGraphQLInContext = async ({ input, conn, pgPool, schema, options, authRole, query, variables, reqOptions = {} }) => {
|
|
6
|
+
if (!conn.pg.client) {
|
|
7
|
+
throw new Error('pgClient is required and must be provided externally.');
|
|
8
|
+
}
|
|
9
|
+
const req = new MockReq({
|
|
10
|
+
url: options.graphqlRoute || '/graphql',
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: {
|
|
13
|
+
Accept: 'application/json',
|
|
14
|
+
'Content-Type': 'application/json'
|
|
15
|
+
},
|
|
16
|
+
...reqOptions
|
|
17
|
+
});
|
|
18
|
+
const pgSettingsGenerator = options.pgSettings;
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
const pgSettings = typeof pgSettingsGenerator === 'function'
|
|
21
|
+
? await pgSettingsGenerator(req)
|
|
22
|
+
: pgSettingsGenerator || {};
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
return await withPostGraphileContext({ ...options, pgPool, pgSettings }, async (context) => {
|
|
25
|
+
const pgConn = input.useRoot ? conn.pg : conn.db;
|
|
26
|
+
const pgClient = pgConn.client;
|
|
27
|
+
// IS THIS BAD TO HAVE ROLE HERE
|
|
28
|
+
await setContextOnClient(pgClient, pgSettings, authRole);
|
|
29
|
+
await pgConn.ctxQuery();
|
|
30
|
+
const printed = typeof query === 'string' ? query : print(query);
|
|
31
|
+
const result = await graphql({
|
|
32
|
+
schema,
|
|
33
|
+
source: printed,
|
|
34
|
+
contextValue: { ...context, pgClient },
|
|
35
|
+
variableValues: variables ?? null
|
|
36
|
+
});
|
|
37
|
+
return result;
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
// IS THIS BAD TO HAVE ROLE HERE
|
|
41
|
+
export async function setContextOnClient(pgClient, pgSettings, role) {
|
|
42
|
+
await pgClient.query(`select set_config('role', $1, true)`, [role]);
|
|
43
|
+
for (const [key, value] of Object.entries(pgSettings)) {
|
|
44
|
+
await pgClient.query(`select set_config($1, $2, true)`, [key, String(value)]);
|
|
45
|
+
}
|
|
46
|
+
}
|
package/esm/graphile-test.js
CHANGED
|
@@ -1,173 +1,30 @@
|
|
|
1
|
+
import { runGraphQLInContext } from './context';
|
|
2
|
+
import { createPostGraphileSchema } from 'postgraphile';
|
|
1
3
|
import { getGraphileSettings } from '@launchql/graphile-settings';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { print } from 'graphql/language/printer';
|
|
8
|
-
import { getEnvOptions } from '@launchql/types';
|
|
9
|
-
const opt = getEnvOptions();
|
|
10
|
-
export const GraphQLTest = ({ dbname, schemas, authRole = 'authenticated' }) => {
|
|
11
|
-
const getDbString = (db) => `postgres://${opt.pg.user}:${opt.pg.password}@${opt.pg.host}:${opt.pg.port}/${db}`;
|
|
12
|
-
const options = {
|
|
13
|
-
...getGraphileSettings({
|
|
14
|
-
graphile: {
|
|
15
|
-
schema: schemas
|
|
16
|
-
}
|
|
17
|
-
}),
|
|
18
|
-
graphqlRoute: '/graphql',
|
|
19
|
-
graphiqlRoute: '/graphiql'
|
|
20
|
-
};
|
|
21
|
-
pg.defaults.poolSize = 1;
|
|
22
|
-
const POSTGRAPHILE_AUTHENTICATOR_ROLE = authRole;
|
|
23
|
-
let ctx = null;
|
|
4
|
+
export const GraphQLTest = (input, conn) => {
|
|
5
|
+
const { schemas, authRole } = input;
|
|
6
|
+
let schema;
|
|
7
|
+
let options;
|
|
8
|
+
const pgPool = conn.manager.getPool(conn.pg.config);
|
|
24
9
|
const setup = async () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
const schema = await createPostGraphileSchema(rootPgPool, schemas, options);
|
|
29
|
-
ctx = { rootPgPool, options, schema };
|
|
30
|
-
};
|
|
31
|
-
const teardown = async () => {
|
|
32
|
-
try {
|
|
33
|
-
if (!ctx)
|
|
34
|
-
return;
|
|
35
|
-
const { rootPgPool } = ctx;
|
|
36
|
-
ctx = null;
|
|
37
|
-
await rootPgPool.end();
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
40
|
-
console.error(e);
|
|
41
|
-
}
|
|
10
|
+
options = getGraphileSettings({ graphile: { schema: schemas } });
|
|
11
|
+
schema = await createPostGraphileSchema(pgPool, schemas, options);
|
|
42
12
|
};
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
checker = args[1];
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
throw new Error('Invalid arguments supplied to graphQL');
|
|
57
|
-
}
|
|
58
|
-
const { schema, rootPgPool, options } = ctx;
|
|
59
|
-
const req = new MockReq({
|
|
60
|
-
url: options.graphqlRoute || '/graphql',
|
|
61
|
-
method: 'POST',
|
|
62
|
-
headers: {
|
|
63
|
-
Accept: 'application/json',
|
|
64
|
-
'Content-Type': 'application/json'
|
|
65
|
-
},
|
|
66
|
-
...reqOptions
|
|
67
|
-
});
|
|
68
|
-
const pgSettingsGenerator = options.pgSettings;
|
|
69
|
-
// @ts-ignore
|
|
70
|
-
const pgSettings = typeof pgSettingsGenerator === 'function' ? await pgSettingsGenerator(req) : pgSettingsGenerator || {};
|
|
71
|
-
return await withPostGraphileContext({ ...options, pgPool: rootPgPool, pgSettings }, async (context) => {
|
|
72
|
-
const replacementPgClient = await rootPgPool.connect();
|
|
73
|
-
await replacementPgClient.query('begin');
|
|
74
|
-
await replacementPgClient.query("select set_config('role', $1, true)", [POSTGRAPHILE_AUTHENTICATOR_ROLE]);
|
|
75
|
-
for (const [key, value] of Object.entries(pgSettings)) {
|
|
76
|
-
await replacementPgClient.query("select set_config($1, $2, true)", [key, String(value)]);
|
|
77
|
-
}
|
|
78
|
-
try {
|
|
79
|
-
const query = async (q, variables) => {
|
|
80
|
-
if (typeof q !== 'string')
|
|
81
|
-
q = print(q);
|
|
82
|
-
return await graphql(schema, q, null, { ...context, pgClient: replacementPgClient }, variables);
|
|
83
|
-
};
|
|
84
|
-
return await checker(query, replacementPgClient);
|
|
85
|
-
}
|
|
86
|
-
finally {
|
|
87
|
-
await replacementPgClient.query('rollback');
|
|
88
|
-
replacementPgClient.release();
|
|
89
|
-
}
|
|
13
|
+
const teardown = async () => { };
|
|
14
|
+
const query = async (opts) => {
|
|
15
|
+
return await runGraphQLInContext({
|
|
16
|
+
input,
|
|
17
|
+
schema,
|
|
18
|
+
options,
|
|
19
|
+
authRole,
|
|
20
|
+
pgPool,
|
|
21
|
+
conn,
|
|
22
|
+
...opts
|
|
90
23
|
});
|
|
91
24
|
};
|
|
92
|
-
async function graphQLQuery(...args) {
|
|
93
|
-
if (!ctx)
|
|
94
|
-
throw new Error('Context is not initialized. Did you run setup()?');
|
|
95
|
-
let reqOptions = {};
|
|
96
|
-
let Query;
|
|
97
|
-
let vars;
|
|
98
|
-
let commit = false;
|
|
99
|
-
if (args.length === 1) {
|
|
100
|
-
Query = args[0];
|
|
101
|
-
}
|
|
102
|
-
else if (args.length === 2) {
|
|
103
|
-
if (typeof args[1] === 'boolean') {
|
|
104
|
-
Query = args[0];
|
|
105
|
-
commit = args[1];
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
Query = args[0];
|
|
109
|
-
vars = args[1];
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
else if (args.length === 3) {
|
|
113
|
-
if (typeof args[2] === 'boolean') {
|
|
114
|
-
Query = args[0];
|
|
115
|
-
vars = args[1];
|
|
116
|
-
commit = args[2];
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
reqOptions = args[0];
|
|
120
|
-
Query = args[1];
|
|
121
|
-
vars = args[2];
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else if (args.length === 4) {
|
|
125
|
-
reqOptions = args[0];
|
|
126
|
-
Query = args[1];
|
|
127
|
-
vars = args[2];
|
|
128
|
-
commit = args[3];
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
throw new Error('Invalid arguments supplied to graphQLQuery');
|
|
132
|
-
}
|
|
133
|
-
const { schema, rootPgPool, options } = ctx;
|
|
134
|
-
const req = new MockReq({
|
|
135
|
-
url: options.graphqlRoute || '/graphql',
|
|
136
|
-
method: 'POST',
|
|
137
|
-
headers: {
|
|
138
|
-
Accept: 'application/json',
|
|
139
|
-
'Content-Type': 'application/json'
|
|
140
|
-
},
|
|
141
|
-
...reqOptions
|
|
142
|
-
});
|
|
143
|
-
const pgSettingsGenerator = options.pgSettings;
|
|
144
|
-
// @ts-ignore
|
|
145
|
-
const pgSettings = typeof pgSettingsGenerator === 'function' ? await pgSettingsGenerator(req) : pgSettingsGenerator || {};
|
|
146
|
-
return await withPostGraphileContext({ ...options, pgPool: rootPgPool, pgSettings }, async (context) => {
|
|
147
|
-
const replacementPgClient = await rootPgPool.connect();
|
|
148
|
-
await replacementPgClient.query('begin');
|
|
149
|
-
await replacementPgClient.query("select set_config('role', $1, true)", [POSTGRAPHILE_AUTHENTICATOR_ROLE]);
|
|
150
|
-
for (const [key, value] of Object.entries(pgSettings)) {
|
|
151
|
-
await replacementPgClient.query("select set_config($1, $2, true)", [key, String(value)]);
|
|
152
|
-
}
|
|
153
|
-
try {
|
|
154
|
-
if (typeof Query !== 'string')
|
|
155
|
-
Query = print(Query);
|
|
156
|
-
return await graphql(schema, Query, null, { ...context, pgClient: replacementPgClient }, vars);
|
|
157
|
-
}
|
|
158
|
-
finally {
|
|
159
|
-
await replacementPgClient.query(commit ? 'commit' : 'rollback');
|
|
160
|
-
replacementPgClient.release();
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
;
|
|
165
25
|
return {
|
|
166
26
|
setup,
|
|
167
27
|
teardown,
|
|
168
|
-
|
|
169
|
-
graphQLQuery,
|
|
170
|
-
// @ts-ignore
|
|
171
|
-
withContext: (cb) => cb(ctx)
|
|
28
|
+
query
|
|
172
29
|
};
|
|
173
30
|
};
|
package/esm/index.js
CHANGED
package/esm/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/graphile-test.d.ts
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
authRole?: string;
|
|
6
|
-
}
|
|
7
|
-
export declare const GraphQLTest: ({ dbname, schemas, authRole }: GraphQLTestOptions) => {
|
|
8
|
-
setup: () => Promise<void>;
|
|
9
|
-
teardown: () => Promise<void>;
|
|
10
|
-
graphQL: (...args: any[]) => Promise<any>;
|
|
11
|
-
graphQLQuery: {
|
|
12
|
-
(Query: string | DocumentNode): Promise<any>;
|
|
13
|
-
(Query: string | DocumentNode, commit: boolean): Promise<any>;
|
|
14
|
-
(Query: string | DocumentNode, vars: Record<string, any>): Promise<any>;
|
|
15
|
-
(Query: string | DocumentNode, vars: Record<string, any>, commit: boolean): Promise<any>;
|
|
16
|
-
(reqOptions: Record<string, any>, Query: string | DocumentNode, vars: Record<string, any>): Promise<any>;
|
|
17
|
-
(reqOptions: Record<string, any>, Query: string | DocumentNode, vars: Record<string, any>, commit: boolean): Promise<any>;
|
|
18
|
-
};
|
|
19
|
-
withContext: <T>(cb: (ctx: typeof ctx) => T) => T;
|
|
20
|
-
};
|
|
1
|
+
import type { GraphQLTestContext } from './types';
|
|
2
|
+
import { GetConnectionsInput } from './connect';
|
|
3
|
+
import { GetConnectionOpts, GetConnectionResult } from 'pgsql-test';
|
|
4
|
+
export declare const GraphQLTest: (input: GetConnectionsInput & GetConnectionOpts, conn: GetConnectionResult) => GraphQLTestContext;
|
package/graphile-test.js
CHANGED
|
@@ -1,180 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.GraphQLTest = void 0;
|
|
7
|
-
const
|
|
8
|
-
const pg_1 = __importDefault(require("pg"));
|
|
4
|
+
const context_1 = require("./context");
|
|
9
5
|
const postgraphile_1 = require("postgraphile");
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const GraphQLTest = ({ dbname, schemas, authRole = 'authenticated' }) => {
|
|
17
|
-
const getDbString = (db) => `postgres://${opt.pg.user}:${opt.pg.password}@${opt.pg.host}:${opt.pg.port}/${db}`;
|
|
18
|
-
const options = {
|
|
19
|
-
...(0, graphile_settings_1.getGraphileSettings)({
|
|
20
|
-
graphile: {
|
|
21
|
-
schema: schemas
|
|
22
|
-
}
|
|
23
|
-
}),
|
|
24
|
-
graphqlRoute: '/graphql',
|
|
25
|
-
graphiqlRoute: '/graphiql'
|
|
26
|
-
};
|
|
27
|
-
pg_1.default.defaults.poolSize = 1;
|
|
28
|
-
const POSTGRAPHILE_AUTHENTICATOR_ROLE = authRole;
|
|
29
|
-
let ctx = null;
|
|
6
|
+
const graphile_settings_1 = require("@launchql/graphile-settings");
|
|
7
|
+
const GraphQLTest = (input, conn) => {
|
|
8
|
+
const { schemas, authRole } = input;
|
|
9
|
+
let schema;
|
|
10
|
+
let options;
|
|
11
|
+
const pgPool = conn.manager.getPool(conn.pg.config);
|
|
30
12
|
const setup = async () => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
const schema = await (0, postgraphile_1.createPostGraphileSchema)(rootPgPool, schemas, options);
|
|
35
|
-
ctx = { rootPgPool, options, schema };
|
|
13
|
+
options = (0, graphile_settings_1.getGraphileSettings)({ graphile: { schema: schemas } });
|
|
14
|
+
schema = await (0, postgraphile_1.createPostGraphileSchema)(pgPool, schemas, options);
|
|
36
15
|
};
|
|
37
|
-
const teardown = async () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
const graphQL = async (...args) => {
|
|
50
|
-
if (!ctx)
|
|
51
|
-
throw new Error('Context is not initialized. Did you run setup()?');
|
|
52
|
-
let reqOptions = {};
|
|
53
|
-
let checker;
|
|
54
|
-
if (args.length === 1) {
|
|
55
|
-
checker = args[0];
|
|
56
|
-
}
|
|
57
|
-
else if (args.length === 2) {
|
|
58
|
-
reqOptions = args[0];
|
|
59
|
-
checker = args[1];
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
throw new Error('Invalid arguments supplied to graphQL');
|
|
63
|
-
}
|
|
64
|
-
const { schema, rootPgPool, options } = ctx;
|
|
65
|
-
const req = new mock_req_1.default({
|
|
66
|
-
url: options.graphqlRoute || '/graphql',
|
|
67
|
-
method: 'POST',
|
|
68
|
-
headers: {
|
|
69
|
-
Accept: 'application/json',
|
|
70
|
-
'Content-Type': 'application/json'
|
|
71
|
-
},
|
|
72
|
-
...reqOptions
|
|
73
|
-
});
|
|
74
|
-
const pgSettingsGenerator = options.pgSettings;
|
|
75
|
-
// @ts-ignore
|
|
76
|
-
const pgSettings = typeof pgSettingsGenerator === 'function' ? await pgSettingsGenerator(req) : pgSettingsGenerator || {};
|
|
77
|
-
return await (0, postgraphile_1.withPostGraphileContext)({ ...options, pgPool: rootPgPool, pgSettings }, async (context) => {
|
|
78
|
-
const replacementPgClient = await rootPgPool.connect();
|
|
79
|
-
await replacementPgClient.query('begin');
|
|
80
|
-
await replacementPgClient.query("select set_config('role', $1, true)", [POSTGRAPHILE_AUTHENTICATOR_ROLE]);
|
|
81
|
-
for (const [key, value] of Object.entries(pgSettings)) {
|
|
82
|
-
await replacementPgClient.query("select set_config($1, $2, true)", [key, String(value)]);
|
|
83
|
-
}
|
|
84
|
-
try {
|
|
85
|
-
const query = async (q, variables) => {
|
|
86
|
-
if (typeof q !== 'string')
|
|
87
|
-
q = (0, printer_1.print)(q);
|
|
88
|
-
return await (0, graphql_1.graphql)(schema, q, null, { ...context, pgClient: replacementPgClient }, variables);
|
|
89
|
-
};
|
|
90
|
-
return await checker(query, replacementPgClient);
|
|
91
|
-
}
|
|
92
|
-
finally {
|
|
93
|
-
await replacementPgClient.query('rollback');
|
|
94
|
-
replacementPgClient.release();
|
|
95
|
-
}
|
|
16
|
+
const teardown = async () => { };
|
|
17
|
+
const query = async (opts) => {
|
|
18
|
+
return await (0, context_1.runGraphQLInContext)({
|
|
19
|
+
input,
|
|
20
|
+
schema,
|
|
21
|
+
options,
|
|
22
|
+
authRole,
|
|
23
|
+
pgPool,
|
|
24
|
+
conn,
|
|
25
|
+
...opts
|
|
96
26
|
});
|
|
97
27
|
};
|
|
98
|
-
async function graphQLQuery(...args) {
|
|
99
|
-
if (!ctx)
|
|
100
|
-
throw new Error('Context is not initialized. Did you run setup()?');
|
|
101
|
-
let reqOptions = {};
|
|
102
|
-
let Query;
|
|
103
|
-
let vars;
|
|
104
|
-
let commit = false;
|
|
105
|
-
if (args.length === 1) {
|
|
106
|
-
Query = args[0];
|
|
107
|
-
}
|
|
108
|
-
else if (args.length === 2) {
|
|
109
|
-
if (typeof args[1] === 'boolean') {
|
|
110
|
-
Query = args[0];
|
|
111
|
-
commit = args[1];
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
Query = args[0];
|
|
115
|
-
vars = args[1];
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
else if (args.length === 3) {
|
|
119
|
-
if (typeof args[2] === 'boolean') {
|
|
120
|
-
Query = args[0];
|
|
121
|
-
vars = args[1];
|
|
122
|
-
commit = args[2];
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
reqOptions = args[0];
|
|
126
|
-
Query = args[1];
|
|
127
|
-
vars = args[2];
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
else if (args.length === 4) {
|
|
131
|
-
reqOptions = args[0];
|
|
132
|
-
Query = args[1];
|
|
133
|
-
vars = args[2];
|
|
134
|
-
commit = args[3];
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
throw new Error('Invalid arguments supplied to graphQLQuery');
|
|
138
|
-
}
|
|
139
|
-
const { schema, rootPgPool, options } = ctx;
|
|
140
|
-
const req = new mock_req_1.default({
|
|
141
|
-
url: options.graphqlRoute || '/graphql',
|
|
142
|
-
method: 'POST',
|
|
143
|
-
headers: {
|
|
144
|
-
Accept: 'application/json',
|
|
145
|
-
'Content-Type': 'application/json'
|
|
146
|
-
},
|
|
147
|
-
...reqOptions
|
|
148
|
-
});
|
|
149
|
-
const pgSettingsGenerator = options.pgSettings;
|
|
150
|
-
// @ts-ignore
|
|
151
|
-
const pgSettings = typeof pgSettingsGenerator === 'function' ? await pgSettingsGenerator(req) : pgSettingsGenerator || {};
|
|
152
|
-
return await (0, postgraphile_1.withPostGraphileContext)({ ...options, pgPool: rootPgPool, pgSettings }, async (context) => {
|
|
153
|
-
const replacementPgClient = await rootPgPool.connect();
|
|
154
|
-
await replacementPgClient.query('begin');
|
|
155
|
-
await replacementPgClient.query("select set_config('role', $1, true)", [POSTGRAPHILE_AUTHENTICATOR_ROLE]);
|
|
156
|
-
for (const [key, value] of Object.entries(pgSettings)) {
|
|
157
|
-
await replacementPgClient.query("select set_config($1, $2, true)", [key, String(value)]);
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
if (typeof Query !== 'string')
|
|
161
|
-
Query = (0, printer_1.print)(Query);
|
|
162
|
-
return await (0, graphql_1.graphql)(schema, Query, null, { ...context, pgClient: replacementPgClient }, vars);
|
|
163
|
-
}
|
|
164
|
-
finally {
|
|
165
|
-
await replacementPgClient.query(commit ? 'commit' : 'rollback');
|
|
166
|
-
replacementPgClient.release();
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
;
|
|
171
28
|
return {
|
|
172
29
|
setup,
|
|
173
30
|
teardown,
|
|
174
|
-
|
|
175
|
-
graphQLQuery,
|
|
176
|
-
// @ts-ignore
|
|
177
|
-
withContext: (cb) => cb(ctx)
|
|
31
|
+
query
|
|
178
32
|
};
|
|
179
33
|
};
|
|
180
34
|
exports.GraphQLTest = GraphQLTest;
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -14,5 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.seed = void 0;
|
|
17
18
|
__exportStar(require("./clean"), exports);
|
|
18
19
|
__exportStar(require("./graphile-test"), exports);
|
|
20
|
+
var pgsql_test_1 = require("pgsql-test");
|
|
21
|
+
Object.defineProperty(exports, "seed", { enumerable: true, get: function () { return pgsql_test_1.seed; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphile-test",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.6",
|
|
4
4
|
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
5
5
|
"description": "PostGraphile Testing",
|
|
6
6
|
"main": "index.js",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"graphql-tag": "2.12.6"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@launchql/graphile-settings": "^2.1.
|
|
37
|
+
"@launchql/graphile-settings": "^2.1.6",
|
|
38
38
|
"@launchql/types": "^2.1.5",
|
|
39
39
|
"graphql": "^15.5.2",
|
|
40
40
|
"mock-req": "^0.2.0",
|
|
41
41
|
"pg": "^8.16.0",
|
|
42
42
|
"postgraphile": "^4.14.1"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "94a9cdd4a89443ef5831a14a2c104b5b40572420"
|
|
45
45
|
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DocumentNode, ExecutionResult } from 'graphql';
|
|
2
|
+
export interface GraphQLQueryOptions {
|
|
3
|
+
query: string | DocumentNode;
|
|
4
|
+
variables?: Record<string, any>;
|
|
5
|
+
commit?: boolean;
|
|
6
|
+
reqOptions?: Record<string, any>;
|
|
7
|
+
}
|
|
8
|
+
export interface GraphQLTestContext {
|
|
9
|
+
setup: () => Promise<void>;
|
|
10
|
+
teardown: () => Promise<void>;
|
|
11
|
+
query: <T = ExecutionResult>(opts: GraphQLQueryOptions) => Promise<T>;
|
|
12
|
+
}
|
package/types.js
ADDED