@jmruthers/pace-core 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/dist/{DataTable-ZQDRE46Q.js → DataTable-3SSI644S.js} +2 -2
  2. package/dist/{chunk-M4RW7PIP.js → chunk-2BJFM2JC.js} +105 -81
  3. package/dist/chunk-2BJFM2JC.js.map +1 -0
  4. package/dist/{chunk-5H3C2SWM.js → chunk-RTCA5ZNK.js} +2 -2
  5. package/dist/components.js +2 -2
  6. package/dist/index.js +2 -2
  7. package/dist/styles/core.css +3 -0
  8. package/dist/utils.js +1 -1
  9. package/docs/api/classes/ErrorBoundary.md +1 -1
  10. package/docs/api/classes/InvalidScopeError.md +1 -1
  11. package/docs/api/classes/MissingUserContextError.md +1 -1
  12. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  13. package/docs/api/classes/PermissionDeniedError.md +1 -1
  14. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  15. package/docs/api/classes/RBACAuditManager.md +1 -1
  16. package/docs/api/classes/RBACCache.md +1 -1
  17. package/docs/api/classes/RBACEngine.md +1 -1
  18. package/docs/api/classes/RBACError.md +1 -1
  19. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  20. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  21. package/docs/api/interfaces/AggregateConfig.md +1 -1
  22. package/docs/api/interfaces/ButtonProps.md +1 -1
  23. package/docs/api/interfaces/CardProps.md +1 -1
  24. package/docs/api/interfaces/ColorPalette.md +1 -1
  25. package/docs/api/interfaces/ColorShade.md +1 -1
  26. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  27. package/docs/api/interfaces/DataTableAction.md +1 -1
  28. package/docs/api/interfaces/DataTableColumn.md +1 -1
  29. package/docs/api/interfaces/DataTableProps.md +34 -34
  30. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  31. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  32. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  33. package/docs/api/interfaces/EventContextType.md +1 -1
  34. package/docs/api/interfaces/EventLogoProps.md +1 -1
  35. package/docs/api/interfaces/EventProviderProps.md +1 -1
  36. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  37. package/docs/api/interfaces/FileUploadProps.md +1 -1
  38. package/docs/api/interfaces/FooterProps.md +1 -1
  39. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  40. package/docs/api/interfaces/InputProps.md +1 -1
  41. package/docs/api/interfaces/LabelProps.md +1 -1
  42. package/docs/api/interfaces/LoginFormProps.md +1 -1
  43. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  44. package/docs/api/interfaces/NavigationContextType.md +1 -1
  45. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  46. package/docs/api/interfaces/NavigationItem.md +1 -1
  47. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  48. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  49. package/docs/api/interfaces/Organisation.md +1 -1
  50. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  51. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  52. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  53. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  54. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  55. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  56. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  57. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  58. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  59. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  60. package/docs/api/interfaces/PaletteData.md +1 -1
  61. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  62. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  63. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  64. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  65. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  66. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  67. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  68. package/docs/api/interfaces/RBACConfig.md +1 -1
  69. package/docs/api/interfaces/RBACContextType.md +1 -1
  70. package/docs/api/interfaces/RBACLogger.md +1 -1
  71. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  72. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  73. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  74. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  75. package/docs/api/interfaces/RouteConfig.md +1 -1
  76. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  77. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  78. package/docs/api/interfaces/StorageConfig.md +1 -1
  79. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  80. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  81. package/docs/api/interfaces/StorageListOptions.md +1 -1
  82. package/docs/api/interfaces/StorageListResult.md +1 -1
  83. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  84. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  85. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  86. package/docs/api/interfaces/StyleImport.md +1 -1
  87. package/docs/api/interfaces/ToastActionElement.md +1 -1
  88. package/docs/api/interfaces/ToastProps.md +1 -1
  89. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  90. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  91. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  92. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  93. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  94. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  95. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  96. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  97. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  98. package/docs/api/interfaces/UserEventAccess.md +1 -1
  99. package/docs/api/interfaces/UserMenuProps.md +1 -1
  100. package/docs/api/interfaces/UserProfile.md +1 -1
  101. package/docs/api/modules.md +3 -3
  102. package/docs/implementation-guides/data-tables.md +20 -0
  103. package/docs/quick-reference.md +9 -0
  104. package/docs/rbac/examples.md +4 -0
  105. package/package.json +1 -1
  106. package/src/__tests__/helpers/test-utils.tsx +147 -1
  107. package/src/components/DataTable/DataTable.tsx +20 -0
  108. package/src/components/DataTable/__tests__/DataTable.hooks.test 2.tsx +191 -0
  109. package/src/components/DataTable/__tests__/DataTable.hooks.test.tsx +191 -0
  110. package/src/components/DataTable/components/DataTableCore.tsx +167 -138
  111. package/src/components/Header/Header.test.tsx +1 -1
  112. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +1 -1
  113. package/src/hooks/__tests__/hooks.integration.test.tsx +575 -0
  114. package/src/hooks/__tests__/useApiFetch.unit.test.ts +115 -0
  115. package/src/hooks/__tests__/useComponentPerformance.unit.test.tsx +133 -0
  116. package/src/hooks/__tests__/useDebounce.unit.test.ts +82 -0
  117. package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +293 -0
  118. package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +385 -0
  119. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +286 -0
  120. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +838 -0
  121. package/src/hooks/__tests__/usePermissionCache.simple.test.ts +104 -0
  122. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +633 -0
  123. package/src/hooks/__tests__/useRBAC.unit.test.ts +856 -0
  124. package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +537 -0
  125. package/src/hooks/__tests__/useToast.unit.test.tsx +62 -0
  126. package/src/hooks/__tests__/useZodForm.unit.test.tsx +37 -0
  127. package/src/rbac/api.test.ts +511 -0
  128. package/src/rbac/components/__tests__/NavigationGuard.test.tsx +843 -0
  129. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +1007 -0
  130. package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +806 -0
  131. package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +741 -0
  132. package/src/rbac/hooks/useCan.test.ts +1 -1
  133. package/src/rbac/hooks/usePermissions.test.ts +10 -5
  134. package/src/rbac/hooks/useRBAC.test.ts +141 -93
  135. package/src/rbac/utils/__tests__/eventContext.test.ts +428 -0
  136. package/src/rbac/utils/__tests__/eventContext.unit.test.ts +428 -0
  137. package/src/styles/core.css +3 -0
  138. package/src/utils/__tests__/appConfig.unit.test.ts +55 -0
  139. package/src/utils/__tests__/audit.unit.test.ts +69 -0
  140. package/src/utils/__tests__/auth-utils.unit.test.ts +70 -0
  141. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +317 -0
  142. package/src/utils/__tests__/cn.unit.test.ts +34 -0
  143. package/src/utils/__tests__/deviceFingerprint.unit.test.ts +503 -0
  144. package/src/utils/__tests__/dynamicUtils.unit.test.ts +322 -0
  145. package/src/utils/__tests__/formatDate.unit.test.ts +109 -0
  146. package/src/utils/__tests__/formatting.unit.test.ts +66 -0
  147. package/src/utils/__tests__/index.unit.test.ts +251 -0
  148. package/src/utils/__tests__/lazyLoad.unit.test.tsx +309 -0
  149. package/src/utils/__tests__/organisationContext.unit.test.ts +192 -0
  150. package/src/utils/__tests__/performanceBudgets.unit.test.ts +259 -0
  151. package/src/utils/__tests__/permissionTypes.unit.test.ts +250 -0
  152. package/src/utils/__tests__/permissionUtils.unit.test.ts +362 -0
  153. package/src/utils/__tests__/sanitization.unit.test.ts +346 -0
  154. package/src/utils/__tests__/schemaUtils.unit.test.ts +441 -0
  155. package/src/utils/__tests__/secureDataAccess.unit.test.ts +334 -0
  156. package/src/utils/__tests__/secureErrors.unit.test.ts +377 -0
  157. package/src/utils/__tests__/secureStorage.unit.test.ts +293 -0
  158. package/src/utils/__tests__/security.unit.test.ts +127 -0
  159. package/src/utils/__tests__/securityMonitor.unit.test.ts +280 -0
  160. package/src/utils/__tests__/sessionTracking.unit.test.ts +356 -0
  161. package/src/utils/__tests__/validation.unit.test.ts +84 -0
  162. package/src/utils/__tests__/validationUtils.unit.test.ts +571 -0
  163. package/src/validation/__tests__/common.unit.test.ts +101 -0
  164. package/src/validation/__tests__/csrf.unit.test.ts +302 -0
  165. package/src/validation/__tests__/passwordSchema.unit.test 2.ts +98 -0
  166. package/src/validation/__tests__/passwordSchema.unit.test.ts +98 -0
  167. package/src/validation/__tests__/sqlInjectionProtection.unit.test.ts +466 -0
  168. package/dist/chunk-M4RW7PIP.js.map +0 -1
  169. /package/dist/{DataTable-ZQDRE46Q.js.map → DataTable-3SSI644S.js.map} +0 -0
  170. /package/dist/{chunk-5H3C2SWM.js.map → chunk-RTCA5ZNK.js.map} +0 -0
