policies.test.ts142 lines · main
1import { afterAll, expect, test } from 'vitest'
2
3import pgMeta, { safeSql } from '../src/index'
4import { cleanupRoot, createTestDatabase } from './db/utils'
5
6afterAll(async () => {
7 await cleanupRoot()
8})
9
10const withTestDatabase = (
11 name: string,
12 fn: (db: Awaited<ReturnType<typeof createTestDatabase>>) => Promise<void>
13) => {
14 test(name, async () => {
15 const db = await createTestDatabase()
16 try {
17 await fn(db)
18 } finally {
19 await db.cleanup()
20 }
21 })
22}
23
24withTestDatabase('list policies', async ({ executeQuery }) => {
25 const { sql, zod } = pgMeta.policies.list()
26 const res = zod.parse(await executeQuery(sql))
27 const policy = res.find(({ name }) => name === 'categories_update_policy')
28
29 expect(policy).toMatchInlineSnapshot(
30 { id: expect.any(Number), table_id: expect.any(Number) },
31 `
32 {
33 "action": "PERMISSIVE",
34 "check": null,
35 "command": "UPDATE",
36 "definition": "(current_setting('my.username'::text) = name)",
37 "id": Any<Number>,
38 "name": "categories_update_policy",
39 "roles": [
40 "postgres",
41 ],
42 "schema": "public",
43 "table": "category",
44 "table_id": Any<Number>,
45 }
46 `
47 )
48})
49
50withTestDatabase('list policies with included schemas', async ({ executeQuery }) => {
51 const { sql, zod } = pgMeta.policies.list({
52 includedSchemas: ['public'],
53 })
54 const res = zod.parse(await executeQuery(sql))
55
56 expect(res.length).toBeGreaterThan(0)
57 res.forEach((policy) => {
58 expect(policy.schema).toBe('public')
59 })
60})
61
62withTestDatabase('retrieve, create, update, delete policies', async ({ executeQuery }) => {
63 // Create policy
64 const { sql: createSql } = pgMeta.policies.create({
65 name: 'test_policy',
66 schema: 'public',
67 table: 'memes',
68 action: 'RESTRICTIVE',
69 })
70 await executeQuery(createSql)
71
72 // List to get the created policy
73 const { sql: listSql, zod: listZod } = pgMeta.policies.list()
74 const policies = listZod.parse(await executeQuery(listSql))
75 const createdPolicy = policies.find((p) => p.name === 'test_policy')
76
77 expect(createdPolicy).toMatchInlineSnapshot(
78 { id: expect.any(Number), table_id: expect.any(Number) },
79 `
80 {
81 "action": "RESTRICTIVE",
82 "check": null,
83 "command": "ALL",
84 "definition": null,
85 "id": Any<Number>,
86 "name": "test_policy",
87 "roles": [
88 "public",
89 ],
90 "schema": "public",
91 "table": "memes",
92 "table_id": Any<Number>,
93 }
94 `
95 )
96
97 // Update policy
98 const { sql: updateSql } = pgMeta.policies.update(createdPolicy!, {
99 name: 'updated_policy',
100 definition: safeSql`current_setting('my.username') IN (name)`,
101 check: safeSql`current_setting('my.username') IN (name)`,
102 roles: ['postgres'],
103 })
104 await executeQuery(updateSql)
105
106 // Retrieve updated policy
107 const { sql: retrieveSql, zod: retrieveZod } = pgMeta.policies.retrieve({
108 id: createdPolicy!.id,
109 })
110 const updatedPolicy = retrieveZod.parse((await executeQuery(retrieveSql))[0])
111
112 expect(updatedPolicy).toMatchInlineSnapshot(
113 { id: expect.any(Number), table_id: expect.any(Number) },
114 `
115 {
116 "action": "RESTRICTIVE",
117 "check": "(current_setting('my.username'::text) = name)",
118 "command": "ALL",
119 "definition": "(current_setting('my.username'::text) = name)",
120 "id": Any<Number>,
121 "name": "updated_policy",
122 "roles": [
123 "postgres",
124 ],
125 "schema": "public",
126 "table": "memes",
127 "table_id": Any<Number>,
128 }
129 `
130 )
131
132 // Remove policy
133 const { sql: removeSql } = pgMeta.policies.remove(updatedPolicy!)
134 await executeQuery(removeSql)
135
136 // Verify policy is removed
137 const { sql: verifyRemoveSql } = pgMeta.policies.retrieve({
138 id: updatedPolicy!.id,
139 })
140 const result = await executeQuery(verifyRemoveSql)
141 expect(result).toHaveLength(0)
142})