roles.test.ts239 lines · main
1import { afterAll, beforeAll, expect, test } from 'vitest'
2
3import pgMeta from '../src/index'
4import { cleanupRoot, createTestDatabase } from './db/utils'
5
6beforeAll(async () => {
7 // Any global setup if needed
8})
9
10afterAll(async () => {
11 await cleanupRoot()
12})
13
14const withTestDatabase = (
15 name: string,
16 fn: (db: Awaited<ReturnType<typeof createTestDatabase>>) => Promise<void>
17) => {
18 test(name, async () => {
19 const db = await createTestDatabase()
20 try {
21 await fn(db)
22 } finally {
23 await db.cleanup()
24 }
25 })
26}
27withTestDatabase('list roles', async ({ executeQuery }) => {
28 const { sql, zod } = await pgMeta.roles.list()
29 const res = zod.parse(await executeQuery(sql))
30
31 let role = res.find(({ name }) => name === 'postgres')
32
33 expect(role).toMatchInlineSnapshot(
34 { activeConnections: expect.any(Number), id: expect.any(Number) },
35 `
36 {
37 "activeConnections": Any<Number>,
38 "canBypassRls": true,
39 "canCreateDb": true,
40 "canCreateRole": true,
41 "canLogin": true,
42 "config": {},
43 "connectionLimit": 100,
44 "id": Any<Number>,
45 "inheritRole": true,
46 "isReplicationRole": true,
47 "isSuperuser": true,
48 "name": "postgres",
49 "validUntil": null,
50 }
51 `
52 )
53
54 // pg_monitor is a predefined role. `includeDefaultRoles` defaults to false,
55 // so it shouldn't be included in the result.
56 role = res.find(({ name }) => name === 'pg_monitor')
57
58 expect(role).toMatchInlineSnapshot(`undefined`)
59})
60
61withTestDatabase('list roles w/ default roles', async ({ executeQuery }) => {
62 const { sql, zod } = await pgMeta.roles.list({ includeDefaultRoles: true })
63 const res = zod.parse(await executeQuery(sql))
64
65 const role = res.find(({ name }) => name === 'pg_monitor')
66
67 expect(role).toMatchInlineSnapshot(
68 {
69 activeConnections: expect.any(Number),
70 id: expect.any(Number),
71 },
72 `
73 {
74 "activeConnections": Any<Number>,
75 "canBypassRls": false,
76 "canCreateDb": false,
77 "canCreateRole": false,
78 "canLogin": false,
79 "config": {},
80 "connectionLimit": 100,
81 "id": Any<Number>,
82 "inheritRole": true,
83 "isReplicationRole": false,
84 "isSuperuser": false,
85 "name": "pg_monitor",
86 "validUntil": null,
87 }
88 `
89 )
90})
91
92withTestDatabase('retrieve, create, update, delete roles', async ({ executeQuery }) => {
93 // Create role
94 const { sql: createSql } = pgMeta.roles.create({
95 name: 'r1',
96 isSuperuser: true,
97 canCreateDb: true,
98 canCreateRole: true,
99 inheritRole: false,
100 canLogin: true,
101 isReplicationRole: true,
102 canBypassRls: true,
103 connectionLimit: 100,
104 validUntil: '2020-01-01T00:00:00.000Z',
105 config: { search_path: 'extension, public' },
106 })
107 await executeQuery(createSql)
108
109 // Retrieve the created role using list
110 const { sql: listSql, zod: listZod } = await pgMeta.roles.list()
111 const roles = listZod.parse(await executeQuery(listSql))
112 const createdRole = roles.find((role) => role.name === 'r1')
113 expect(createdRole).toMatchInlineSnapshot(
114 { id: expect.any(Number), activeConnections: expect.any(Number) },
115 `
116 {
117 "activeConnections": Any<Number>,
118 "canBypassRls": true,
119 "canCreateDb": true,
120 "canCreateRole": true,
121 "canLogin": true,
122 "config": {
123 "search_path": ""extension, public"",
124 },
125 "connectionLimit": 100,
126 "id": Any<Number>,
127 "inheritRole": false,
128 "isReplicationRole": true,
129 "isSuperuser": true,
130 "name": "r1",
131 "validUntil": "2020-01-01 00:00:00+00",
132 }
133 `
134 )
135
136 // Remove role
137 const { sql: removeSql } = pgMeta.roles.remove({ id: createdRole!.id })
138 await executeQuery(removeSql)
139
140 // Create a new role for update test
141 const { sql: createNewSql } = pgMeta.roles.create({
142 name: 'r1',
143 })
144 await executeQuery(createNewSql)
145
146 // Get the role ID for update
147 const { sql: getIdSql, zod: getIdZod } = await pgMeta.roles.list()
148 const roleForUpdate = getIdZod
149 .parse(await executeQuery(getIdSql))
150 .find((role) => role.name === 'r1')
151
152 // Update role with ISO string date
153 const { sql: updateSql } = pgMeta.roles.update(
154 { id: roleForUpdate!.id },
155 {
156 name: 'rr',
157 isSuperuser: true,
158 canCreateDb: true,
159 canCreateRole: true,
160 inheritRole: false,
161 canLogin: true,
162 isReplicationRole: true,
163 canBypassRls: true,
164 connectionLimit: 100,
165 validUntil: '2020-01-01T00:00:00.000Z',
166 }
167 )
168 await executeQuery(updateSql)
169
170 // Verify update using retrieve
171 const { sql: retrieveUpdatedSql, zod: retrieveZod } = pgMeta.roles.retrieve({
172 id: roleForUpdate!.id,
173 })
174 const updatedRole = retrieveZod.parse((await executeQuery(retrieveUpdatedSql))[0])
175 expect(updatedRole).toMatchInlineSnapshot(
176 { id: expect.any(Number), activeConnections: expect.any(Number) },
177 `
178 {
179 "activeConnections": Any<Number>,
180 "canBypassRls": true,
181 "canCreateDb": true,
182 "canCreateRole": true,
183 "canLogin": true,
184 "config": {},
185 "connectionLimit": 100,
186 "id": Any<Number>,
187 "inheritRole": false,
188 "isReplicationRole": true,
189 "isSuperuser": true,
190 "name": "rr",
191 "validUntil": "2020-01-01 00:00:00+00",
192 }
193 `
194 )
195
196 // Create role with config
197 const { sql: createConfigSql } = pgMeta.roles.create({
198 name: 'config_role',
199 config: { search_path: 'public', log_statement: 'all' },
200 })
201 await executeQuery(createConfigSql)
202
203 // Verify config role using list
204 const { sql: listConfigSql, zod: listConfigZod } = await pgMeta.roles.list()
205 const configRole = listConfigZod
206 .parse(await executeQuery(listConfigSql))
207 .find((role) => role.name === 'config_role')
208 expect(configRole).toMatchInlineSnapshot(
209 { id: expect.any(Number), activeConnections: expect.any(Number) },
210 `
211 {
212 "activeConnections": Any<Number>,
213 "canBypassRls": false,
214 "canCreateDb": false,
215 "canCreateRole": false,
216 "canLogin": false,
217 "config": {
218 "log_statement": "all",
219 "search_path": "public",
220 },
221 "connectionLimit": 100,
222 "id": Any<Number>,
223 "inheritRole": true,
224 "isReplicationRole": false,
225 "isSuperuser": false,
226 "name": "config_role",
227 "validUntil": null,
228 }
229 `
230 )
231
232 // Remove role and verify it's gone
233 const { sql: finalRemoveSql } = pgMeta.roles.remove({ id: configRole!.id })
234 await executeQuery(finalRemoveSql)
235
236 const { sql: finalListSql, zod: finalListZod } = await pgMeta.roles.list()
237 const finalRoles = finalListZod.parse(await executeQuery(finalListSql))
238 expect(finalRoles.find((role) => role.name === 'config_role')).toBeUndefined()
239})