@@ -403,7 +403,7 @@ describe('useCan Hook', () => {
403
403
  permission: mockPermission,
404
404
  pageId: undefined
405
405
  });
406
- }, { timeout: 5000 });
406
+ }, { timeout: 20000 });
407
407
  });
408
408
  });
409
409
 
@@ -226,7 +226,9 @@ describe('usePermissions Hook', () => {
226
226
  mockGetPermissionMap.mockImplementation(() => new Promise(() => {}));
227
227
  result.current.refetch();
228
228
 
229
- expect(result.current.isLoading).toBe(true);
229
+ await waitFor(() => {
230
+ expect(result.current.isLoading).toBe(true);
231
+ }, { timeout: 1000 });
230
232
  });
231
233
  });
232
234
 
@@ -288,12 +290,15 @@ describe('usePermissions Hook', () => {
288
290
 
289
291
  const { result } = renderHook(() => usePermissions(mockUserId, {} as any));
290
292
 
293
+ // Wait for the hook to complete loading
291
294
  await waitFor(() => {
292
295
  expect(result.current.isLoading).toBe(false);
293
- expect(mockGetPermissionMap).toHaveBeenCalledWith({
294
- userId: mockUserId,
295
- scope: {}
296
- });
296
+ }, { timeout: 20000 });
297
+
298
+ // Then check the API call
299
+ expect(mockGetPermissionMap).toHaveBeenCalledWith({
300
+ userId: mockUserId,
301
+ scope: {}
297
302
  });
298
303
  });
