RLSTesterResults.test.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import type { SafeSqlFragment } from '@supabase/pg-meta'
  2. import { screen } from '@testing-library/react'
  3. import { describe, expect, it, vi } from 'vitest'
  4. import type { Policy } from '@/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRow.utils'
  5. import type { ParseQueryResults } from '@/components/interfaces/Auth/RLSTester/RLSTester.types'
  6. import { RLSTesterResults } from '@/components/interfaces/Auth/RLSTester/RLSTesterResults'
  7. import { render } from '@/tests/helpers'
  8. vi.mock('@/components/interfaces/Auth/RLSTester/useTestQueryRLS', () => ({
  9. useTestQueryRLS: () => ({ limit: 100 }),
  10. }))
  11. vi.mock('@/components/interfaces/Auth/RLSTester/RLSTableCard', () => ({
  12. RLSTableCard: () => <div data-testid="rls-table-card" />,
  13. }))
  14. vi.mock('@/components/interfaces/SQLEditor/UtilityPanel/Results', () => ({
  15. Results: () => <div data-testid="results" />,
  16. }))
  17. const sql = (s: string) => s as unknown as SafeSqlFragment
  18. const makePolicy = (definition: string | null = null): Policy =>
  19. ({ definition: definition !== null ? sql(definition) : null }) as Policy
  20. const makeTable = (
  21. overrides?: Partial<ParseQueryResults['tables'][number]>
  22. ): ParseQueryResults['tables'][number] => ({
  23. schema: 'public',
  24. table: 'items',
  25. isRLSEnabled: true,
  26. tablePolicies: [],
  27. ...overrides,
  28. })
  29. const defaultProps = {
  30. results: [],
  31. autoLimit: false,
  32. handleSelectEditPolicy: vi.fn(),
  33. }
  34. describe('RLSTesterResults', () => {
  35. describe('access badge', () => {
  36. it('shows "No access" badge when table has RLS enabled but no policies', () => {
  37. render(
  38. <RLSTesterResults
  39. {...defaultProps}
  40. parseQueryResults={{
  41. tables: [makeTable()],
  42. operation: 'SELECT',
  43. role: 'anon',
  44. }}
  45. />
  46. )
  47. expect(screen.getByText('No access')).toBeInTheDocument()
  48. })
  49. it('shows "No access" badge when a policy definition is false', () => {
  50. render(
  51. <RLSTesterResults
  52. {...defaultProps}
  53. parseQueryResults={{
  54. tables: [makeTable({ tablePolicies: [makePolicy('false')] })],
  55. operation: 'SELECT',
  56. role: 'anon',
  57. }}
  58. />
  59. )
  60. expect(screen.getByText('No access')).toBeInTheDocument()
  61. })
  62. it('shows "Has access" badge when results are empty and user has access', () => {
  63. render(
  64. <RLSTesterResults
  65. {...defaultProps}
  66. results={[]}
  67. parseQueryResults={{
  68. tables: [makeTable({ tablePolicies: [makePolicy('auth.uid() = user_id')] })],
  69. operation: 'SELECT',
  70. role: 'authenticated',
  71. }}
  72. />
  73. )
  74. expect(screen.getByText('Has access')).toBeInTheDocument()
  75. })
  76. it('shows "Can access" badge when results are returned', () => {
  77. render(
  78. <RLSTesterResults
  79. {...defaultProps}
  80. results={[{ id: 1 }]}
  81. parseQueryResults={{
  82. tables: [makeTable({ tablePolicies: [makePolicy('auth.uid() = user_id')] })],
  83. operation: 'SELECT',
  84. role: 'authenticated',
  85. }}
  86. />
  87. )
  88. expect(screen.getByText('Can access')).toBeInTheDocument()
  89. })
  90. })
  91. describe('policy admonitions', () => {
  92. it('shows service role admonition for postgres role', () => {
  93. render(
  94. <RLSTesterResults
  95. {...defaultProps}
  96. parseQueryResults={{
  97. tables: [makeTable()],
  98. operation: 'SELECT',
  99. role: undefined,
  100. }}
  101. />
  102. )
  103. expect(screen.getByText(/bypasses all RLS policies/)).toBeInTheDocument()
  104. })
  105. it('shows "no policies" admonition when RLS is enabled but no policies exist', () => {
  106. render(
  107. <RLSTesterResults
  108. {...defaultProps}
  109. parseQueryResults={{
  110. tables: [makeTable({ table: 'profiles', tablePolicies: [] })],
  111. operation: 'SELECT',
  112. role: 'anon',
  113. }}
  114. />
  115. )
  116. expect(screen.getByText(/no policies set up/)).toBeInTheDocument()
  117. expect(screen.getByText(/public.profiles/)).toBeInTheDocument()
  118. })
  119. it('shows "policy false" admonition when a policy evaluates to false', () => {
  120. render(
  121. <RLSTesterResults
  122. {...defaultProps}
  123. parseQueryResults={{
  124. tables: [makeTable({ table: 'secrets', tablePolicies: [makePolicy('false')] })],
  125. operation: 'SELECT',
  126. role: 'anon',
  127. }}
  128. />
  129. )
  130. expect(screen.getByText(/evaluates to/)).toBeInTheDocument()
  131. expect(screen.getByText(/public.secrets/)).toBeInTheDocument()
  132. })
  133. })
  134. describe('"Ran as" section', () => {
  135. it('shows postgres for service role', () => {
  136. render(
  137. <RLSTesterResults
  138. {...defaultProps}
  139. parseQueryResults={{
  140. tables: [],
  141. operation: 'SELECT',
  142. role: undefined,
  143. }}
  144. />
  145. )
  146. expect(screen.getAllByText('postgres').length).toBeGreaterThan(0)
  147. })
  148. it('shows "an Anonymous user" for anon role', () => {
  149. render(
  150. <RLSTesterResults
  151. {...defaultProps}
  152. parseQueryResults={{
  153. tables: [],
  154. operation: 'SELECT',
  155. role: 'anon',
  156. }}
  157. />
  158. )
  159. expect(screen.getByText('an Anonymous user')).toBeInTheDocument()
  160. expect(screen.getByText('Not logged in user')).toBeInTheDocument()
  161. })
  162. it('shows user email and ID when a user is present', () => {
  163. render(
  164. <RLSTesterResults
  165. {...defaultProps}
  166. parseQueryResults={{
  167. tables: [],
  168. operation: 'SELECT',
  169. role: 'authenticated',
  170. user: {
  171. id: 'user-123',
  172. email: 'alice@example.com',
  173. } as any,
  174. }}
  175. />
  176. )
  177. expect(screen.getByText('alice@example.com')).toBeInTheDocument()
  178. expect(screen.getByText('ID: user-123')).toBeInTheDocument()
  179. })
  180. })
  181. })