RLSTesterResults.test.tsx199 lines · main
1import type { SafeSqlFragment } from '@supabase/pg-meta'
2import { screen } from '@testing-library/react'
3import { describe, expect, it, vi } from 'vitest'
4
5import type { Policy } from '@/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRow.utils'
6import type { ParseQueryResults } from '@/components/interfaces/Auth/RLSTester/RLSTester.types'
7import { RLSTesterResults } from '@/components/interfaces/Auth/RLSTester/RLSTesterResults'
8import { render } from '@/tests/helpers'
9
10vi.mock('@/components/interfaces/Auth/RLSTester/useTestQueryRLS', () => ({
11 useTestQueryRLS: () => ({ limit: 100 }),
12}))
13
14vi.mock('@/components/interfaces/Auth/RLSTester/RLSTableCard', () => ({
15 RLSTableCard: () => <div data-testid="rls-table-card" />,
16}))
17
18vi.mock('@/components/interfaces/SQLEditor/UtilityPanel/Results', () => ({
19 Results: () => <div data-testid="results" />,
20}))
21
22const sql = (s: string) => s as unknown as SafeSqlFragment
23
24const makePolicy = (definition: string | null = null): Policy =>
25 ({ definition: definition !== null ? sql(definition) : null }) as Policy
26
27const 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
37const defaultProps = {
38 results: [],
39 autoLimit: false,
40 handleSelectEditPolicy: vi.fn(),
41}
42
43describe('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})