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