supabase-test 0.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 +23 -0
- package/README.md +557 -0
- package/admin.d.ts +26 -0
- package/admin.js +182 -0
- package/connect.d.ts +19 -0
- package/connect.js +83 -0
- package/dist/README.md +557 -0
- package/dist/package.json +72 -0
- package/esm/admin.js +178 -0
- package/esm/connect.js +78 -0
- package/esm/index.js +6 -0
- package/esm/manager.js +136 -0
- package/esm/roles.js +32 -0
- package/esm/seed/adapters.js +23 -0
- package/esm/seed/csv.js +44 -0
- package/esm/seed/index.js +16 -0
- package/esm/seed/json.js +18 -0
- package/esm/seed/launchql.js +19 -0
- package/esm/seed/sqitch.js +17 -0
- package/esm/seed/types.js +1 -0
- package/esm/stream.js +43 -0
- package/esm/test-client.js +150 -0
- package/index.d.ts +6 -0
- package/index.js +22 -0
- package/manager.d.ts +25 -0
- package/manager.js +140 -0
- package/package.json +72 -0
- package/roles.d.ts +17 -0
- package/roles.js +38 -0
- package/seed/adapters.d.ts +4 -0
- package/seed/adapters.js +28 -0
- package/seed/csv.d.ts +9 -0
- package/seed/csv.js +49 -0
- package/seed/index.d.ts +16 -0
- package/seed/index.js +33 -0
- package/seed/json.d.ts +6 -0
- package/seed/json.js +21 -0
- package/seed/launchql.d.ts +2 -0
- package/seed/launchql.js +22 -0
- package/seed/sqitch.d.ts +2 -0
- package/seed/sqitch.js +20 -0
- package/seed/types.d.ts +13 -0
- package/seed/types.js +2 -0
- package/stream.d.ts +2 -0
- package/stream.js +46 -0
- package/test-client.d.ts +49 -0
- package/test-client.js +154 -0
package/seed/sqitch.d.ts
ADDED
package/seed/sqitch.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sqitch = sqitch;
|
|
4
|
+
const core_1 = require("@launchql/core");
|
|
5
|
+
const env_1 = require("@launchql/env");
|
|
6
|
+
function sqitch(cwd) {
|
|
7
|
+
return {
|
|
8
|
+
async seed(ctx) {
|
|
9
|
+
const proj = new core_1.LaunchQLPackage(cwd ?? ctx.connect.cwd);
|
|
10
|
+
if (!proj.isInModule())
|
|
11
|
+
return;
|
|
12
|
+
await proj.deploy((0, env_1.getEnvOptions)({
|
|
13
|
+
pg: ctx.config,
|
|
14
|
+
deployment: {
|
|
15
|
+
fast: false
|
|
16
|
+
}
|
|
17
|
+
}), proj.getModuleName(), true);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
package/seed/types.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PgTestConnectionOptions } from '@launchql/types';
|
|
2
|
+
import { PgConfig } from 'pg-env';
|
|
3
|
+
import { DbAdmin } from '../admin';
|
|
4
|
+
import { PgTestClient } from '../test-client';
|
|
5
|
+
export interface SeedContext {
|
|
6
|
+
connect: PgTestConnectionOptions;
|
|
7
|
+
admin: DbAdmin;
|
|
8
|
+
config: PgConfig;
|
|
9
|
+
pg: PgTestClient;
|
|
10
|
+
}
|
|
11
|
+
export interface SeedAdapter {
|
|
12
|
+
seed(ctx: SeedContext): Promise<void> | void;
|
|
13
|
+
}
|
package/seed/types.js
ADDED
package/stream.d.ts
ADDED
package/stream.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.streamSql = streamSql;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
const pg_env_1 = require("pg-env");
|
|
6
|
+
const stream_1 = require("stream");
|
|
7
|
+
function setArgs(config) {
|
|
8
|
+
const args = [
|
|
9
|
+
'-U', config.user,
|
|
10
|
+
'-h', config.host,
|
|
11
|
+
'-d', config.database
|
|
12
|
+
];
|
|
13
|
+
if (config.port) {
|
|
14
|
+
args.push('-p', String(config.port));
|
|
15
|
+
}
|
|
16
|
+
return args;
|
|
17
|
+
}
|
|
18
|
+
// Converts a string to a readable stream (replaces streamify-string)
|
|
19
|
+
function stringToStream(text) {
|
|
20
|
+
const stream = new stream_1.Readable({
|
|
21
|
+
read() {
|
|
22
|
+
this.push(text);
|
|
23
|
+
this.push(null);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return stream;
|
|
27
|
+
}
|
|
28
|
+
async function streamSql(config, sql) {
|
|
29
|
+
const args = setArgs(config);
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const sqlStream = stringToStream(sql);
|
|
32
|
+
const proc = (0, child_process_1.spawn)('psql', args, {
|
|
33
|
+
env: (0, pg_env_1.getSpawnEnvWithPg)(config)
|
|
34
|
+
});
|
|
35
|
+
sqlStream.pipe(proc.stdin);
|
|
36
|
+
proc.on('close', (code) => {
|
|
37
|
+
resolve();
|
|
38
|
+
});
|
|
39
|
+
proc.on('error', (error) => {
|
|
40
|
+
reject(error);
|
|
41
|
+
});
|
|
42
|
+
proc.stderr.on('data', (data) => {
|
|
43
|
+
reject(new Error(data.toString()));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
package/test-client.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Client, QueryResult } from 'pg';
|
|
2
|
+
import { PgConfig } from 'pg-env';
|
|
3
|
+
import { AuthOptions, PgTestConnectionOptions } from '@launchql/types';
|
|
4
|
+
export type PgTestClientOpts = {
|
|
5
|
+
deferConnect?: boolean;
|
|
6
|
+
trackConnect?: (p: Promise<any>) => void;
|
|
7
|
+
} & Partial<PgTestConnectionOptions>;
|
|
8
|
+
export declare class PgTestClient {
|
|
9
|
+
config: PgConfig;
|
|
10
|
+
client: Client;
|
|
11
|
+
private opts;
|
|
12
|
+
private ctxStmts;
|
|
13
|
+
private contextSettings;
|
|
14
|
+
private _ended;
|
|
15
|
+
private connectPromise;
|
|
16
|
+
constructor(config: PgConfig, opts?: PgTestClientOpts);
|
|
17
|
+
private ensureConnected;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
begin(): Promise<void>;
|
|
20
|
+
savepoint(name?: string): Promise<void>;
|
|
21
|
+
rollback(name?: string): Promise<void>;
|
|
22
|
+
commit(): Promise<void>;
|
|
23
|
+
beforeEach(): Promise<void>;
|
|
24
|
+
afterEach(): Promise<void>;
|
|
25
|
+
setContext(ctx: Record<string, string | null>): void;
|
|
26
|
+
/**
|
|
27
|
+
* Set authentication context for the current session.
|
|
28
|
+
* Configures role and user ID using cascading defaults from options → opts.auth → RoleMapping.
|
|
29
|
+
*/
|
|
30
|
+
auth(options?: AuthOptions): void;
|
|
31
|
+
/**
|
|
32
|
+
* Commit current transaction to make data visible to other connections, then start fresh transaction.
|
|
33
|
+
* Maintains test isolation by creating a savepoint and reapplying session context.
|
|
34
|
+
*/
|
|
35
|
+
publish(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Clear all session context variables and reset to default anonymous role.
|
|
38
|
+
*/
|
|
39
|
+
clearContext(): void;
|
|
40
|
+
any<T = any>(query: string, values?: any[]): Promise<T[]>;
|
|
41
|
+
one<T = any>(query: string, values?: any[]): Promise<T>;
|
|
42
|
+
oneOrNone<T = any>(query: string, values?: any[]): Promise<T | null>;
|
|
43
|
+
many<T = any>(query: string, values?: any[]): Promise<T[]>;
|
|
44
|
+
manyOrNone<T = any>(query: string, values?: any[]): Promise<T[]>;
|
|
45
|
+
none(query: string, values?: any[]): Promise<void>;
|
|
46
|
+
result(query: string, values?: any[]): Promise<import('pg').QueryResult>;
|
|
47
|
+
query<T = any>(query: string, values?: any[]): Promise<QueryResult<T>>;
|
|
48
|
+
ctxQuery(): Promise<void>;
|
|
49
|
+
}
|
package/test-client.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PgTestClient = void 0;
|
|
4
|
+
const pg_1 = require("pg");
|
|
5
|
+
const roles_1 = require("./roles");
|
|
6
|
+
class PgTestClient {
|
|
7
|
+
config;
|
|
8
|
+
client;
|
|
9
|
+
opts;
|
|
10
|
+
ctxStmts = '';
|
|
11
|
+
contextSettings = {};
|
|
12
|
+
_ended = false;
|
|
13
|
+
connectPromise = null;
|
|
14
|
+
constructor(config, opts = {}) {
|
|
15
|
+
this.opts = opts;
|
|
16
|
+
this.config = config;
|
|
17
|
+
this.client = new pg_1.Client({
|
|
18
|
+
host: this.config.host,
|
|
19
|
+
port: this.config.port,
|
|
20
|
+
database: this.config.database,
|
|
21
|
+
user: this.config.user,
|
|
22
|
+
password: this.config.password
|
|
23
|
+
});
|
|
24
|
+
if (!opts.deferConnect) {
|
|
25
|
+
this.connectPromise = this.client.connect();
|
|
26
|
+
if (opts.trackConnect)
|
|
27
|
+
opts.trackConnect(this.connectPromise);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async ensureConnected() {
|
|
31
|
+
if (this.connectPromise) {
|
|
32
|
+
try {
|
|
33
|
+
await this.connectPromise;
|
|
34
|
+
}
|
|
35
|
+
catch { }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async close() {
|
|
39
|
+
if (!this._ended) {
|
|
40
|
+
this._ended = true;
|
|
41
|
+
await this.ensureConnected();
|
|
42
|
+
this.client.end();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async begin() {
|
|
46
|
+
await this.client.query('BEGIN;');
|
|
47
|
+
}
|
|
48
|
+
async savepoint(name = 'lqlsavepoint') {
|
|
49
|
+
await this.client.query(`SAVEPOINT "${name}";`);
|
|
50
|
+
}
|
|
51
|
+
async rollback(name = 'lqlsavepoint') {
|
|
52
|
+
await this.client.query(`ROLLBACK TO SAVEPOINT "${name}";`);
|
|
53
|
+
}
|
|
54
|
+
async commit() {
|
|
55
|
+
await this.client.query('COMMIT;');
|
|
56
|
+
}
|
|
57
|
+
async beforeEach() {
|
|
58
|
+
await this.begin();
|
|
59
|
+
await this.savepoint();
|
|
60
|
+
}
|
|
61
|
+
async afterEach() {
|
|
62
|
+
await this.rollback();
|
|
63
|
+
await this.commit();
|
|
64
|
+
}
|
|
65
|
+
setContext(ctx) {
|
|
66
|
+
Object.assign(this.contextSettings, ctx);
|
|
67
|
+
this.ctxStmts = Object.entries(this.contextSettings)
|
|
68
|
+
.map(([key, val]) => val === null
|
|
69
|
+
? `SELECT set_config('${key}', NULL, true);`
|
|
70
|
+
: `SELECT set_config('${key}', '${val}', true);`)
|
|
71
|
+
.join('\n');
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Set authentication context for the current session.
|
|
75
|
+
* Configures role and user ID using cascading defaults from options → opts.auth → RoleMapping.
|
|
76
|
+
*/
|
|
77
|
+
auth(options = {}) {
|
|
78
|
+
const role = options.role ?? this.opts.auth?.role ?? (0, roles_1.getRoleName)('authenticated', this.opts);
|
|
79
|
+
const userIdKey = options.userIdKey ?? this.opts.auth?.userIdKey ?? 'jwt.claims.user_id';
|
|
80
|
+
const userId = options.userId ?? this.opts.auth?.userId ?? null;
|
|
81
|
+
this.setContext({
|
|
82
|
+
role,
|
|
83
|
+
[userIdKey]: userId !== null ? String(userId) : null
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Commit current transaction to make data visible to other connections, then start fresh transaction.
|
|
88
|
+
* Maintains test isolation by creating a savepoint and reapplying session context.
|
|
89
|
+
*/
|
|
90
|
+
async publish() {
|
|
91
|
+
await this.commit(); // make data visible to other sessions
|
|
92
|
+
await this.begin(); // fresh tx
|
|
93
|
+
await this.savepoint(); // keep rollback harness
|
|
94
|
+
await this.ctxQuery(); // reapply all setContext()
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Clear all session context variables and reset to default anonymous role.
|
|
98
|
+
*/
|
|
99
|
+
clearContext() {
|
|
100
|
+
const defaultRole = (0, roles_1.getRoleName)('anonymous', this.opts);
|
|
101
|
+
const nulledSettings = {};
|
|
102
|
+
Object.keys(this.contextSettings).forEach(key => {
|
|
103
|
+
nulledSettings[key] = null;
|
|
104
|
+
});
|
|
105
|
+
nulledSettings.role = defaultRole;
|
|
106
|
+
this.ctxStmts = Object.entries(nulledSettings)
|
|
107
|
+
.map(([key, val]) => val === null
|
|
108
|
+
? `SELECT set_config('${key}', NULL, true);`
|
|
109
|
+
: `SELECT set_config('${key}', '${val}', true);`)
|
|
110
|
+
.join('\n');
|
|
111
|
+
this.contextSettings = { role: defaultRole };
|
|
112
|
+
}
|
|
113
|
+
async any(query, values) {
|
|
114
|
+
const result = await this.query(query, values);
|
|
115
|
+
return result.rows;
|
|
116
|
+
}
|
|
117
|
+
async one(query, values) {
|
|
118
|
+
const rows = await this.any(query, values);
|
|
119
|
+
if (rows.length !== 1) {
|
|
120
|
+
throw new Error('Expected exactly one result');
|
|
121
|
+
}
|
|
122
|
+
return rows[0];
|
|
123
|
+
}
|
|
124
|
+
async oneOrNone(query, values) {
|
|
125
|
+
const rows = await this.any(query, values);
|
|
126
|
+
return rows[0] || null;
|
|
127
|
+
}
|
|
128
|
+
async many(query, values) {
|
|
129
|
+
const rows = await this.any(query, values);
|
|
130
|
+
if (rows.length === 0)
|
|
131
|
+
throw new Error('Expected many rows, got none');
|
|
132
|
+
return rows;
|
|
133
|
+
}
|
|
134
|
+
async manyOrNone(query, values) {
|
|
135
|
+
return this.any(query, values);
|
|
136
|
+
}
|
|
137
|
+
async none(query, values) {
|
|
138
|
+
await this.query(query, values);
|
|
139
|
+
}
|
|
140
|
+
async result(query, values) {
|
|
141
|
+
return this.query(query, values);
|
|
142
|
+
}
|
|
143
|
+
async query(query, values) {
|
|
144
|
+
await this.ctxQuery();
|
|
145
|
+
const result = await this.client.query(query, values);
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
async ctxQuery() {
|
|
149
|
+
if (this.ctxStmts) {
|
|
150
|
+
await this.client.query(this.ctxStmts);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.PgTestClient = PgTestClient;
|