299
304
 
@@ -33,12 +33,22 @@ const mockSupabase = {
33
33
  select: vi.fn(() => ({
34
34
  eq: vi.fn(() => ({
35
35
  eq: vi.fn(() => ({
36
- single: vi.fn()
36
+ single: vi.fn().mockResolvedValue({ data: { global_role: 'user' }, error: null })
37
37
  }))
38
38
  }))
39
- })),
40
- rpc: vi.fn()
41
- }))
39
+ }))
40
+ })),
41
+ rpc: vi.fn().mockResolvedValue({
42
+ data: [
43
+ {
44
+ permission_type: 'organisation_access',
45
+ role_name: 'user',
46
+ operation: 'read',
47
+ resource: 'users'
48
+ }
49
+ ],
50
+ error: null
51
+ })
42
52
  };
43
53
 
44
54
  // Mock data
@@ -93,14 +103,25 @@ describe('useRBAC Hook', () => {
93
103
  });
94
104
 
95
105
  describe('Initialization', () => {
96
- it('initializes with loading state', () => {
106
+ it('initializes with loading state', async () => {
97
107
  const { result } = renderHook(() => useRBAC());
98
108
 
99
- expect(result.current.isLoading).toBe(false);
109
+ // Wait for loading to complete
110
+ await waitFor(() => {
111
+ expect(result.current.isLoading).toBe(false);
112
+ });
113
+
100
114
  expect(result.current.globalRole).toBeNull();
101
- expect(result.current.organisationRole).toBeNull();
115
+ expect(result.current.organisationRole).toBe('user');
102
116
  expect(result.current.eventAppRole).toBeNull();
103
- expect(result.current.permissions).toEqual([]);
117
+ expect(result.current.permissions).toEqual([
118
+ {
119
+ permission_type: 'organisation_access',
120
+ role_name: 'user',
121
+ operation: 'read',
122
+ resource: 'users'
123
+ }
124
+ ]);
104
125
  expect(result.current.error).toBeNull();
105
126
  });
106
127
 
@@ -144,17 +165,17 @@ describe('useRBAC Hook', () => {
144
165
 
145
166
  describe('Role Detection', () => {
146
167
  it('detects super admin role', async () => {
147
- mockSupabase.from.mockReturnValue({
148
- select: vi.fn(() => ({
149
- eq: vi.fn(() => ({
150
- eq: vi.fn(() => ({
151
- single: vi.fn().mockResolvedValue({
152
- data: { global_role: 'super_admin' },
153
- error: null
154
- })
155
- }))
156
- }))
157
- }))
168
+ // Override the rpc mock for this test
169
+ mockSupabase.rpc.mockResolvedValue({
170
+ data: [
171
+ {
172
+ permission_type: 'all_permissions',
173
+ role_name: 'super_admin',
174
+ operation: 'read',
175
+ resource: 'users'
176
+ }
177
+ ],
178
+ error: null
158
179
  });
159
180
 
160
181
  const { result } = renderHook(() => useRBAC());
@@ -166,20 +187,17 @@ describe('useRBAC Hook', () => {
166
187
  });
167
188
 
168
189
  it('detects organisation roles', async () => {
169
- mockSupabase.from.mockReturnValue({
170
- select: vi.fn(() => ({
171
- eq: vi.fn(() => ({
172
- eq: vi.fn(() => ({
173
- single: vi.fn().mockResolvedValue({
174
- data: {
175
- global_role: 'user',
176
- organisation_role: 'org_admin'
177
- },
178
- error: null
179
- })
180
- }))
181
- }))
182
- }))
190
+ // Override the rpc mock for this test
191
+ mockSupabase.rpc.mockResolvedValue({
192
+ data: [
193
+ {
194
+ permission_type: 'organisation_access',
195
+ role_name: 'org_admin',
196
+ operation: 'read',
197
+ resource: 'users'
198
+ }
199
+ ],
200
+ error: null
183
201
  });
184
202
 
185
203
  const { result } = renderHook(() => useRBAC());
@@ -191,21 +209,17 @@ describe('useRBAC Hook', () => {
191
209
  });
192
210
 
193
211
  it('detects event app roles', async () => {
194
- mockSupabase.from.mockReturnValue({
195
- select: vi.fn(() => ({
196
- eq: vi.fn(() => ({
197
- eq: vi.fn(() => ({
198
- single: vi.fn().mockResolvedValue({
199
- data: {
200
- global_role: 'user',
201
- organisation_role: 'member',
202
- event_app_role: 'planner'
203
- },
204
- error: null
205
- })
206
- }))
207
- }))
208
- }))
212
+ // Override the rpc mock for this test
213
+ mockSupabase.rpc.mockResolvedValue({
214
+ data: [
215
+ {
216
+ permission_type: 'event_app_access',
217
+ role_name: 'planner',
218
+ operation: 'read',
219
+ resource: 'users'
220
+ }
221
+ ],
222
+ error: null
209
223
  });
210
224
 
211
225
  const { result } = renderHook(() => useRBAC());
@@ -216,21 +230,23 @@ describe('useRBAC Hook', () => {
216
230
  });
217
231
 
218
232
  it('handles role hierarchy correctly', async () => {
219
- mockSupabase.from.mockReturnValue({
220
- select: vi.fn(() => ({
221
- eq: vi.fn(() => ({
222
- eq: vi.fn(() => ({
223
- single: vi.fn().mockResolvedValue({
224
- data: {
225
- global_role: 'super_admin',
226
- organisation_role: 'org_admin',
227
- event_app_role: 'planner'
228
- },
229
- error: null
230
- })
231
- }))
232
- }))
233
- }))
233
+ // Override the rpc mock for this test
234
+ mockSupabase.rpc.mockResolvedValue({
235
+ data: [
236
+ {
237
+ permission_type: 'all_permissions',
238
+ role_name: 'super_admin',
239
+ operation: 'read',
240
+ resource: 'users'
241
+ },
242
+ {
243
+ permission_type: 'organisation_access',
244
+ role_name: 'org_admin',
245
+ operation: 'read',
246
+ resource: 'users'
247
+ }
248
+ ],
249
+ error: null
234
250
  });
235
251
 
236
252
  const { result } = renderHook(() => useRBAC());
@@ -244,17 +260,17 @@ describe('useRBAC Hook', () => {
244
260
 
245
261
  describe('Permission Checking', () => {
246
262
  it('hasPermission returns true for super admin', async () => {
247
- mockSupabase.from.mockReturnValue({
248
- select: vi.fn(() => ({
249
- eq: vi.fn(() => ({
250
- eq: vi.fn(() => ({
251
- single: vi.fn().mockResolvedValue({
252
- data: { global_role: 'super_admin' },
253
- error: null
254
- })
255
- }))
256
- }))
257
- }))
263
+ // Override the rpc mock for this test
264
+ mockSupabase.rpc.mockResolvedValue({
265
+ data: [
266
+ {
267
+ permission_type: 'all_permissions',
268
+ role_name: 'super_admin',
269
+ operation: 'read',
270
+ resource: 'users'
271
+ }
272
+ ],
273
+ error: null
258
274
  });
259
275
 
260
276
  const { result } = renderHook(() => useRBAC());
@@ -269,34 +285,50 @@ describe('useRBAC Hook', () => {
269
285
  });
270
286
 
271
287
  it('hasPermission checks database for regular users', async () => {
288
+ // Override the rpc mock for this test
289
+ mockSupabase.rpc.mockResolvedValue({
290
+ data: [
291
+ {
292
+ permission_type: 'organisation_access',
293
+ role_name: 'user',
294
+ operation: 'read',
295
+ resource: 'users'
296
+ }
297
+ ],
298
+ error: null
299
+ });
300
+
301
+ const { result } = renderHook(() => useRBAC());
302
+
303
+ await waitFor(() => {
304
+ expect(result.current.organisationRole).toBe('user');
305
+ });
306
+
307
+ // Mock the app lookup
272
308
  mockSupabase.from.mockReturnValue({
273
309
  select: vi.fn(() => ({
274
310
  eq: vi.fn(() => ({
275
311
  eq: vi.fn(() => ({
276
312
  single: vi.fn().mockResolvedValue({
277
- data: { global_role: 'user' },
313
+ data: { id: 'app-123' },
278
314
  error: null
279
315
  })
280
316
  }))
281
317
  }))
282
- })),
283
- rpc: vi.fn().mockResolvedValue({
284
- data: true,
285
- error: null
286
- })
318
+ }))
287
319
  });
288
320
 
289
- const { result } = renderHook(() => useRBAC());
290
-
291
- await waitFor(() => {
292
- expect(result.current.globalRole).toBe('user');
321
+ // Mock the permission check
322
+ mockSupabase.rpc.mockResolvedValue({
323
+ data: true,
324
+ error: null
293
325
  });
294
326
 
295
327
  const hasPermission = await result.current.hasPermission('read', 'dashboard');
296
328
  expect(hasPermission).toBe(true);
297
329
  expect(mockSupabase.rpc).toHaveBeenCalledWith('check_page_permission', {
298
330
  p_user_id: 'user-123',
299
- p_app_id: expect.any(String),
331
+ p_app_id: 'app-123',
300
332
  p_page_id: 'dashboard',
301
333
  p_operation: 'read',
302
334
  p_event_id: 'event-123',
@@ -305,25 +337,41 @@ describe('useRBAC Hook', () => {
305
337
  });
306
338
 
307
339
  it('hasPermission handles errors gracefully', async () => {
340
+ // Override the rpc mock for this test
341
+ mockSupabase.rpc.mockResolvedValue({
342
+ data: [
343
+ {
344
+ permission_type: 'organisation_access',
345
+ role_name: 'user',
346
+ operation: 'read',
347
+ resource: 'users'
348
+ }
349
+ ],
350
+ error: null
351
+ });
352
+
353
+ const { result } = renderHook(() => useRBAC());
354
+
355
+ await waitFor(() => {
356
+ expect(result.current.organisationRole).toBe('user');
357
+ });
358
+
359
+ // Mock the app lookup
308
360
  mockSupabase.from.mockReturnValue({
309
361
  select: vi.fn(() => ({
310
362
  eq: vi.fn(() => ({
311
363
  eq: vi.fn(() => ({
312
364
  single: vi.fn().mockResolvedValue({
313
- data: { global_role: 'user' },
365
+ data: { id: 'app-123' },
314
366
  error: null
315
367
  })
316
368
  }))
317
369
  }))
318
- })),
319
- rpc: vi.fn().mockRejectedValue(new Error('Database error'))
370
+ }))
320
371
  });
321
372
 
322
- const { result } = renderHook(() => useRBAC());
323
-
324
- await waitFor(() => {
325
- expect(result.current.globalRole).toBe('user');
326
- });
373
+ // Mock the permission check to fail
374
+ mockSupabase.rpc.mockRejectedValue(new Error('Database error'));
327
375
 
328
376
  const hasPermission = await result.current.hasPermission('read', 'dashboard');
329
377
  expect(hasPermission).toBe(false);