policies.test.ts 3.8 KB

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