@jmruthers/pace-core 0.5.110 → 0.5.111
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.
- package/dist/{AuthService-DrHrvXNZ.d.ts → AuthService-CVgsgtaZ.d.ts} +8 -0
- package/dist/{DataTable-D3BK2FCN.js → DataTable-5W2HVLLV.js} +8 -8
- package/dist/{UnifiedAuthProvider-A7I23UCN.js → UnifiedAuthProvider-LUM3QLS5.js} +3 -3
- package/dist/{api-PIE4JRFS.js → api-SIZPFBFX.js} +5 -3
- package/dist/{audit-65VNHEV2.js → audit-5JI5T3SL.js} +2 -2
- package/dist/{chunk-3J5N2T2N.js → chunk-2BIDKXQU.js} +113 -116
- package/dist/chunk-2BIDKXQU.js.map +1 -0
- package/dist/{chunk-AWK2FAUN.js → chunk-ACYQNYHB.js} +7 -7
- package/dist/{chunk-D6MEKC27.js → chunk-EFVQBYFN.js} +2 -2
- package/dist/{chunk-EZ64QG2I.js → chunk-I5YM5BGS.js} +2 -2
- package/dist/{chunk-Q7APDV6H.js → chunk-IWJYNWXN.js} +13 -5
- package/dist/chunk-IWJYNWXN.js.map +1 -0
- package/dist/{chunk-YFMENCR4.js → chunk-JE2GFA3O.js} +3 -3
- package/dist/{chunk-AUXS7XSO.js → chunk-MW73E7SP.js} +35 -11
- package/dist/chunk-MW73E7SP.js.map +1 -0
- package/dist/{chunk-XRSP3H52.js → chunk-PXXS26G5.js} +57 -23
- package/dist/chunk-PXXS26G5.js.map +1 -0
- package/dist/{chunk-HGZSO43Y.js → chunk-TD4BXGPE.js} +4 -4
- package/dist/{chunk-EYSXQ756.js → chunk-TDFBX7KJ.js} +2 -2
- package/dist/{chunk-HADXAZT3.js → chunk-UGVU7L7N.js} +52 -90
- package/dist/chunk-UGVU7L7N.js.map +1 -0
- package/dist/{chunk-2W4WKJVF.js → chunk-X7SPKHYZ.js} +290 -255
- package/dist/chunk-X7SPKHYZ.js.map +1 -0
- package/dist/{chunk-7GBEBJLR.js → chunk-ZL45MG76.js} +45 -37
- package/dist/chunk-ZL45MG76.js.map +1 -0
- package/dist/components.js +10 -10
- package/dist/hooks.d.ts +11 -1
- package/dist/hooks.js +9 -7
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +13 -13
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +13 -8
- package/dist/rbac/index.js +9 -9
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +4 -4
- package/docs/api/classes/MissingUserContextError.md +4 -4
- package/docs/api/classes/OrganisationContextRequiredError.md +4 -4
- package/docs/api/classes/PermissionDeniedError.md +4 -4
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +8 -8
- package/docs/api/classes/RBACCache.md +8 -8
- package/docs/api/classes/RBACEngine.md +4 -4
- package/docs/api/classes/RBACError.md +4 -4
- package/docs/api/classes/RBACNotInitializedError.md +4 -4
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +27 -27
- package/docs/api/interfaces/PaceLoginPageProps.md +4 -4
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +8 -8
- package/docs/api/interfaces/RoleBasedRouterProps.md +10 -10
- package/docs/api/interfaces/RouteAccessRecord.md +10 -10
- package/docs/api/interfaces/RouteConfig.md +19 -6
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +36 -36
- package/docs/api-reference/hooks.md +8 -4
- package/docs/architecture/rpc-function-standards.md +3 -1
- package/docs/best-practices/common-patterns.md +3 -3
- package/docs/best-practices/deployment.md +10 -4
- package/docs/best-practices/performance.md +11 -3
- package/docs/core-concepts/organisations.md +8 -8
- package/docs/core-concepts/permissions.md +133 -72
- package/docs/migration/rbac-migration.md +65 -66
- package/docs/rbac/advanced-patterns.md +15 -22
- package/docs/rbac/examples.md +12 -12
- package/docs/rbac/getting-started.md +3 -3
- package/docs/rbac/troubleshooting.md +2 -1
- package/package.json +1 -1
- package/src/components/DataTable/components/__tests__/ActionButtons.test.tsx +913 -0
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +609 -0
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +434 -0
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +120 -0
- package/src/components/DataTable/components/__tests__/PaginationControls.test.tsx +519 -0
- package/src/components/DataTable/examples/__tests__/HierarchicalActionsExample.test.tsx +316 -0
- package/src/components/DataTable/examples/__tests__/InitialPageSizeExample.test.tsx +211 -0
- package/src/components/FileUpload/FileUpload.tsx +2 -8
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +193 -63
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +102 -135
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.accessibility.test.tsx +41 -2
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.integration.test.tsx +61 -6
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +71 -21
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.security.test.tsx +113 -41
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.unit.test.tsx +155 -45
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +30 -1
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +63 -5
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +156 -72
- package/src/hooks/__tests__/useRBAC.unit.test.ts +4 -38
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useFileDisplay.ts +51 -0
- package/src/hooks/usePermissionCache.test.ts +112 -68
- package/src/hooks/usePermissionCache.ts +55 -15
- package/src/rbac/README.md +81 -39
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +3 -3
- package/src/rbac/__tests__/engine.comprehensive.test.ts +15 -6
- package/src/rbac/__tests__/rbac-core.test.tsx +1 -1
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +57 -4
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +3 -2
- package/src/rbac/adapters.tsx +4 -4
- package/src/rbac/api.test.ts +37 -13
- package/src/rbac/api.ts +25 -8
- package/src/rbac/audit.test.ts +2 -2
- package/src/rbac/audit.ts +14 -5
- package/src/rbac/cache.test.ts +12 -0
- package/src/rbac/cache.ts +29 -9
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +14 -14
- package/src/rbac/components/NavigationProvider.test.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +4 -3
- package/src/rbac/components/PagePermissionProvider.test.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +19 -15
- package/src/rbac/components/RoleBasedRouter.tsx +16 -9
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +123 -107
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +1 -1
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +121 -103
- package/src/rbac/docs/event-based-apps.md +6 -6
- package/src/rbac/engine.ts +12 -2
- package/src/rbac/hooks/useCan.test.ts +29 -2
- package/src/rbac/hooks/usePermissions.test.ts +25 -25
- package/src/rbac/hooks/usePermissions.ts +47 -23
- package/src/rbac/hooks/useRBAC.simple.test.ts +1 -8
- package/src/rbac/hooks/useRBAC.test.ts +3 -40
- package/src/rbac/hooks/useRBAC.ts +0 -55
- package/src/rbac/hooks/useResolvedScope.ts +23 -31
- package/src/rbac/permissions.test.ts +11 -7
- package/src/rbac/security.test.ts +2 -2
- package/src/rbac/security.ts +22 -7
- package/src/rbac/types.test.ts +2 -2
- package/src/rbac/types.ts +1 -2
- package/src/services/EventService.ts +41 -13
- package/src/services/__tests__/EventService.test.ts +25 -4
- package/src/services/interfaces/IEventService.ts +1 -0
- package/src/utils/file-reference.ts +9 -0
- package/dist/chunk-2W4WKJVF.js.map +0 -1
- package/dist/chunk-3J5N2T2N.js.map +0 -1
- package/dist/chunk-7GBEBJLR.js.map +0 -1
- package/dist/chunk-AUXS7XSO.js.map +0 -1
- package/dist/chunk-HADXAZT3.js.map +0 -1
- package/dist/chunk-Q7APDV6H.js.map +0 -1
- package/dist/chunk-XRSP3H52.js.map +0 -1
- /package/dist/{DataTable-D3BK2FCN.js.map → DataTable-5W2HVLLV.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-A7I23UCN.js.map → UnifiedAuthProvider-LUM3QLS5.js.map} +0 -0
- /package/dist/{api-PIE4JRFS.js.map → api-SIZPFBFX.js.map} +0 -0
- /package/dist/{audit-65VNHEV2.js.map → audit-5JI5T3SL.js.map} +0 -0
- /package/dist/{chunk-AWK2FAUN.js.map → chunk-ACYQNYHB.js.map} +0 -0
- /package/dist/{chunk-D6MEKC27.js.map → chunk-EFVQBYFN.js.map} +0 -0
- /package/dist/{chunk-EZ64QG2I.js.map → chunk-I5YM5BGS.js.map} +0 -0
- /package/dist/{chunk-YFMENCR4.js.map → chunk-JE2GFA3O.js.map} +0 -0
- /package/dist/{chunk-HGZSO43Y.js.map → chunk-TD4BXGPE.js.map} +0 -0
- /package/dist/{chunk-EYSXQ756.js.map → chunk-TDFBX7KJ.js.map} +0 -0
|
@@ -6,15 +6,73 @@ import { useRBAC } from '../../rbac/hooks/useRBAC';
|
|
|
6
6
|
// Mock the useRBAC hook
|
|
7
7
|
vi.mock('../../rbac/hooks/useRBAC');
|
|
8
8
|
|
|
9
|
+
// Mock isPermittedCached from RBAC API (used by usePermissionCache)
|
|
10
|
+
vi.mock('../../rbac/api', async () => {
|
|
11
|
+
const actual = await vi.importActual('../../rbac/api');
|
|
12
|
+
return {
|
|
13
|
+
...actual,
|
|
14
|
+
isPermittedCached: vi.fn().mockResolvedValue(true),
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Mock useOrganisations hook (required by usePermissionCache)
|
|
19
|
+
const mockOrganisationContext = {
|
|
20
|
+
selectedOrganisation: {
|
|
21
|
+
id: 'test-org-id',
|
|
22
|
+
name: 'Test Organisation',
|
|
23
|
+
display_name: 'Test Organisation',
|
|
24
|
+
slug: 'test-org',
|
|
25
|
+
description: 'Test organisation',
|
|
26
|
+
subscription_tier: 'basic',
|
|
27
|
+
settings: {},
|
|
28
|
+
is_active: true,
|
|
29
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
30
|
+
updated_at: '2023-01-01T00:00:00Z',
|
|
31
|
+
},
|
|
32
|
+
organisations: [],
|
|
33
|
+
userMemberships: [],
|
|
34
|
+
isLoading: false,
|
|
35
|
+
error: null,
|
|
36
|
+
hasValidOrganisationContext: true,
|
|
37
|
+
setSelectedOrganisation: vi.fn(),
|
|
38
|
+
switchOrganisation: vi.fn().mockResolvedValue(undefined),
|
|
39
|
+
getUserRole: vi.fn().mockReturnValue('member'),
|
|
40
|
+
validateOrganisationAccess: vi.fn().mockReturnValue(true),
|
|
41
|
+
ensureOrganisationContext: vi.fn().mockReturnValue(null),
|
|
42
|
+
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
43
|
+
getPrimaryOrganisation: vi.fn().mockReturnValue(null),
|
|
44
|
+
isOrganisationSecure: vi.fn().mockReturnValue(true),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
vi.mock('../useOrganisations', () => ({
|
|
48
|
+
useOrganisations: () => mockOrganisationContext,
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
// Mock useEvents hook (optional - wrapped in try/catch in usePermissionCache)
|
|
52
|
+
vi.mock('../useEvents', () => ({
|
|
53
|
+
useEvents: vi.fn(() => ({
|
|
54
|
+
selectedEvent: { event_id: 'event-123' },
|
|
55
|
+
events: [],
|
|
56
|
+
isLoading: false,
|
|
57
|
+
error: null,
|
|
58
|
+
})),
|
|
59
|
+
}));
|
|
60
|
+
|
|
9
61
|
const mockUseRBAC = vi.mocked(useRBAC);
|
|
10
62
|
|
|
11
63
|
// Import after mocking
|
|
12
64
|
import { usePermissionCache } from '../usePermissionCache';
|
|
65
|
+
import { isPermittedCached } from '../../rbac/api';
|
|
66
|
+
const mockIsPermittedCached = vi.mocked(isPermittedCached);
|
|
13
67
|
|
|
14
68
|
describe('usePermissionCache - Simple Tests', () => {
|
|
15
69
|
beforeEach(() => {
|
|
16
70
|
vi.clearAllMocks();
|
|
17
71
|
|
|
72
|
+
// Reset isPermittedCached mock
|
|
73
|
+
mockIsPermittedCached.mockClear();
|
|
74
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
75
|
+
|
|
18
76
|
// Mock the useRBAC hook return value
|
|
19
77
|
mockUseRBAC.mockReturnValue({
|
|
20
78
|
hasPermission: vi.fn().mockResolvedValue(true),
|
|
@@ -40,9 +98,9 @@ describe('usePermissionCache - Simple Tests', () => {
|
|
|
40
98
|
});
|
|
41
99
|
|
|
42
100
|
it('should check permission and return result', async () => {
|
|
43
|
-
|
|
101
|
+
mockIsPermittedCached.mockResolvedValueOnce(true);
|
|
44
102
|
mockUseRBAC.mockReturnValue({
|
|
45
|
-
hasPermission:
|
|
103
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
46
104
|
user: { id: 'test-user-id' }
|
|
47
105
|
});
|
|
48
106
|
|
|
@@ -53,15 +111,15 @@ describe('usePermissionCache - Simple Tests', () => {
|
|
|
53
111
|
const permission = await result.current.checkPermission('read', 'dashboard');
|
|
54
112
|
|
|
55
113
|
expect(permission).toBe(true);
|
|
56
|
-
expect(
|
|
114
|
+
expect(mockIsPermittedCached).toHaveBeenCalled();
|
|
57
115
|
});
|
|
58
116
|
|
|
59
117
|
it('should handle permission check errors gracefully', async () => {
|
|
60
118
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
61
|
-
|
|
119
|
+
mockIsPermittedCached.mockRejectedValueOnce(new Error('Database error'));
|
|
62
120
|
|
|
63
121
|
mockUseRBAC.mockReturnValue({
|
|
64
|
-
hasPermission:
|
|
122
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
65
123
|
user: { id: 'test-user-id' }
|
|
66
124
|
});
|
|
67
125
|
|
|
@@ -6,15 +6,86 @@ import { useRBAC } from '../../rbac/hooks/useRBAC';
|
|
|
6
6
|
// Mock the useRBAC hook
|
|
7
7
|
vi.mock('../../rbac/hooks/useRBAC');
|
|
8
8
|
|
|
9
|
+
// Mock useUnifiedAuth (required by usePermissionCache for user context)
|
|
10
|
+
vi.mock('../../providers/UnifiedAuthProvider', () => ({
|
|
11
|
+
useUnifiedAuth: vi.fn(() => ({
|
|
12
|
+
user: { id: 'test-user-id' },
|
|
13
|
+
session: null,
|
|
14
|
+
appName: 'test-app',
|
|
15
|
+
selectedOrganisationId: undefined,
|
|
16
|
+
selectedEventId: undefined
|
|
17
|
+
}))
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
// Mock isPermittedCached from RBAC API (used by usePermissionCache)
|
|
21
|
+
vi.mock('../../rbac/api', async () => {
|
|
22
|
+
const actual = await vi.importActual('../../rbac/api');
|
|
23
|
+
return {
|
|
24
|
+
...actual,
|
|
25
|
+
isPermittedCached: vi.fn().mockResolvedValue(true),
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Mock useOrganisations hook (required by usePermissionCache)
|
|
30
|
+
const mockOrganisationContext = {
|
|
31
|
+
selectedOrganisation: {
|
|
32
|
+
id: 'test-org-id',
|
|
33
|
+
name: 'Test Organisation',
|
|
34
|
+
display_name: 'Test Organisation',
|
|
35
|
+
slug: 'test-org',
|
|
36
|
+
description: 'Test organisation',
|
|
37
|
+
subscription_tier: 'basic',
|
|
38
|
+
settings: {},
|
|
39
|
+
is_active: true,
|
|
40
|
+
created_at: '2023-01-01T00:00:00Z',
|
|
41
|
+
updated_at: '2023-01-01T00:00:00Z',
|
|
42
|
+
},
|
|
43
|
+
organisations: [],
|
|
44
|
+
userMemberships: [],
|
|
45
|
+
isLoading: false,
|
|
46
|
+
error: null,
|
|
47
|
+
hasValidOrganisationContext: true,
|
|
48
|
+
setSelectedOrganisation: vi.fn(),
|
|
49
|
+
switchOrganisation: vi.fn().mockResolvedValue(undefined),
|
|
50
|
+
getUserRole: vi.fn().mockReturnValue('member'),
|
|
51
|
+
validateOrganisationAccess: vi.fn().mockReturnValue(true),
|
|
52
|
+
ensureOrganisationContext: vi.fn().mockReturnValue(null),
|
|
53
|
+
refreshOrganisations: vi.fn().mockResolvedValue(undefined),
|
|
54
|
+
getPrimaryOrganisation: vi.fn().mockReturnValue(null),
|
|
55
|
+
isOrganisationSecure: vi.fn().mockReturnValue(true),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
vi.mock('../useOrganisations', () => ({
|
|
59
|
+
useOrganisations: () => mockOrganisationContext,
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
// Mock useEvents hook (optional - wrapped in try/catch in usePermissionCache)
|
|
63
|
+
vi.mock('../useEvents', () => ({
|
|
64
|
+
useEvents: vi.fn(() => ({
|
|
65
|
+
selectedEvent: { event_id: 'event-123' },
|
|
66
|
+
events: [],
|
|
67
|
+
isLoading: false,
|
|
68
|
+
error: null,
|
|
69
|
+
})),
|
|
70
|
+
}));
|
|
71
|
+
|
|
9
72
|
const mockUseRBAC = vi.mocked(useRBAC);
|
|
10
73
|
|
|
11
74
|
// Import after mocking
|
|
12
75
|
import { usePermissionCache } from '../usePermissionCache';
|
|
76
|
+
import { isPermittedCached } from '../../rbac/api';
|
|
77
|
+
import { useUnifiedAuth } from '../../providers/UnifiedAuthProvider';
|
|
78
|
+
const mockIsPermittedCached = vi.mocked(isPermittedCached);
|
|
79
|
+
const mockUseUnifiedAuth = vi.mocked(useUnifiedAuth);
|
|
13
80
|
|
|
14
81
|
describe('usePermissionCache', () => {
|
|
15
82
|
beforeEach(() => {
|
|
16
83
|
vi.clearAllMocks();
|
|
17
84
|
|
|
85
|
+
// Reset isPermittedCached mock
|
|
86
|
+
mockIsPermittedCached.mockClear();
|
|
87
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
88
|
+
|
|
18
89
|
// Default mock implementation
|
|
19
90
|
mockUseRBAC.mockReturnValue({
|
|
20
91
|
hasPermission: vi.fn().mockResolvedValue(true),
|
|
@@ -59,9 +130,9 @@ describe('usePermissionCache', () => {
|
|
|
59
130
|
|
|
60
131
|
describe('Single Permission Checking', () => {
|
|
61
132
|
it('checks permission and caches result', async () => {
|
|
62
|
-
|
|
133
|
+
mockIsPermittedCached.mockResolvedValueOnce(true);
|
|
63
134
|
mockUseRBAC.mockReturnValue({
|
|
64
|
-
hasPermission:
|
|
135
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
65
136
|
user: { id: 'test-user-id' }
|
|
66
137
|
});
|
|
67
138
|
|
|
@@ -72,13 +143,13 @@ describe('usePermissionCache', () => {
|
|
|
72
143
|
const permission = await result.current.checkPermission('read', 'dashboard');
|
|
73
144
|
|
|
74
145
|
expect(permission).toBe(true);
|
|
75
|
-
expect(
|
|
146
|
+
expect(mockIsPermittedCached).toHaveBeenCalled();
|
|
76
147
|
});
|
|
77
148
|
|
|
78
149
|
it('returns cached result for subsequent calls', async () => {
|
|
79
|
-
|
|
150
|
+
mockIsPermittedCached.mockResolvedValueOnce(true);
|
|
80
151
|
mockUseRBAC.mockReturnValue({
|
|
81
|
-
hasPermission:
|
|
152
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
82
153
|
user: { id: 'test-user-id' }
|
|
83
154
|
});
|
|
84
155
|
|
|
@@ -92,13 +163,14 @@ describe('usePermissionCache', () => {
|
|
|
92
163
|
// Second call should use cache
|
|
93
164
|
await result.current.checkPermission('read', 'dashboard');
|
|
94
165
|
|
|
95
|
-
|
|
166
|
+
// Should only call isPermittedCached once (second call uses cache)
|
|
167
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(1);
|
|
96
168
|
});
|
|
97
169
|
|
|
98
170
|
it('respects custom TTL', async () => {
|
|
99
|
-
|
|
171
|
+
mockIsPermittedCached.mockResolvedValueOnce(true);
|
|
100
172
|
mockUseRBAC.mockReturnValue({
|
|
101
|
-
hasPermission:
|
|
173
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
102
174
|
user: { id: 'test-user-id' }
|
|
103
175
|
});
|
|
104
176
|
|
|
@@ -108,15 +180,15 @@ describe('usePermissionCache', () => {
|
|
|
108
180
|
|
|
109
181
|
await result.current.checkPermission('read', 'dashboard', 2000);
|
|
110
182
|
|
|
111
|
-
expect(
|
|
183
|
+
expect(mockIsPermittedCached).toHaveBeenCalled();
|
|
112
184
|
});
|
|
113
185
|
|
|
114
186
|
it('handles permission check errors gracefully', async () => {
|
|
115
187
|
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
116
|
-
|
|
188
|
+
mockIsPermittedCached.mockRejectedValueOnce(new Error('Database error'));
|
|
117
189
|
|
|
118
190
|
mockUseRBAC.mockReturnValue({
|
|
119
|
-
hasPermission:
|
|
191
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
120
192
|
user: { id: 'test-user-id' }
|
|
121
193
|
});
|
|
122
194
|
|
|
@@ -135,14 +207,14 @@ describe('usePermissionCache', () => {
|
|
|
135
207
|
|
|
136
208
|
describe('Multiple Permission Checking', () => {
|
|
137
209
|
it('checks multiple permissions efficiently', async () => {
|
|
138
|
-
|
|
210
|
+
mockIsPermittedCached
|
|
139
211
|
.mockResolvedValueOnce(true)
|
|
140
212
|
.mockResolvedValueOnce(false)
|
|
141
213
|
.mockResolvedValueOnce(true)
|
|
142
214
|
.mockResolvedValueOnce(false);
|
|
143
215
|
|
|
144
216
|
mockUseRBAC.mockReturnValue({
|
|
145
|
-
hasPermission:
|
|
217
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
146
218
|
user: { id: 'test-user-id' }
|
|
147
219
|
});
|
|
148
220
|
|
|
@@ -176,12 +248,12 @@ describe('usePermissionCache', () => {
|
|
|
176
248
|
});
|
|
177
249
|
|
|
178
250
|
it('uses cached results for multiple permission checks', async () => {
|
|
179
|
-
|
|
251
|
+
mockIsPermittedCached
|
|
180
252
|
.mockResolvedValueOnce(true)
|
|
181
253
|
.mockResolvedValueOnce(false);
|
|
182
254
|
|
|
183
255
|
mockUseRBAC.mockReturnValue({
|
|
184
|
-
hasPermission:
|
|
256
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
185
257
|
user: { id: 'test-user-id' }
|
|
186
258
|
});
|
|
187
259
|
|
|
@@ -202,17 +274,19 @@ describe('usePermissionCache', () => {
|
|
|
202
274
|
['create', 'dashboard']
|
|
203
275
|
]);
|
|
204
276
|
|
|
205
|
-
|
|
277
|
+
// Should only call isPermittedCached twice (once per permission, second call uses cache)
|
|
278
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(2);
|
|
206
279
|
});
|
|
207
280
|
|
|
208
281
|
it('handles mixed cached and uncached permissions', async () => {
|
|
209
|
-
|
|
282
|
+
mockIsPermittedCached
|
|
210
283
|
.mockResolvedValueOnce(true)
|
|
211
284
|
.mockResolvedValueOnce(false)
|
|
212
|
-
.mockResolvedValueOnce(true)
|
|
285
|
+
.mockResolvedValueOnce(true)
|
|
286
|
+
.mockResolvedValueOnce(false);
|
|
213
287
|
|
|
214
288
|
mockUseRBAC.mockReturnValue({
|
|
215
|
-
hasPermission:
|
|
289
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
216
290
|
user: { id: 'test-user-id' }
|
|
217
291
|
});
|
|
218
292
|
|
|
@@ -245,9 +319,9 @@ describe('usePermissionCache', () => {
|
|
|
245
319
|
|
|
246
320
|
describe('Cache Management', () => {
|
|
247
321
|
it('enforces maximum cache size', async () => {
|
|
248
|
-
|
|
322
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
249
323
|
mockUseRBAC.mockReturnValue({
|
|
250
|
-
hasPermission:
|
|
324
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
251
325
|
user: { id: 'test-user-id' }
|
|
252
326
|
});
|
|
253
327
|
|
|
@@ -268,9 +342,9 @@ describe('usePermissionCache', () => {
|
|
|
268
342
|
});
|
|
269
343
|
|
|
270
344
|
it('invalidates cache entries', async () => {
|
|
271
|
-
|
|
345
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
272
346
|
mockUseRBAC.mockReturnValue({
|
|
273
|
-
hasPermission:
|
|
347
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
274
348
|
user: { id: 'test-user-id' }
|
|
275
349
|
});
|
|
276
350
|
|
|
@@ -289,13 +363,14 @@ describe('usePermissionCache', () => {
|
|
|
289
363
|
await result.current.checkPermission('read', 'dashboard');
|
|
290
364
|
await result.current.checkPermission('create', 'dashboard');
|
|
291
365
|
|
|
292
|
-
|
|
366
|
+
// Should be called 4 times (2 initial + 2 after invalidation)
|
|
367
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(4);
|
|
293
368
|
});
|
|
294
369
|
|
|
295
370
|
it('invalidates cache entries by pattern', async () => {
|
|
296
|
-
|
|
371
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
297
372
|
mockUseRBAC.mockReturnValue({
|
|
298
|
-
hasPermission:
|
|
373
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
299
374
|
user: { id: 'test-user-id' }
|
|
300
375
|
});
|
|
301
376
|
|
|
@@ -316,15 +391,16 @@ describe('usePermissionCache', () => {
|
|
|
316
391
|
await result.current.checkPermission('read', 'admin');
|
|
317
392
|
|
|
318
393
|
// dashboard should be called again, admin should use cache
|
|
319
|
-
|
|
394
|
+
// 3 initial calls + 1 for dashboard after invalidation = 4 total
|
|
395
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(4);
|
|
320
396
|
});
|
|
321
397
|
|
|
322
398
|
it('cleans up expired cache entries', async () => {
|
|
323
399
|
vi.useFakeTimers();
|
|
324
400
|
|
|
325
|
-
|
|
401
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
326
402
|
mockUseRBAC.mockReturnValue({
|
|
327
|
-
hasPermission:
|
|
403
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
328
404
|
user: { id: 'test-user-id' }
|
|
329
405
|
});
|
|
330
406
|
|
|
@@ -348,12 +424,12 @@ describe('usePermissionCache', () => {
|
|
|
348
424
|
|
|
349
425
|
describe('Debug Information', () => {
|
|
350
426
|
it('provides accurate debug information', async () => {
|
|
351
|
-
|
|
427
|
+
mockIsPermittedCached
|
|
352
428
|
.mockResolvedValueOnce(true)
|
|
353
429
|
.mockResolvedValueOnce(false);
|
|
354
430
|
|
|
355
431
|
mockUseRBAC.mockReturnValue({
|
|
356
|
-
hasPermission:
|
|
432
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
357
433
|
user: { id: 'test-user-id' }
|
|
358
434
|
});
|
|
359
435
|
|
|
@@ -378,9 +454,9 @@ describe('usePermissionCache', () => {
|
|
|
378
454
|
});
|
|
379
455
|
|
|
380
456
|
it('tracks response times accurately', async () => {
|
|
381
|
-
|
|
457
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
382
458
|
mockUseRBAC.mockReturnValue({
|
|
383
|
-
hasPermission:
|
|
459
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
384
460
|
user: { id: 'test-user-id' }
|
|
385
461
|
});
|
|
386
462
|
|
|
@@ -400,9 +476,17 @@ describe('usePermissionCache', () => {
|
|
|
400
476
|
|
|
401
477
|
describe('Audit Trail', () => {
|
|
402
478
|
it('records audit trail when enabled', async () => {
|
|
403
|
-
|
|
479
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
480
|
+
// The hook uses useUnifiedAuth for user, not useRBAC
|
|
481
|
+
mockUseUnifiedAuth.mockReturnValue({
|
|
482
|
+
user: { id: 'test-user-id' },
|
|
483
|
+
session: null,
|
|
484
|
+
appName: 'test-app',
|
|
485
|
+
selectedOrganisationId: undefined,
|
|
486
|
+
selectedEventId: undefined
|
|
487
|
+
} as any);
|
|
404
488
|
mockUseRBAC.mockReturnValue({
|
|
405
|
-
hasPermission:
|
|
489
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
406
490
|
user: { id: 'test-user-id' }
|
|
407
491
|
});
|
|
408
492
|
|
|
@@ -416,28 +500,28 @@ describe('usePermissionCache', () => {
|
|
|
416
500
|
const auditTrail = result.current.getAuditTrail();
|
|
417
501
|
|
|
418
502
|
expect(auditTrail).toHaveLength(2);
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
503
|
+
// Check all properties including timestamp
|
|
504
|
+
expect(auditTrail[0]).toHaveProperty('timestamp');
|
|
505
|
+
expect(auditTrail[0]).toHaveProperty('operation', 'read');
|
|
506
|
+
expect(auditTrail[0]).toHaveProperty('pageId', 'dashboard');
|
|
507
|
+
expect(auditTrail[0]).toHaveProperty('result', true);
|
|
508
|
+
expect(auditTrail[0]).toHaveProperty('cached', false);
|
|
509
|
+
expect(auditTrail[0]).toHaveProperty('userId', 'test-user-id');
|
|
510
|
+
expect(typeof auditTrail[0].timestamp).toBe('number');
|
|
511
|
+
|
|
512
|
+
expect(auditTrail[1]).toHaveProperty('timestamp');
|
|
513
|
+
expect(auditTrail[1]).toHaveProperty('operation', 'create');
|
|
514
|
+
expect(auditTrail[1]).toHaveProperty('pageId', 'admin');
|
|
515
|
+
expect(auditTrail[1]).toHaveProperty('result', true);
|
|
516
|
+
expect(auditTrail[1]).toHaveProperty('cached', false);
|
|
517
|
+
expect(auditTrail[1]).toHaveProperty('userId', 'test-user-id');
|
|
518
|
+
expect(typeof auditTrail[1].timestamp).toBe('number');
|
|
435
519
|
});
|
|
436
520
|
|
|
437
521
|
it('does not record audit trail when disabled', async () => {
|
|
438
|
-
|
|
522
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
439
523
|
mockUseRBAC.mockReturnValue({
|
|
440
|
-
hasPermission:
|
|
524
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
441
525
|
user: { id: 'test-user-id' }
|
|
442
526
|
});
|
|
443
527
|
|
|
@@ -452,9 +536,9 @@ describe('usePermissionCache', () => {
|
|
|
452
536
|
});
|
|
453
537
|
|
|
454
538
|
it('limits audit trail size', async () => {
|
|
455
|
-
|
|
539
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
456
540
|
mockUseRBAC.mockReturnValue({
|
|
457
|
-
hasPermission:
|
|
541
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
458
542
|
user: { id: 'test-user-id' }
|
|
459
543
|
});
|
|
460
544
|
|
|
@@ -476,9 +560,9 @@ describe('usePermissionCache', () => {
|
|
|
476
560
|
it('logs permission checks when enabled', async () => {
|
|
477
561
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
478
562
|
|
|
479
|
-
|
|
563
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
480
564
|
mockUseRBAC.mockReturnValue({
|
|
481
|
-
hasPermission:
|
|
565
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
482
566
|
user: { id: 'test-user-id' }
|
|
483
567
|
});
|
|
484
568
|
|
|
@@ -498,9 +582,9 @@ describe('usePermissionCache', () => {
|
|
|
498
582
|
it('does not log when disabled', async () => {
|
|
499
583
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
500
584
|
|
|
501
|
-
|
|
585
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
502
586
|
mockUseRBAC.mockReturnValue({
|
|
503
|
-
hasPermission:
|
|
587
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
504
588
|
user: { id: 'test-user-id' }
|
|
505
589
|
});
|
|
506
590
|
|
|
@@ -520,14 +604,14 @@ describe('usePermissionCache', () => {
|
|
|
520
604
|
|
|
521
605
|
describe('Cached Permissions', () => {
|
|
522
606
|
it('returns cached permissions for a page', async () => {
|
|
523
|
-
|
|
607
|
+
mockIsPermittedCached
|
|
524
608
|
.mockResolvedValueOnce(true)
|
|
525
609
|
.mockResolvedValueOnce(false)
|
|
526
610
|
.mockResolvedValueOnce(true)
|
|
527
611
|
.mockResolvedValueOnce(false);
|
|
528
612
|
|
|
529
613
|
mockUseRBAC.mockReturnValue({
|
|
530
|
-
hasPermission:
|
|
614
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
531
615
|
user: { id: 'test-user-id' }
|
|
532
616
|
});
|
|
533
617
|
|
|
@@ -554,9 +638,9 @@ describe('usePermissionCache', () => {
|
|
|
554
638
|
});
|
|
555
639
|
|
|
556
640
|
it('returns empty array for uncached page', async () => {
|
|
557
|
-
|
|
641
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
558
642
|
mockUseRBAC.mockReturnValue({
|
|
559
|
-
hasPermission:
|
|
643
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
560
644
|
user: { id: 'test-user-id' }
|
|
561
645
|
});
|
|
562
646
|
|
|
@@ -572,9 +656,9 @@ describe('usePermissionCache', () => {
|
|
|
572
656
|
|
|
573
657
|
describe('Edge Cases', () => {
|
|
574
658
|
it('handles concurrent permission checks', async () => {
|
|
575
|
-
|
|
659
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
576
660
|
mockUseRBAC.mockReturnValue({
|
|
577
|
-
hasPermission:
|
|
661
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
578
662
|
user: { id: 'test-user-id' }
|
|
579
663
|
});
|
|
580
664
|
|
|
@@ -592,13 +676,13 @@ describe('usePermissionCache', () => {
|
|
|
592
676
|
const results = await Promise.all(promises);
|
|
593
677
|
|
|
594
678
|
expect(results).toEqual([true, true, true]);
|
|
595
|
-
expect(
|
|
679
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(1); // Should only call once due to caching
|
|
596
680
|
});
|
|
597
681
|
|
|
598
682
|
it('handles rapid cache invalidation', async () => {
|
|
599
|
-
|
|
683
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
600
684
|
mockUseRBAC.mockReturnValue({
|
|
601
|
-
hasPermission:
|
|
685
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
602
686
|
user: { id: 'test-user-id' }
|
|
603
687
|
});
|
|
604
688
|
|
|
@@ -610,13 +694,13 @@ describe('usePermissionCache', () => {
|
|
|
610
694
|
result.current.invalidateCache();
|
|
611
695
|
await result.current.checkPermission('read', 'dashboard');
|
|
612
696
|
|
|
613
|
-
expect(
|
|
697
|
+
expect(mockIsPermittedCached).toHaveBeenCalledTimes(2);
|
|
614
698
|
});
|
|
615
699
|
|
|
616
700
|
it('handles empty permission arrays', async () => {
|
|
617
|
-
|
|
701
|
+
mockIsPermittedCached.mockResolvedValue(true);
|
|
618
702
|
mockUseRBAC.mockReturnValue({
|
|
619
|
-
hasPermission:
|
|
703
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
620
704
|
user: { id: 'test-user-id' }
|
|
621
705
|
});
|
|
622
706
|
|
|
@@ -627,7 +711,7 @@ describe('usePermissionCache', () => {
|
|
|
627
711
|
const permissions = await result.current.checkMultiplePermissions([]);
|
|
628
712
|
|
|
629
713
|
expect(permissions).toHaveLength(0);
|
|
630
|
-
expect(
|
|
714
|
+
expect(mockIsPermittedCached).not.toHaveBeenCalled();
|
|
631
715
|
});
|
|
632
716
|
});
|
|
633
717
|
});
|
|
@@ -139,45 +139,11 @@ describe('useRBAC (unit)', () => {
|
|
|
139
139
|
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
140
140
|
|
|
141
141
|
expect(result.current.isSuperAdmin).toBe(true);
|
|
142
|
-
|
|
142
|
+
// Note: Permission checking tests moved to useCan.test.ts
|
|
143
143
|
});
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
user: { id: 'user-1' },
|
|
148
|
-
session: { access_token: 'token' },
|
|
149
|
-
appName: 'console',
|
|
150
|
-
});
|
|
151
|
-
mockUseOrganisations.mockReturnValue({ selectedOrganisation: { id: 'org-1' } });
|
|
152
|
-
mockGetPermissionMap.mockResolvedValue({ 'read:users': true, 'write:users': false });
|
|
153
|
-
|
|
154
|
-
const { result } = renderHook(() => useRBAC());
|
|
155
|
-
|
|
156
|
-
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
157
|
-
|
|
158
|
-
mockIsPermittedCached.mockClear();
|
|
159
|
-
expect(await result.current.hasPermission('read:users')).toBe(true);
|
|
160
|
-
expect(await result.current.hasPermission('write:users')).toBe(false);
|
|
161
|
-
expect(mockIsPermittedCached).not.toHaveBeenCalled();
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('falls back to engine when permission not cached', async () => {
|
|
165
|
-
mockUseUnifiedAuth.mockReturnValue({
|
|
166
|
-
user: { id: 'user-1' },
|
|
167
|
-
session: { access_token: 'token' },
|
|
168
|
-
appName: 'console',
|
|
169
|
-
});
|
|
170
|
-
mockUseOrganisations.mockReturnValue({ selectedOrganisation: { id: 'org-1' } });
|
|
171
|
-
mockGetPermissionMap.mockResolvedValue({});
|
|
172
|
-
mockIsPermittedCached.mockResolvedValue(true);
|
|
173
|
-
|
|
174
|
-
const { result } = renderHook(() => useRBAC());
|
|
175
|
-
|
|
176
|
-
await waitFor(() => expect(result.current.isLoading).toBe(false));
|
|
177
|
-
|
|
178
|
-
expect(await result.current.hasPermission('read', 'users')).toBe(true);
|
|
179
|
-
expect(mockIsPermittedCached).toHaveBeenCalled();
|
|
180
|
-
});
|
|
145
|
+
// Note: Permission checking tests have been moved to useCan.test.ts
|
|
146
|
+
// useRBAC now focuses on role information and context, not permission checks
|
|
181
147
|
|
|
182
148
|
it('returns true for hasGlobalPermission when organisation admin', async () => {
|
|
183
149
|
mockUseUnifiedAuth.mockReturnValue({
|
|
@@ -213,6 +179,6 @@ describe('useRBAC (unit)', () => {
|
|
|
213
179
|
|
|
214
180
|
expect(result.current.error).toBeInstanceOf(Error);
|
|
215
181
|
expect(result.current.globalRole).toBeNull();
|
|
216
|
-
|
|
182
|
+
// Note: Permission checking moved to useCan hook
|
|
217
183
|
});
|
|
218
184
|
});
|
package/src/hooks/index.ts
CHANGED
|
@@ -52,7 +52,7 @@ export { useDataTablePerformance } from './useDataTablePerformance';
|
|
|
52
52
|
export type { UseDataTablePerformanceOptions, UseDataTablePerformanceReturn } from './useDataTablePerformance';
|
|
53
53
|
|
|
54
54
|
// === FILE DISPLAY HOOKS ===
|
|
55
|
-
export { useFileDisplay, clearFileDisplayCache, getFileDisplayCacheStats } from './useFileDisplay';
|
|
55
|
+
export { useFileDisplay, clearFileDisplayCache, getFileDisplayCacheStats, invalidateFileDisplayCache } from './useFileDisplay';
|
|
56
56
|
export type { UseFileDisplayReturn, UseFileDisplayOptions } from './useFileDisplay';
|
|
57
57
|
|
|
58
58
|
// === PUBLIC DATA ACCESS HOOKS ===
|