@pgpm/verify 0.15.3 → 0.15.5
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/Makefile +1 -1
- package/README.md +1 -1
- package/package.json +3 -3
- package/pgpm-verify.control +1 -1
- package/__tests__/ext-verify.test.ts +0 -489
- package/jest.config.js +0 -15
- /package/sql/{pgpm-verify--0.15.2.sql → pgpm-verify--0.15.3.sql} +0 -0
package/Makefile
CHANGED
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<img height="20" src="https://github.com/constructive-io/pgpm-modules/actions/workflows/ci.yml/badge.svg" />
|
|
10
10
|
</a>
|
|
11
11
|
<a href="https://github.com/constructive-io/pgpm-modules/blob/main/LICENSE"><img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/></a>
|
|
12
|
-
<a href="https://www.npmjs.com/package/@pgpm/verify"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/pgpm-modules?filename=packages%
|
|
12
|
+
<a href="https://www.npmjs.com/package/@pgpm/verify"><img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/pgpm-modules?filename=packages%2Fverify%2Fpackage.json"/></a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
15
|
Verification utilities for PostgreSQL modules
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pgpm/verify",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.5",
|
|
4
4
|
"description": "Verification utilities for PGPM deploy/verify/revert workflow",
|
|
5
5
|
"author": "Dan Lynch <pyramation@gmail.com>",
|
|
6
6
|
"contributors": [
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test:watch": "jest --watch"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"pgpm": "^1.
|
|
24
|
+
"pgpm": "^1.3.0"
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"bugs": {
|
|
32
32
|
"url": "https://github.com/constructive-io/pgpm-modules/issues"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "f6bbdfb20760e308b02968038b6f54191a9fd527"
|
|
35
35
|
}
|
package/pgpm-verify.control
CHANGED
|
@@ -1,489 +0,0 @@
|
|
|
1
|
-
import { getConnections, PgTestClient, snapshot } from 'pgsql-test';
|
|
2
|
-
|
|
3
|
-
let pg: PgTestClient;
|
|
4
|
-
let teardown: () => Promise<void>;
|
|
5
|
-
|
|
6
|
-
describe('ext-verify utilities', () => {
|
|
7
|
-
beforeAll(async () => {
|
|
8
|
-
({ pg, teardown } = await getConnections());
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
afterAll(async () => {
|
|
12
|
-
await teardown();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
beforeEach(async () => {
|
|
16
|
-
await pg.beforeEach();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(async () => {
|
|
20
|
-
await pg.afterEach();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
describe('helper functions', () => {
|
|
24
|
-
it('get_entity_from_str should extract entity name', async () => {
|
|
25
|
-
const tests = [
|
|
26
|
-
['public.users', 'users'],
|
|
27
|
-
['users', 'users'],
|
|
28
|
-
['schema.table_name', 'table_name'],
|
|
29
|
-
['my_function', 'my_function']
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
for (const [input, expected] of tests) {
|
|
33
|
-
const [result] = await pg.any(
|
|
34
|
-
`SELECT get_entity_from_str($1) as entity`,
|
|
35
|
-
[input]
|
|
36
|
-
);
|
|
37
|
-
expect(result.entity).toBe(expected);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('get_schema_from_str should extract schema name', async () => {
|
|
42
|
-
const tests = [
|
|
43
|
-
['public.users', 'public'],
|
|
44
|
-
['users', 'public'],
|
|
45
|
-
['my_schema.table_name', 'my_schema'],
|
|
46
|
-
['function_name', 'public']
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
for (const [input, expected] of tests) {
|
|
50
|
-
const [result] = await pg.any(
|
|
51
|
-
`SELECT get_schema_from_str($1) as schema_name`,
|
|
52
|
-
[input]
|
|
53
|
-
);
|
|
54
|
-
expect(result.schema_name).toBe(expected);
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('schema verification', () => {
|
|
60
|
-
it('verify_schema should return true for existing schema', async () => {
|
|
61
|
-
const [result] = await pg.any(
|
|
62
|
-
`SELECT verify_schema('public') as verified`
|
|
63
|
-
);
|
|
64
|
-
expect(result.verified).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('verify_schema should throw for non-existent schema', async () => {
|
|
68
|
-
await expect(
|
|
69
|
-
pg.any(`SELECT verify_schema('nonexistent_schema')`)
|
|
70
|
-
).rejects.toThrow('Nonexistent schema');
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('table verification', () => {
|
|
75
|
-
beforeEach(async () => {
|
|
76
|
-
// Create a test table
|
|
77
|
-
await pg.any(`
|
|
78
|
-
CREATE TABLE test_table (
|
|
79
|
-
id serial PRIMARY KEY,
|
|
80
|
-
name text NOT NULL,
|
|
81
|
-
email text UNIQUE
|
|
82
|
-
)
|
|
83
|
-
`);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('verify_table should return true for existing table', async () => {
|
|
87
|
-
const [result] = await pg.any(
|
|
88
|
-
`SELECT verify_table('test_table') as verified`
|
|
89
|
-
);
|
|
90
|
-
expect(result.verified).toBe(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('verify_table should work with schema-qualified names', async () => {
|
|
94
|
-
const [result] = await pg.any(
|
|
95
|
-
`SELECT verify_table('public.test_table') as verified`
|
|
96
|
-
);
|
|
97
|
-
expect(result.verified).toBe(true);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('verify_table should throw for non-existent table', async () => {
|
|
101
|
-
await expect(
|
|
102
|
-
pg.any(`SELECT verify_table('nonexistent_table')`)
|
|
103
|
-
).rejects.toThrow('Nonexistent table');
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
describe('constraint verification', () => {
|
|
108
|
-
beforeEach(async () => {
|
|
109
|
-
await pg.any(`
|
|
110
|
-
CREATE TABLE test_constraints (
|
|
111
|
-
id serial PRIMARY KEY,
|
|
112
|
-
name text NOT NULL,
|
|
113
|
-
email text UNIQUE,
|
|
114
|
-
age integer CHECK (age > 0)
|
|
115
|
-
)
|
|
116
|
-
`);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('verify_constraint should return true for existing constraint', async () => {
|
|
120
|
-
// Get the actual constraint name (PostgreSQL generates names)
|
|
121
|
-
const [constraint] = await pg.any(`
|
|
122
|
-
SELECT conname
|
|
123
|
-
FROM pg_constraint c
|
|
124
|
-
JOIN pg_class t ON c.conrelid = t.oid
|
|
125
|
-
WHERE t.relname = 'test_constraints' AND contype = 'u'
|
|
126
|
-
LIMIT 1
|
|
127
|
-
`);
|
|
128
|
-
|
|
129
|
-
if (constraint) {
|
|
130
|
-
const [result] = await pg.any(
|
|
131
|
-
`SELECT verify_constraint('test_constraints', $1) as verified`,
|
|
132
|
-
[constraint.conname]
|
|
133
|
-
);
|
|
134
|
-
expect(result.verified).toBe(true);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('verify_constraint should throw for non-existent constraint', async () => {
|
|
139
|
-
await expect(
|
|
140
|
-
pg.any(`SELECT verify_constraint('test_constraints', 'nonexistent_constraint')`)
|
|
141
|
-
).rejects.toThrow('Nonexistent constraint');
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('function verification', () => {
|
|
146
|
-
beforeEach(async () => {
|
|
147
|
-
await pg.any(`
|
|
148
|
-
CREATE OR REPLACE FUNCTION test_function(x integer)
|
|
149
|
-
RETURNS integer AS $$
|
|
150
|
-
BEGIN
|
|
151
|
-
RETURN x * 2;
|
|
152
|
-
END;
|
|
153
|
-
$$ LANGUAGE plpgsql;
|
|
154
|
-
`);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('verify_function should return true for existing function', async () => {
|
|
158
|
-
const [result] = await pg.any(
|
|
159
|
-
`SELECT verify_function('test_function') as verified`
|
|
160
|
-
);
|
|
161
|
-
expect(result.verified).toBe(true);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('verify_function should work with schema-qualified names', async () => {
|
|
165
|
-
const [result] = await pg.any(
|
|
166
|
-
`SELECT verify_function('public.test_function') as verified`
|
|
167
|
-
);
|
|
168
|
-
expect(result.verified).toBe(true);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('verify_function should throw for non-existent function', async () => {
|
|
172
|
-
await expect(
|
|
173
|
-
pg.any(`SELECT verify_function('nonexistent_function')`)
|
|
174
|
-
).rejects.toThrow('Nonexistent function');
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
describe('view verification', () => {
|
|
179
|
-
beforeEach(async () => {
|
|
180
|
-
await pg.any(`
|
|
181
|
-
CREATE TABLE test_view_base (
|
|
182
|
-
id serial PRIMARY KEY,
|
|
183
|
-
name text
|
|
184
|
-
)
|
|
185
|
-
`);
|
|
186
|
-
|
|
187
|
-
await pg.any(`
|
|
188
|
-
CREATE VIEW test_view AS
|
|
189
|
-
SELECT id, name FROM test_view_base WHERE name IS NOT NULL
|
|
190
|
-
`);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('verify_view should return true for existing view', async () => {
|
|
194
|
-
const [result] = await pg.any(
|
|
195
|
-
`SELECT verify_view('test_view') as verified`
|
|
196
|
-
);
|
|
197
|
-
expect(result.verified).toBe(true);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('verify_view should work with schema-qualified names', async () => {
|
|
201
|
-
const [result] = await pg.any(
|
|
202
|
-
`SELECT verify_view('public.test_view') as verified`
|
|
203
|
-
);
|
|
204
|
-
expect(result.verified).toBe(true);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('verify_view should throw for non-existent view', async () => {
|
|
208
|
-
await expect(
|
|
209
|
-
pg.any(`SELECT verify_view('nonexistent_view')`)
|
|
210
|
-
).rejects.toThrow('Nonexistent view');
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
describe('index verification', () => {
|
|
215
|
-
beforeEach(async () => {
|
|
216
|
-
await pg.any(`
|
|
217
|
-
CREATE TABLE test_index_table (
|
|
218
|
-
id serial PRIMARY KEY,
|
|
219
|
-
name text,
|
|
220
|
-
email text
|
|
221
|
-
)
|
|
222
|
-
`);
|
|
223
|
-
|
|
224
|
-
await pg.any(`
|
|
225
|
-
CREATE INDEX test_custom_index ON test_index_table (name)
|
|
226
|
-
`);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
it('verify_index should return true for existing index', async () => {
|
|
230
|
-
const [result] = await pg.any(
|
|
231
|
-
`SELECT verify_index('test_index_table', 'test_custom_index') as verified`
|
|
232
|
-
);
|
|
233
|
-
expect(result.verified).toBe(true);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('verify_index should throw for non-existent index', async () => {
|
|
237
|
-
await expect(
|
|
238
|
-
pg.any(`SELECT verify_index('test_index_table', 'nonexistent_index')`)
|
|
239
|
-
).rejects.toThrow('Nonexistent index');
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('list_indexes should return index information', async () => {
|
|
243
|
-
const results = await pg.any(
|
|
244
|
-
`SELECT * FROM list_indexes('test_index_table', 'test_custom_index')`
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
expect(results).toHaveLength(1);
|
|
248
|
-
expect(results[0].schema_name).toBe('public');
|
|
249
|
-
expect(results[0].table_name).toBe('test_index_table');
|
|
250
|
-
expect(results[0].index_name).toBe('test_custom_index');
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
describe('trigger verification', () => {
|
|
255
|
-
beforeEach(async () => {
|
|
256
|
-
await pg.any(`
|
|
257
|
-
CREATE TABLE test_trigger_table (
|
|
258
|
-
id serial PRIMARY KEY,
|
|
259
|
-
name text,
|
|
260
|
-
updated_at timestamp DEFAULT now()
|
|
261
|
-
)
|
|
262
|
-
`);
|
|
263
|
-
|
|
264
|
-
await pg.any(`
|
|
265
|
-
CREATE OR REPLACE FUNCTION update_timestamp()
|
|
266
|
-
RETURNS trigger AS $$
|
|
267
|
-
BEGIN
|
|
268
|
-
NEW.updated_at = now();
|
|
269
|
-
RETURN NEW;
|
|
270
|
-
END;
|
|
271
|
-
$$ LANGUAGE plpgsql;
|
|
272
|
-
`);
|
|
273
|
-
|
|
274
|
-
await pg.any(`
|
|
275
|
-
CREATE TRIGGER test_update_trigger
|
|
276
|
-
BEFORE UPDATE ON test_trigger_table
|
|
277
|
-
FOR EACH ROW EXECUTE FUNCTION update_timestamp()
|
|
278
|
-
`);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('verify_trigger should return true for existing trigger', async () => {
|
|
282
|
-
const [result] = await pg.any(
|
|
283
|
-
`SELECT verify_trigger('test_update_trigger') as verified`
|
|
284
|
-
);
|
|
285
|
-
expect(result.verified).toBe(true);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('verify_trigger should throw for non-existent trigger', async () => {
|
|
289
|
-
await expect(
|
|
290
|
-
pg.any(`SELECT verify_trigger('nonexistent_trigger')`)
|
|
291
|
-
).rejects.toThrow('Nonexistent trigger');
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
describe('type verification', () => {
|
|
296
|
-
beforeEach(async () => {
|
|
297
|
-
await pg.any(`
|
|
298
|
-
CREATE TYPE test_enum AS ENUM ('active', 'inactive', 'pending')
|
|
299
|
-
`);
|
|
300
|
-
|
|
301
|
-
await pg.any(`
|
|
302
|
-
CREATE TYPE test_composite AS (
|
|
303
|
-
name text,
|
|
304
|
-
value integer
|
|
305
|
-
)
|
|
306
|
-
`);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('verify_type should return true for existing enum type', async () => {
|
|
310
|
-
const [result] = await pg.any(
|
|
311
|
-
`SELECT verify_type('test_enum') as verified`
|
|
312
|
-
);
|
|
313
|
-
expect(result.verified).toBe(true);
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it('verify_type should return true for existing composite type', async () => {
|
|
317
|
-
const [result] = await pg.any(
|
|
318
|
-
`SELECT verify_type('test_composite') as verified`
|
|
319
|
-
);
|
|
320
|
-
expect(result.verified).toBe(true);
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('verify_type should throw for non-existent type', async () => {
|
|
324
|
-
await expect(
|
|
325
|
-
pg.any(`SELECT verify_type('nonexistent_type')`)
|
|
326
|
-
).rejects.toThrow('Nonexistent type');
|
|
327
|
-
});
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
describe('domain verification', () => {
|
|
331
|
-
beforeEach(async () => {
|
|
332
|
-
await pg.any(`
|
|
333
|
-
CREATE DOMAIN test_domain AS text CHECK (length(value) > 0)
|
|
334
|
-
`);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
it('verify_domain should return true for existing domain', async () => {
|
|
338
|
-
const [result] = await pg.any(
|
|
339
|
-
`SELECT verify_domain('test_domain') as verified`
|
|
340
|
-
);
|
|
341
|
-
expect(result.verified).toBe(true);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
it('verify_domain should throw for non-existent domain', async () => {
|
|
345
|
-
await expect(
|
|
346
|
-
pg.any(`SELECT verify_domain('nonexistent_domain')`)
|
|
347
|
-
).rejects.toThrow('Nonexistent type');
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
describe('role and membership verification', () => {
|
|
352
|
-
it('verify_role should return true for current user', async () => {
|
|
353
|
-
// Get current user
|
|
354
|
-
const [currentUser] = await pg.any(`SELECT current_user as username`);
|
|
355
|
-
|
|
356
|
-
const [result] = await pg.any(
|
|
357
|
-
`SELECT verify_role($1) as verified`,
|
|
358
|
-
[currentUser.username]
|
|
359
|
-
);
|
|
360
|
-
expect(result.verified).toBe(true);
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('verify_role should throw for non-existent role', async () => {
|
|
364
|
-
await expect(
|
|
365
|
-
pg.any(`SELECT verify_role('nonexistent_user_12345')`)
|
|
366
|
-
).rejects.toThrow('Nonexistent user');
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('list_memberships should return role memberships', async () => {
|
|
370
|
-
const [currentUser] = await pg.any(`SELECT current_user as username`);
|
|
371
|
-
|
|
372
|
-
const results = await pg.any(
|
|
373
|
-
`SELECT * FROM list_memberships($1)`,
|
|
374
|
-
[currentUser.username]
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
// Should at least include the user themselves
|
|
378
|
-
expect(results.length).toBeGreaterThan(0);
|
|
379
|
-
const usernames = results.map(r => r.rolname);
|
|
380
|
-
expect(usernames).toContain(currentUser.username);
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
describe('grant verification', () => {
|
|
385
|
-
beforeEach(async () => {
|
|
386
|
-
await pg.any(`
|
|
387
|
-
CREATE TABLE test_grants_table (
|
|
388
|
-
id serial PRIMARY KEY,
|
|
389
|
-
data text
|
|
390
|
-
)
|
|
391
|
-
`);
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
it('verify_table_grant should work for existing grants', async () => {
|
|
395
|
-
// Grant SELECT to current user (if not already granted)
|
|
396
|
-
const [currentUser] = await pg.any(`SELECT current_user as username`);
|
|
397
|
-
|
|
398
|
-
// The table owner should have all privileges
|
|
399
|
-
const [result] = await pg.any(
|
|
400
|
-
`SELECT verify_table_grant('test_grants_table', 'INSERT', $1) as verified`,
|
|
401
|
-
[currentUser.username]
|
|
402
|
-
);
|
|
403
|
-
expect(result.verified).toBe(true);
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
describe('extension verification', () => {
|
|
408
|
-
it('verify_extension should return true for available extensions', async () => {
|
|
409
|
-
// Most PostgreSQL installations have the plpgsql extension available
|
|
410
|
-
const availableExtensions = await pg.any(`
|
|
411
|
-
SELECT name FROM pg_available_extensions
|
|
412
|
-
WHERE name IN ('plpgsql', 'uuid-ossp', 'pgcrypto')
|
|
413
|
-
LIMIT 1
|
|
414
|
-
`);
|
|
415
|
-
|
|
416
|
-
if (availableExtensions.length > 0) {
|
|
417
|
-
const [result] = await pg.any(
|
|
418
|
-
`SELECT verify_extension($1) as verified`,
|
|
419
|
-
[availableExtensions[0].name]
|
|
420
|
-
);
|
|
421
|
-
expect(result.verified).toBe(true);
|
|
422
|
-
} else {
|
|
423
|
-
// Skip this test if no common extensions are available
|
|
424
|
-
expect(true).toBe(true);
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
it('verify_extension should throw for non-existent extension', async () => {
|
|
429
|
-
await expect(
|
|
430
|
-
pg.any(`SELECT verify_extension('definitely_nonexistent_extension_12345')`)
|
|
431
|
-
).rejects.toThrow('Nonexistent extension');
|
|
432
|
-
});
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
describe('security verification', () => {
|
|
436
|
-
beforeEach(async () => {
|
|
437
|
-
await pg.any(`
|
|
438
|
-
CREATE TABLE test_security_table (
|
|
439
|
-
id serial PRIMARY KEY,
|
|
440
|
-
user_id text,
|
|
441
|
-
data text
|
|
442
|
-
)
|
|
443
|
-
`);
|
|
444
|
-
|
|
445
|
-
// Enable row level security
|
|
446
|
-
await pg.any(`ALTER TABLE test_security_table ENABLE ROW LEVEL SECURITY`);
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it('verify_security should return true for tables with RLS enabled', async () => {
|
|
450
|
-
const [result] = await pg.any(
|
|
451
|
-
`SELECT verify_security('test_security_table') as verified`
|
|
452
|
-
);
|
|
453
|
-
expect(result.verified).toBe(true);
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
describe('policy verification', () => {
|
|
458
|
-
beforeEach(async () => {
|
|
459
|
-
await pg.any(`
|
|
460
|
-
CREATE TABLE test_policy_table (
|
|
461
|
-
id serial PRIMARY KEY,
|
|
462
|
-
user_id text,
|
|
463
|
-
data text
|
|
464
|
-
)
|
|
465
|
-
`);
|
|
466
|
-
|
|
467
|
-
await pg.any(`ALTER TABLE test_policy_table ENABLE ROW LEVEL SECURITY`);
|
|
468
|
-
|
|
469
|
-
await pg.any(`
|
|
470
|
-
CREATE POLICY test_policy ON test_policy_table
|
|
471
|
-
FOR SELECT
|
|
472
|
-
USING (user_id = current_user)
|
|
473
|
-
`);
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
it('verify_policy should return true for existing policy', async () => {
|
|
477
|
-
const [result] = await pg.any(
|
|
478
|
-
`SELECT verify_policy('test_policy', 'test_policy_table') as verified`
|
|
479
|
-
);
|
|
480
|
-
expect(result.verified).toBe(true);
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
it('verify_policy should throw for non-existent policy', async () => {
|
|
484
|
-
await expect(
|
|
485
|
-
pg.any(`SELECT verify_policy('nonexistent_policy', 'test_policy_table')`)
|
|
486
|
-
).rejects.toThrow('Nonexistent policy');
|
|
487
|
-
});
|
|
488
|
-
});
|
|
489
|
-
});
|
package/jest.config.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
|
2
|
-
module.exports = {
|
|
3
|
-
preset: 'ts-jest',
|
|
4
|
-
testEnvironment: 'node',
|
|
5
|
-
|
|
6
|
-
// Match both __tests__ and colocated test files
|
|
7
|
-
testMatch: ['**/?(*.)+(test|spec).{ts,tsx,js,jsx}'],
|
|
8
|
-
|
|
9
|
-
// Ignore build artifacts and type declarations
|
|
10
|
-
testPathIgnorePatterns: ['/dist/', '\\.d\\.ts$'],
|
|
11
|
-
modulePathIgnorePatterns: ['<rootDir>/dist/'],
|
|
12
|
-
watchPathIgnorePatterns: ['/dist/'],
|
|
13
|
-
|
|
14
|
-
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
|
|
15
|
-
};
|
|
File without changes
|