@jmruthers/pace-core 0.5.54 → 0.5.55
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/README.md +0 -4
- package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
- package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
- package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
- package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
- package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
- package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
- package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
- package/dist/chunk-5BSLGBYI.js.map +1 -0
- package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
- package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
- package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
- package/dist/chunk-AJ2KMES7.js.map +1 -0
- package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
- package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
- package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
- package/dist/chunk-B2WTCLCV.js.map +1 -0
- package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
- package/dist/chunk-D7ARGIA3.js.map +1 -0
- package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
- package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
- package/dist/chunk-KJDPSM64.js.map +1 -0
- package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
- package/dist/chunk-KLPVOPRI.js.map +1 -0
- package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
- package/dist/chunk-MPQDF75X.js.map +1 -0
- package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
- package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
- package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
- package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
- package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
- package/dist/chunk-STT7INZR.js.map +1 -0
- package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
- package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
- package/dist/chunk-UQE2Y64H.js.map +1 -0
- package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
- package/dist/chunk-W66AZIOH.js.map +1 -0
- package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
- package/dist/chunk-YNUBMSMV.js.map +1 -0
- package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
- package/dist/chunk-ZOD2ZY6X.js.map +1 -0
- package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
- package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
- package/dist/components.d.ts +22 -899
- package/dist/components.js +436 -3118
- package/dist/components.js.map +1 -1
- package/dist/file-reference-9xUOnwyt.d.ts +70 -0
- package/dist/hooks.d.ts +2 -2
- package/dist/hooks.js +10 -10
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +49 -9
- package/dist/index.js +190 -25
- package/dist/index.js.map +1 -1
- package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +5 -5
- package/dist/rbac/index.d.ts +65 -46
- package/dist/rbac/index.js +10 -12
- package/dist/styles/core.css +0 -125
- package/dist/types.d.ts +2 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +44 -13
- package/dist/utils.js.map +1 -1
- package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
- package/docs/INDEX.md +7 -5
- package/docs/README.md +0 -1
- package/docs/api/README.md +0 -4
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +2 -2
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +12 -12
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +6 -6
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +281 -0
- 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/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/EventContextType.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +1 -1
- package/docs/api/interfaces/EventProviderProps.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.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 +2 -2
- 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 +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- 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 +4 -4
- 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/RBACContextType.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACProviderProps.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +2 -2
- package/docs/api/interfaces/RouteConfig.md +2 -2
- 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/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/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.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 +204 -200
- package/docs/api-reference/components.md +141 -163
- package/docs/api-reference/hooks.md +347 -0
- package/docs/core-concepts/rbac-system.md +69 -16
- package/docs/getting-started/examples/basic-auth-app.md +0 -1
- package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
- package/docs/implementation-guides/file-upload-storage.md +733 -0
- package/docs/implementation-guides/inactivity-tracking.md +779 -0
- package/docs/implementation-guides/organisation-security.md +748 -0
- package/docs/implementation-guides/public-pages-advanced.md +1022 -0
- package/docs/migration/MIGRATION_GUIDE.md +684 -0
- package/docs/migration/README.md +13 -2
- package/docs/migration/rbac-migration.md +73 -0
- package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
- package/docs/style-guide.md +269 -1
- package/package.json +1 -1
- package/src/__tests__/TESTING_GUIDELINES.md +331 -18
- package/src/__tests__/helpers/supabaseMock.ts +99 -0
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
- package/src/__tests__/shared.ts +6 -0
- package/src/components/DataTable/components/ActionButtons.tsx +2 -2
- package/src/components/DataTable/components/DataTableCore.tsx +2 -2
- package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
- package/src/components/DataTable/utils/debugTools.ts +2 -2
- package/src/components/Dialog/Dialog.test.tsx +12 -2
- package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
- package/src/components/FileDisplay.tsx +233 -0
- package/src/components/FileUpload.tsx +176 -0
- package/src/components/Footer/Footer.test.tsx +7 -7
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
- package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
- package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
- package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
- package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
- package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
- package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
- package/src/components/Select/Select.test.tsx +50 -15
- package/src/components/SuperAdminGuard.tsx +2 -2
- package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
- package/src/components/index.ts +0 -183
- package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
- package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
- package/src/hooks/public/usePublicEvent.ts +2 -2
- package/src/hooks/useAppConfig.ts +3 -3
- package/src/hooks/useComponentPerformance.ts +1 -1
- package/src/hooks/useDataTablePerformance.ts +1 -1
- package/src/hooks/useFileReference.ts +232 -0
- package/src/hooks/useOrganisationPermissions.test.ts +254 -344
- package/src/hooks/useOrganisationPermissions.ts +15 -7
- package/src/hooks/useOrganisationSecurity.test.ts +390 -402
- package/src/hooks/usePerformanceMonitor.ts +1 -1
- package/src/hooks/usePermissionCache.test.ts +264 -395
- package/src/hooks/usePermissionCache.ts +34 -4
- package/src/hooks/useSecureDataAccess.test.ts +486 -0
- package/src/hooks/useSecureDataAccess.ts +4 -1
- package/src/providers/InactivityProvider.tsx +2 -2
- package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
- package/src/providers/OrganisationProvider.test.tsx +168 -0
- package/src/providers/OrganisationProvider.tsx +18 -31
- package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
- package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
- package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
- package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
- package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
- package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
- package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
- package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
- package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
- package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
- package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
- package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
- package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
- package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
- package/src/rbac/api.test.ts +6 -6
- package/src/rbac/api.ts +2 -2
- package/src/rbac/audit.test.ts +485 -0
- package/src/rbac/audit.ts +7 -1
- package/src/rbac/cache-invalidation.ts +318 -0
- package/src/rbac/cache.test.ts +286 -0
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
- package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
- package/src/rbac/components/NavigationProvider.test.tsx +449 -0
- package/src/rbac/components/PagePermissionGuard.tsx +4 -4
- package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
- package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
- package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
- package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
- package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
- package/src/rbac/config.test.ts +410 -0
- package/src/rbac/engine.test.simple.ts +237 -0
- package/src/rbac/engine.test.ts +233 -0
- package/src/rbac/engine.ts +37 -41
- package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
- package/src/rbac/examples/EventBasedApp.tsx +4 -4
- package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
- package/src/rbac/hooks/useRBAC.test.ts +207 -455
- package/src/rbac/hooks/useRBAC.ts +30 -22
- package/src/rbac/permissions.test.ts +128 -0
- package/src/rbac/permissions.ts +56 -141
- package/src/rbac/providers/RBACProvider.tsx +1 -1
- package/src/rbac/secureClient.test.ts +444 -0
- package/src/rbac/security.test.ts +390 -0
- package/src/rbac/security.ts +1 -1
- package/src/rbac/types.test.ts +382 -0
- package/src/rbac/types.ts +2 -2
- package/src/styles/core.css +0 -125
- package/src/types/file-reference.ts +77 -0
- package/src/types/rbac-functions.ts +290 -0
- package/src/types/supabase.ts +10 -28
- package/src/types/unified.ts +4 -1
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
- package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
- package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
- package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
- package/src/utils/appConfig.ts +1 -1
- package/src/utils/appIdResolver.test.ts +503 -0
- package/src/utils/appIdResolver.ts +1 -1
- package/src/utils/appNameResolver.test.ts +494 -0
- package/src/utils/appNameResolver.ts +3 -2
- package/src/utils/bundleAnalysis.ts +3 -3
- package/src/utils/debugLogger.ts +1 -1
- package/src/utils/file-reference.ts +263 -0
- package/src/utils/formatDate.test.ts +2 -2
- package/src/utils/organisationContext.test.ts +340 -0
- package/src/utils/organisationContext.ts +19 -6
- package/src/utils/performanceBudgets.ts +2 -2
- package/src/utils/permissionUtils.test.ts +393 -0
- package/src/utils/permissionUtils.ts +5 -2
- package/src/utils/secureDataAccess.test.ts +715 -0
- package/src/utils/secureDataAccess.ts +21 -5
- package/src/utils/sessionTracking.ts +34 -4
- package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
- package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
- package/src/utils/storage/helpers.ts +20 -25
- package/src/utils/storage/index.ts +29 -1
- package/src/vite-env.d.ts +17 -0
- package/dist/chunk-22KLBHPS.js.map +0 -1
- package/dist/chunk-7BNPOCLL.js.map +0 -1
- package/dist/chunk-BC3S53OZ.js.map +0 -1
- package/dist/chunk-GWSBHC4J.js.map +0 -1
- package/dist/chunk-MYP2EGHX.js.map +0 -1
- package/dist/chunk-MZBUOP4P.js.map +0 -1
- package/dist/chunk-NYUJ4FJR.js.map +0 -1
- package/dist/chunk-NZ655MWE.js.map +0 -1
- package/dist/chunk-SS3E6QLB.js.map +0 -1
- package/dist/chunk-TRIZ7IB7.js.map +0 -1
- package/dist/chunk-WJARTBCT.js.map +0 -1
- package/dist/chunk-YDJW5XTN.js.map +0 -1
- package/docs/print-components/README.md +0 -258
- package/docs/print-components/api-reference.md +0 -636
- package/docs/print-components/examples/README.md +0 -204
- package/docs/print-components/examples/basic-report.tsx +0 -92
- package/docs/print-components/examples/card-catalog.tsx +0 -149
- package/docs/print-components/examples/cover-page-report.tsx +0 -163
- package/docs/print-components/quick-start.md +0 -363
- package/src/components/PrintButton/PrintButton.tsx +0 -321
- package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
- package/src/components/PrintButton/PrintToolbar.tsx +0 -94
- package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
- package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
- package/src/components/PrintButton/index.ts +0 -33
- package/src/components/PrintButton/types.ts +0 -173
- package/src/components/PrintCard/PrintCard.tsx +0 -154
- package/src/components/PrintCard/PrintCardContent.tsx +0 -57
- package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
- package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
- package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
- package/src/components/PrintCard/PrintCardImage.tsx +0 -81
- package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
- package/src/components/PrintCard/index.ts +0 -34
- package/src/components/PrintCard/types.ts +0 -171
- package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
- package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
- package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
- package/src/components/PrintDataTable/index.ts +0 -25
- package/src/components/PrintDataTable/types.ts +0 -67
- package/src/components/PrintFooter/PrintFooter.tsx +0 -183
- package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
- package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
- package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
- package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
- package/src/components/PrintFooter/index.ts +0 -30
- package/src/components/PrintFooter/types.ts +0 -149
- package/src/components/PrintGrid/PrintGrid.tsx +0 -180
- package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
- package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
- package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
- package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
- package/src/components/PrintGrid/index.ts +0 -31
- package/src/components/PrintGrid/types.ts +0 -159
- package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
- package/src/components/PrintHeader/PrintHeader.tsx +0 -150
- package/src/components/PrintHeader/index.ts +0 -17
- package/src/components/PrintHeader/types.ts +0 -42
- package/src/components/PrintLayout/PrintLayout.tsx +0 -122
- package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
- package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
- package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
- package/src/components/PrintLayout/index.ts +0 -19
- package/src/components/PrintLayout/types.ts +0 -37
- package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
- package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
- package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
- package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
- package/src/components/PrintPageBreak/index.ts +0 -23
- package/src/components/PrintPageBreak/types.ts +0 -94
- package/src/components/PrintSection/PrintColumn.tsx +0 -104
- package/src/components/PrintSection/PrintDivider.tsx +0 -101
- package/src/components/PrintSection/PrintSection.tsx +0 -129
- package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
- package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
- package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
- package/src/components/PrintSection/index.ts +0 -33
- package/src/components/PrintSection/types.ts +0 -155
- package/src/components/PrintText/PrintText.tsx +0 -116
- package/src/components/PrintText/index.ts +0 -16
- package/src/components/PrintText/types.ts +0 -24
- package/src/rbac/__tests__/integration.test.tsx +0 -218
- package/src/utils/print/PrintDataProcessor.ts +0 -390
- package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
- package/src/utils/print/index.ts +0 -29
- package/src/utils/print/types.ts +0 -196
- package/src/utils/print/usePrintOptimization.ts +0 -272
- /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
- /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
- /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
- /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
- /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
- /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
|
@@ -1,542 +1,411 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
3
|
-
* @
|
|
2
|
+
* @file Permission Cache Hook Tests
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module Hooks/usePermissionCache
|
|
5
|
+
* @since 0.3.0
|
|
6
|
+
*
|
|
7
|
+
* Comprehensive tests for the usePermissionCache hook covering all critical functionality.
|
|
4
8
|
*/
|
|
5
9
|
|
|
6
|
-
import { renderHook, act } from '@testing-library/react';
|
|
7
|
-
import { describe, it, expect,
|
|
10
|
+
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
11
|
+
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
8
12
|
import { usePermissionCache } from './usePermissionCache';
|
|
9
13
|
import { useRBAC } from '../rbac/hooks/useRBAC';
|
|
10
|
-
import type { Operation } from '../rbac/types';
|
|
11
14
|
|
|
12
|
-
// Mock the
|
|
15
|
+
// Mock the RBAC hook
|
|
13
16
|
vi.mock('../rbac/hooks/useRBAC', () => ({
|
|
14
17
|
useRBAC: vi.fn()
|
|
15
18
|
}));
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
organisationRole: null,
|
|
29
|
-
eventAppRole: null,
|
|
30
|
-
hasPermission: vi.fn(),
|
|
31
|
-
hasGlobalPermission: vi.fn(),
|
|
32
|
-
isSuperAdmin: false,
|
|
33
|
-
isOrgAdmin: false,
|
|
34
|
-
isEventAdmin: false,
|
|
35
|
-
canManageOrganisation: false,
|
|
36
|
-
canManageEvent: false,
|
|
37
|
-
isLoading: false,
|
|
38
|
-
error: null
|
|
39
|
-
};
|
|
20
|
+
describe('usePermissionCache', () => {
|
|
21
|
+
const mockUseRBAC = vi.mocked(useRBAC);
|
|
22
|
+
|
|
23
|
+
// Create stable mock objects to prevent unnecessary re-renders
|
|
24
|
+
const stableMockRBAC = {
|
|
25
|
+
hasPermission: vi.fn().mockResolvedValue(true),
|
|
26
|
+
isSuperAdmin: vi.fn().mockResolvedValue(false),
|
|
27
|
+
isOrgAdmin: vi.fn().mockResolvedValue(false),
|
|
28
|
+
isEventAdmin: vi.fn().mockResolvedValue(false),
|
|
29
|
+
// Add other required properties
|
|
30
|
+
} as any;
|
|
40
31
|
|
|
41
32
|
beforeEach(() => {
|
|
42
33
|
vi.clearAllMocks();
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
// Reset the mock functions to return the expected values
|
|
35
|
+
stableMockRBAC.hasPermission.mockResolvedValue(true);
|
|
36
|
+
stableMockRBAC.isSuperAdmin.mockResolvedValue(false);
|
|
37
|
+
stableMockRBAC.isOrgAdmin.mockResolvedValue(false);
|
|
38
|
+
stableMockRBAC.isEventAdmin.mockResolvedValue(false);
|
|
39
|
+
mockUseRBAC.mockReturnValue(stableMockRBAC);
|
|
45
40
|
});
|
|
46
41
|
|
|
47
42
|
afterEach(() => {
|
|
48
|
-
vi.
|
|
43
|
+
vi.restoreAllMocks();
|
|
49
44
|
});
|
|
50
45
|
|
|
51
|
-
describe('Initialization', () => {
|
|
46
|
+
describe('Hook Initialization', () => {
|
|
52
47
|
it('initializes with default configuration', () => {
|
|
53
48
|
const { result } = renderHook(() => usePermissionCache());
|
|
54
49
|
|
|
55
|
-
expect(result.current
|
|
56
|
-
expect(result.current.
|
|
57
|
-
expect(result.current.
|
|
58
|
-
expect(result.current.
|
|
50
|
+
expect(result.current).toBeDefined();
|
|
51
|
+
expect(result.current.checkPermission).toBeInstanceOf(Function);
|
|
52
|
+
expect(result.current.checkMultiplePermissions).toBeInstanceOf(Function);
|
|
53
|
+
expect(result.current.getCachedPermissions).toBeInstanceOf(Function);
|
|
54
|
+
expect(result.current.invalidateCache).toBeInstanceOf(Function);
|
|
59
55
|
expect(result.current.getDebugInfo).toBeDefined();
|
|
60
|
-
expect(result.current.getAuditTrail).toBeDefined();
|
|
61
56
|
});
|
|
62
57
|
|
|
63
58
|
it('initializes with custom configuration', () => {
|
|
64
59
|
const customConfig = {
|
|
65
60
|
defaultTTL: 10000,
|
|
66
61
|
maxCacheSize: 500,
|
|
67
|
-
enableLogging:
|
|
62
|
+
enableLogging: true,
|
|
68
63
|
enableAuditTrail: false
|
|
69
64
|
};
|
|
70
65
|
|
|
71
66
|
const { result } = renderHook(() => usePermissionCache(customConfig));
|
|
72
67
|
|
|
73
|
-
expect(result.current
|
|
74
|
-
expect(result.current.checkMultiplePermissions).toBeDefined();
|
|
75
|
-
expect(result.current.getCachedPermissions).toBeDefined();
|
|
76
|
-
expect(result.current.invalidateCache).toBeDefined();
|
|
68
|
+
expect(result.current).toBeDefined();
|
|
77
69
|
expect(result.current.getDebugInfo).toBeDefined();
|
|
78
|
-
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('depends on useRBAC hook', () => {
|
|
73
|
+
renderHook(() => usePermissionCache());
|
|
74
|
+
expect(mockUseRBAC).toHaveBeenCalled();
|
|
79
75
|
});
|
|
80
76
|
});
|
|
81
77
|
|
|
82
78
|
describe('Permission Checking', () => {
|
|
83
|
-
it('checks permission and caches result', async () => {
|
|
84
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
85
|
-
|
|
79
|
+
it('checks single permission and caches result', async () => {
|
|
86
80
|
const { result } = renderHook(() => usePermissionCache());
|
|
87
81
|
|
|
88
|
-
const
|
|
82
|
+
const hasPermission = await result.current.checkPermission('read', 'users');
|
|
83
|
+
expect(hasPermission).toBe(true);
|
|
84
|
+
expect(mockUseRBAC().hasPermission).toHaveBeenCalledWith('read', 'users');
|
|
89
85
|
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
// Check that result is cached
|
|
87
|
+
const cachedResult = await result.current.checkPermission('read', 'users');
|
|
88
|
+
expect(cachedResult).toBe(true);
|
|
92
89
|
});
|
|
93
90
|
|
|
94
|
-
it('
|
|
95
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
96
|
-
|
|
91
|
+
it('checks multiple permissions in batch', async () => {
|
|
97
92
|
const { result } = renderHook(() => usePermissionCache());
|
|
98
93
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
const permissions = [
|
|
95
|
+
['read', 'users'],
|
|
96
|
+
['create', 'users'],
|
|
97
|
+
['update', 'users'],
|
|
98
|
+
['delete', 'users']
|
|
99
|
+
];
|
|
103
100
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
expect(
|
|
107
|
-
expect(mockRBACContext.hasPermission).toHaveBeenCalledTimes(1);
|
|
101
|
+
const results = await result.current.checkMultiplePermissions(permissions);
|
|
102
|
+
expect(results).toHaveLength(4);
|
|
103
|
+
expect(results.every(r => r.hasPermission)).toBe(true);
|
|
108
104
|
});
|
|
109
105
|
|
|
110
|
-
it('handles permission check errors', async () => {
|
|
111
|
-
|
|
106
|
+
it('handles permission check errors gracefully', async () => {
|
|
107
|
+
mockUseRBAC.mockReturnValue({
|
|
108
|
+
hasPermission: vi.fn().mockRejectedValue(new Error('Permission check failed')),
|
|
109
|
+
isSuperAdmin: vi.fn().mockResolvedValue(false),
|
|
110
|
+
isOrgAdmin: vi.fn().mockResolvedValue(false),
|
|
111
|
+
isEventAdmin: vi.fn().mockResolvedValue(false),
|
|
112
|
+
// Add other required properties
|
|
113
|
+
} as any);
|
|
112
114
|
|
|
113
115
|
const { result } = renderHook(() => usePermissionCache());
|
|
114
116
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
expect(permission).toBe(false);
|
|
117
|
+
const hasPermission = await result.current.checkPermission('read', 'users');
|
|
118
|
+
expect(hasPermission).toBe(false);
|
|
118
119
|
});
|
|
119
120
|
|
|
120
|
-
it('handles
|
|
121
|
-
|
|
122
|
-
.mockResolvedValueOnce(true)
|
|
123
|
-
.mockResolvedValueOnce(false)
|
|
124
|
-
.mockResolvedValueOnce(true);
|
|
121
|
+
it('handles missing RBAC hook gracefully', () => {
|
|
122
|
+
mockUseRBAC.mockReturnValue(null as any);
|
|
125
123
|
|
|
126
124
|
const { result } = renderHook(() => usePermissionCache());
|
|
127
125
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const updatePermission = await result.current.checkPermission('update', 'users');
|
|
131
|
-
|
|
132
|
-
expect(readPermission).toBe(true);
|
|
133
|
-
expect(createPermission).toBe(false);
|
|
134
|
-
expect(updatePermission).toBe(true);
|
|
126
|
+
expect(result.current).toBeDefined();
|
|
127
|
+
expect(result.current.checkPermission).toBeInstanceOf(Function);
|
|
135
128
|
});
|
|
136
129
|
});
|
|
137
130
|
|
|
138
|
-
describe('
|
|
139
|
-
it('
|
|
140
|
-
mockRBACContext.hasPermission
|
|
141
|
-
.mockResolvedValueOnce(true)
|
|
142
|
-
.mockResolvedValueOnce(false)
|
|
143
|
-
.mockResolvedValueOnce(true);
|
|
144
|
-
|
|
131
|
+
describe('Cache Management', () => {
|
|
132
|
+
it('caches permission results with TTL', async () => {
|
|
145
133
|
const { result } = renderHook(() => usePermissionCache());
|
|
146
134
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
expect(results).toHaveLength(3);
|
|
156
|
-
expect(results[0].hasPermission).toBe(true);
|
|
157
|
-
expect(results[1].hasPermission).toBe(false);
|
|
158
|
-
expect(results[2].hasPermission).toBe(true);
|
|
159
|
-
expect(results[0].cached).toBe(false);
|
|
160
|
-
expect(results[1].cached).toBe(false);
|
|
161
|
-
expect(results[2].cached).toBe(false);
|
|
135
|
+
// First check
|
|
136
|
+
await result.current.checkPermission('read', 'users');
|
|
137
|
+
|
|
138
|
+
// Second check should use cache
|
|
139
|
+
await result.current.checkPermission('read', 'users');
|
|
140
|
+
|
|
141
|
+
expect(mockUseRBAC().hasPermission).toHaveBeenCalledTimes(1);
|
|
162
142
|
});
|
|
163
143
|
|
|
164
|
-
it('
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// First check individual permission
|
|
170
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
144
|
+
it('expires cached results after TTL', async () => {
|
|
145
|
+
const { result } = renderHook(() => usePermissionCache({
|
|
146
|
+
defaultTTL: 100 // 100ms TTL
|
|
147
|
+
}));
|
|
171
148
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
expect(
|
|
182
|
-
expect(mockRBACContext.hasPermission).toHaveBeenCalledTimes(2); // Once for each unique permission
|
|
149
|
+
// First check
|
|
150
|
+
await result.current.checkPermission('read', 'users');
|
|
151
|
+
|
|
152
|
+
// Wait for TTL to expire
|
|
153
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
154
|
+
|
|
155
|
+
// Second check should not use cache
|
|
156
|
+
await result.current.checkPermission('read', 'users');
|
|
157
|
+
|
|
158
|
+
expect(mockUseRBAC().hasPermission).toHaveBeenCalledTimes(2);
|
|
183
159
|
});
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
describe('Cache Management', () => {
|
|
187
|
-
it('invalidates all cache', async () => {
|
|
188
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
189
160
|
|
|
161
|
+
it('invalidates cache correctly', async () => {
|
|
190
162
|
const { result } = renderHook(() => usePermissionCache());
|
|
191
163
|
|
|
192
|
-
// Check
|
|
193
|
-
await result.current.checkPermission('read', '
|
|
194
|
-
|
|
164
|
+
// Check permission
|
|
165
|
+
await result.current.checkPermission('read', 'users');
|
|
166
|
+
|
|
195
167
|
// Invalidate cache
|
|
196
168
|
result.current.invalidateCache();
|
|
197
|
-
|
|
198
|
-
// Check
|
|
199
|
-
await result.current.checkPermission('read', '
|
|
200
|
-
|
|
201
|
-
expect(
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it('invalidates cache by pattern', async () => {
|
|
205
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
206
|
-
|
|
207
|
-
const { result } = renderHook(() => usePermissionCache());
|
|
208
|
-
|
|
209
|
-
// Check multiple permissions
|
|
210
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
211
|
-
await result.current.checkPermission('read', 'events');
|
|
212
|
-
await result.current.checkPermission('create', 'dashboard');
|
|
213
|
-
|
|
214
|
-
// Invalidate only 'read' permissions
|
|
215
|
-
result.current.invalidateCache('read:.*');
|
|
216
|
-
|
|
217
|
-
// Check permissions again
|
|
218
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
219
|
-
await result.current.checkPermission('create', 'dashboard');
|
|
220
|
-
|
|
221
|
-
// 'read' permissions should be fresh, 'create' should be cached
|
|
222
|
-
expect(mockRBACContext.hasPermission).toHaveBeenCalledTimes(4); // 2 fresh + 2 cached
|
|
169
|
+
|
|
170
|
+
// Check again - should not use cache
|
|
171
|
+
await result.current.checkPermission('read', 'users');
|
|
172
|
+
|
|
173
|
+
expect(mockUseRBAC().hasPermission).toHaveBeenCalledTimes(2);
|
|
223
174
|
});
|
|
224
175
|
|
|
225
|
-
it('
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
230
|
-
|
|
231
|
-
// Add more permissions than max cache size
|
|
232
|
-
await result.current.checkPermission('read', 'page1');
|
|
233
|
-
await result.current.checkPermission('read', 'page2');
|
|
234
|
-
await result.current.checkPermission('read', 'page3');
|
|
176
|
+
it('respects max cache size', async () => {
|
|
177
|
+
const { result } = renderHook(() => usePermissionCache({
|
|
178
|
+
maxCacheSize: 2
|
|
179
|
+
}));
|
|
235
180
|
|
|
236
|
-
//
|
|
181
|
+
// Fill cache beyond max size
|
|
182
|
+
await result.current.checkPermission('read', 'users');
|
|
183
|
+
await result.current.checkPermission('create', 'users');
|
|
184
|
+
await result.current.checkPermission('update', 'users');
|
|
185
|
+
await result.current.checkPermission('delete', 'users');
|
|
186
|
+
|
|
187
|
+
// Check that cache size is respected
|
|
237
188
|
const debugInfo = result.current.getDebugInfo();
|
|
238
189
|
expect(debugInfo.cacheSize).toBeLessThanOrEqual(2);
|
|
239
190
|
});
|
|
240
191
|
});
|
|
241
192
|
|
|
242
|
-
describe('TTL and Expiration', () => {
|
|
243
|
-
it('respects TTL for cache entries', async () => {
|
|
244
|
-
const config = { defaultTTL: 1000 }; // 1 second
|
|
245
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
246
|
-
|
|
247
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
248
|
-
|
|
249
|
-
// Check permission
|
|
250
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
251
|
-
|
|
252
|
-
// Fast-forward time past TTL
|
|
253
|
-
act(() => {
|
|
254
|
-
vi.advanceTimersByTime(1001);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
// Check same permission again - should not be cached
|
|
258
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
259
|
-
|
|
260
|
-
expect(mockRBACContext.hasPermission).toHaveBeenCalledTimes(2);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('respects custom TTL for individual permissions', async () => {
|
|
264
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
265
|
-
|
|
266
|
-
const { result } = renderHook(() => usePermissionCache());
|
|
267
|
-
|
|
268
|
-
// Check permission with custom TTL
|
|
269
|
-
await result.current.checkPermission('read', 'dashboard', 500);
|
|
270
|
-
|
|
271
|
-
// Fast-forward time past custom TTL but before default TTL
|
|
272
|
-
act(() => {
|
|
273
|
-
vi.advanceTimersByTime(501);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
// Check same permission again - should not be cached
|
|
277
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
278
|
-
|
|
279
|
-
expect(mockRBACContext.hasPermission).toHaveBeenCalledTimes(2);
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
describe('Concurrent Requests', () => {
|
|
284
|
-
it('handles concurrent requests for same permission', async () => {
|
|
285
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
286
|
-
|
|
287
|
-
const { result } = renderHook(() => usePermissionCache());
|
|
288
|
-
|
|
289
|
-
// Make multiple concurrent requests for same permission
|
|
290
|
-
const promises = [
|
|
291
|
-
result.current.checkPermission('read', 'dashboard'),
|
|
292
|
-
result.current.checkPermission('read', 'dashboard'),
|
|
293
|
-
result.current.checkPermission('read', 'dashboard')
|
|
294
|
-
];
|
|
295
|
-
|
|
296
|
-
const results = await Promise.all(promises);
|
|
297
|
-
|
|
298
|
-
expect(results).toEqual([true, true, true]);
|
|
299
|
-
// Should only call the underlying function once
|
|
300
|
-
expect(mockRBACContext.hasPermission).toHaveBeenCalledTimes(1);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it('handles concurrent requests for different permissions', async () => {
|
|
304
|
-
mockRBACContext.hasPermission
|
|
305
|
-
.mockResolvedValueOnce(true)
|
|
306
|
-
.mockResolvedValueOnce(false)
|
|
307
|
-
.mockResolvedValueOnce(true);
|
|
308
|
-
|
|
309
|
-
const { result } = renderHook(() => usePermissionCache());
|
|
310
|
-
|
|
311
|
-
// Make concurrent requests for different permissions
|
|
312
|
-
const promises = [
|
|
313
|
-
result.current.checkPermission('read', 'dashboard'),
|
|
314
|
-
result.current.checkPermission('create', 'events'),
|
|
315
|
-
result.current.checkPermission('update', 'users')
|
|
316
|
-
];
|
|
317
|
-
|
|
318
|
-
const results = await Promise.all(promises);
|
|
319
|
-
|
|
320
|
-
expect(results).toEqual([true, false, true]);
|
|
321
|
-
expect(mockRBACContext.hasPermission).toHaveBeenCalledTimes(3);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
193
|
describe('Debug Information', () => {
|
|
326
|
-
it('provides debug information',
|
|
327
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
328
|
-
|
|
194
|
+
it('provides debug information', () => {
|
|
329
195
|
const { result } = renderHook(() => usePermissionCache());
|
|
330
196
|
|
|
331
|
-
// Check some permissions
|
|
332
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
333
|
-
await result.current.checkPermission('create', 'events');
|
|
334
|
-
|
|
335
197
|
const debugInfo = result.current.getDebugInfo();
|
|
336
|
-
|
|
337
|
-
expect(debugInfo.cacheSize).toBe(
|
|
198
|
+
expect(debugInfo).toBeDefined();
|
|
199
|
+
expect(debugInfo.cacheSize).toBe(0);
|
|
338
200
|
expect(debugInfo.cacheHits).toBe(0);
|
|
339
|
-
expect(debugInfo.cacheMisses).toBe(
|
|
340
|
-
expect(debugInfo.totalChecks).toBe(
|
|
341
|
-
expect(debugInfo.averageResponseTime).
|
|
342
|
-
expect(debugInfo.lastInvalidation).
|
|
201
|
+
expect(debugInfo.cacheMisses).toBe(0);
|
|
202
|
+
expect(debugInfo.totalChecks).toBe(0);
|
|
203
|
+
expect(debugInfo.averageResponseTime).toBe(0);
|
|
204
|
+
expect(debugInfo.lastInvalidation).toBe(0);
|
|
343
205
|
});
|
|
344
206
|
|
|
345
207
|
it('tracks cache hits and misses', async () => {
|
|
346
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
347
|
-
|
|
348
208
|
const { result } = renderHook(() => usePermissionCache());
|
|
349
209
|
|
|
350
|
-
// First
|
|
351
|
-
await result.current.checkPermission('read', '
|
|
210
|
+
// First check - cache miss
|
|
211
|
+
await result.current.checkPermission('read', 'users');
|
|
212
|
+
|
|
213
|
+
// Second check - cache hit
|
|
214
|
+
await result.current.checkPermission('read', 'users');
|
|
352
215
|
|
|
353
|
-
// Second call - cache hit
|
|
354
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
355
|
-
|
|
356
216
|
const debugInfo = result.current.getDebugInfo();
|
|
357
|
-
|
|
358
|
-
expect(debugInfo.cacheHits).toBe(1);
|
|
359
217
|
expect(debugInfo.cacheMisses).toBe(1);
|
|
218
|
+
expect(debugInfo.cacheHits).toBe(1);
|
|
360
219
|
expect(debugInfo.totalChecks).toBe(2);
|
|
361
220
|
});
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
describe('Audit Trail', () => {
|
|
365
|
-
it('records audit trail when enabled', async () => {
|
|
366
|
-
const config = { enableAuditTrail: true };
|
|
367
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
368
|
-
|
|
369
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
370
|
-
|
|
371
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
372
221
|
|
|
373
|
-
|
|
222
|
+
it('tracks average response time', async () => {
|
|
223
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
374
224
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
225
|
+
// Mock slow permission check
|
|
226
|
+
mockUseRBAC.mockReturnValue({
|
|
227
|
+
hasPermission: vi.fn().mockImplementation(() =>
|
|
228
|
+
new Promise(resolve => setTimeout(() => resolve(true), 100))
|
|
229
|
+
),
|
|
230
|
+
isSuperAdmin: vi.fn().mockResolvedValue(false),
|
|
231
|
+
isOrgAdmin: vi.fn().mockResolvedValue(false),
|
|
232
|
+
isEventAdmin: vi.fn().mockResolvedValue(false),
|
|
233
|
+
// Add other required properties
|
|
234
|
+
} as any);
|
|
235
|
+
|
|
236
|
+
await result.current.checkPermission('read', 'users');
|
|
237
|
+
|
|
238
|
+
const debugInfo = result.current.getDebugInfo();
|
|
239
|
+
expect(debugInfo.averageResponseTime).toBeGreaterThan(0);
|
|
381
240
|
});
|
|
241
|
+
});
|
|
382
242
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
243
|
+
describe('Audit Trail', () => {
|
|
244
|
+
it('logs permission checks when audit trail is enabled', async () => {
|
|
245
|
+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
388
246
|
|
|
389
|
-
|
|
247
|
+
const { result } = renderHook(() => usePermissionCache({
|
|
248
|
+
enableAuditTrail: true,
|
|
249
|
+
enableLogging: true
|
|
250
|
+
}));
|
|
390
251
|
|
|
391
|
-
|
|
252
|
+
await result.current.checkPermission('read', 'users');
|
|
253
|
+
|
|
254
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
255
|
+
expect.stringContaining('[PermissionCache] read:users = true (fresh)')
|
|
256
|
+
);
|
|
392
257
|
|
|
393
|
-
|
|
258
|
+
consoleSpy.mockRestore();
|
|
394
259
|
});
|
|
395
260
|
|
|
396
|
-
it('
|
|
397
|
-
const
|
|
398
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
399
|
-
|
|
400
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
261
|
+
it('does not log when audit trail is disabled', async () => {
|
|
262
|
+
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
401
263
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
264
|
+
const { result } = renderHook(() => usePermissionCache({
|
|
265
|
+
enableAuditTrail: false,
|
|
266
|
+
enableLogging: false
|
|
267
|
+
}));
|
|
406
268
|
|
|
407
|
-
|
|
269
|
+
await result.current.checkPermission('read', 'users');
|
|
270
|
+
|
|
271
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
408
272
|
|
|
409
|
-
|
|
273
|
+
consoleSpy.mockRestore();
|
|
410
274
|
});
|
|
411
275
|
});
|
|
412
276
|
|
|
413
|
-
describe('
|
|
414
|
-
it('
|
|
415
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
416
|
-
|
|
277
|
+
describe('Performance Optimization', () => {
|
|
278
|
+
it('batches multiple permission checks efficiently', async () => {
|
|
417
279
|
const { result } = renderHook(() => usePermissionCache());
|
|
418
280
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
281
|
+
const permissions = [
|
|
282
|
+
['read', 'users'],
|
|
283
|
+
['create', 'users'],
|
|
284
|
+
['update', 'users'],
|
|
285
|
+
['delete', 'users']
|
|
286
|
+
];
|
|
423
287
|
|
|
424
|
-
const
|
|
288
|
+
const startTime = Date.now();
|
|
289
|
+
await result.current.checkMultiplePermissions(permissions);
|
|
290
|
+
const endTime = Date.now();
|
|
425
291
|
|
|
426
|
-
|
|
427
|
-
expect(
|
|
292
|
+
// Should complete quickly due to batching
|
|
293
|
+
expect(endTime - startTime).toBeLessThan(1000);
|
|
428
294
|
});
|
|
429
295
|
|
|
430
|
-
it('
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
const
|
|
296
|
+
it('uses memoization to prevent unnecessary re-renders', () => {
|
|
297
|
+
let renderCount = 0;
|
|
298
|
+
|
|
299
|
+
const TestComponent = () => {
|
|
300
|
+
renderCount++;
|
|
301
|
+
const cache = usePermissionCache();
|
|
302
|
+
// Test that the same functions are returned (memoized)
|
|
303
|
+
expect(cache.checkPermission).toBeInstanceOf(Function);
|
|
304
|
+
expect(cache.getDebugInfo).toBeInstanceOf(Function);
|
|
305
|
+
return null;
|
|
306
|
+
};
|
|
434
307
|
|
|
435
|
-
|
|
308
|
+
const { rerender } = renderHook(() => TestComponent());
|
|
309
|
+
const initialRenderCount = renderCount;
|
|
310
|
+
|
|
311
|
+
// Re-render with same props
|
|
312
|
+
rerender();
|
|
313
|
+
|
|
314
|
+
// The hook will re-render, but the functions should be memoized
|
|
315
|
+
expect(renderCount).toBe(initialRenderCount + 1);
|
|
436
316
|
});
|
|
437
317
|
});
|
|
438
318
|
|
|
439
319
|
describe('Error Handling', () => {
|
|
440
|
-
it('handles
|
|
441
|
-
|
|
320
|
+
it('handles RBAC hook errors gracefully', async () => {
|
|
321
|
+
mockUseRBAC.mockReturnValue({
|
|
322
|
+
hasPermission: vi.fn().mockRejectedValue(new Error('RBAC error')),
|
|
323
|
+
isSuperAdmin: vi.fn().mockResolvedValue(false),
|
|
324
|
+
isOrgAdmin: vi.fn().mockResolvedValue(false),
|
|
325
|
+
isEventAdmin: vi.fn().mockResolvedValue(false),
|
|
326
|
+
// Add other required properties
|
|
327
|
+
} as any);
|
|
442
328
|
|
|
443
329
|
const { result } = renderHook(() => usePermissionCache());
|
|
444
330
|
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
expect(permission).toBe(false);
|
|
331
|
+
const hasPermission = await result.current.checkPermission('read', 'users');
|
|
332
|
+
expect(hasPermission).toBe(false);
|
|
448
333
|
});
|
|
449
334
|
|
|
450
|
-
it('handles
|
|
451
|
-
mockRBACContext.hasPermission.mockRejectedValue(new Error('Database error'));
|
|
452
|
-
|
|
335
|
+
it('handles invalid permission formats gracefully', async () => {
|
|
453
336
|
const { result } = renderHook(() => usePermissionCache());
|
|
454
337
|
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
] as Array<[Operation, string]>;
|
|
338
|
+
const hasPermission = await result.current.checkPermission('', '');
|
|
339
|
+
expect(hasPermission).toBe(false);
|
|
340
|
+
});
|
|
459
341
|
|
|
460
|
-
|
|
342
|
+
it('handles null/undefined parameters gracefully', async () => {
|
|
343
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
461
344
|
|
|
462
|
-
|
|
463
|
-
expect(
|
|
345
|
+
const hasPermission = await result.current.checkPermission(null as any, null as any);
|
|
346
|
+
expect(hasPermission).toBe(false);
|
|
464
347
|
});
|
|
465
348
|
});
|
|
466
349
|
|
|
467
|
-
describe('Configuration', () => {
|
|
468
|
-
it('uses custom TTL
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
473
|
-
|
|
474
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
475
|
-
|
|
476
|
-
// Fast-forward time less than custom TTL
|
|
477
|
-
act(() => {
|
|
478
|
-
vi.advanceTimersByTime(1000);
|
|
479
|
-
});
|
|
350
|
+
describe('Cache Configuration', () => {
|
|
351
|
+
it('uses custom TTL when provided', async () => {
|
|
352
|
+
const { result } = renderHook(() => usePermissionCache({
|
|
353
|
+
defaultTTL: 200
|
|
354
|
+
}));
|
|
480
355
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
356
|
+
await result.current.checkPermission('read', 'users');
|
|
357
|
+
|
|
358
|
+
// Wait for custom TTL to expire
|
|
359
|
+
await new Promise(resolve => setTimeout(resolve, 250));
|
|
360
|
+
|
|
361
|
+
// Should not use cache
|
|
362
|
+
await result.current.checkPermission('read', 'users');
|
|
363
|
+
|
|
364
|
+
expect(mockUseRBAC().hasPermission).toHaveBeenCalledTimes(2);
|
|
485
365
|
});
|
|
486
366
|
|
|
487
|
-
it('
|
|
488
|
-
const
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
492
|
-
|
|
493
|
-
await result.current.checkPermission('read', 'page1');
|
|
494
|
-
await result.current.checkPermission('read', 'page2');
|
|
367
|
+
it('respects max cache size configuration', async () => {
|
|
368
|
+
const { result } = renderHook(() => usePermissionCache({
|
|
369
|
+
maxCacheSize: 1
|
|
370
|
+
}));
|
|
495
371
|
|
|
372
|
+
// Fill cache beyond max size
|
|
373
|
+
await result.current.checkPermission('read', 'users');
|
|
374
|
+
await result.current.checkPermission('create', 'users');
|
|
375
|
+
|
|
496
376
|
const debugInfo = result.current.getDebugInfo();
|
|
497
377
|
expect(debugInfo.cacheSize).toBeLessThanOrEqual(1);
|
|
498
378
|
});
|
|
499
|
-
|
|
500
|
-
it('respects logging configuration', async () => {
|
|
501
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
502
|
-
const config = { enableLogging: true };
|
|
503
|
-
mockRBACContext.hasPermission.mockResolvedValue(true);
|
|
504
|
-
|
|
505
|
-
const { result } = renderHook(() => usePermissionCache(config));
|
|
506
|
-
|
|
507
|
-
await result.current.checkPermission('read', 'dashboard');
|
|
508
|
-
|
|
509
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
510
|
-
expect.stringMatching(/\[PermissionCache\].*read:dashboard.*true.*fresh/)
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
consoleSpy.mockRestore();
|
|
514
|
-
});
|
|
515
379
|
});
|
|
516
380
|
|
|
517
|
-
describe('
|
|
518
|
-
it('
|
|
519
|
-
|
|
520
|
-
|
|
381
|
+
describe('Integration with RBAC', () => {
|
|
382
|
+
it('integrates with useRBAC hook correctly', () => {
|
|
383
|
+
renderHook(() => usePermissionCache());
|
|
384
|
+
expect(mockUseRBAC).toHaveBeenCalled();
|
|
385
|
+
});
|
|
521
386
|
|
|
522
|
-
|
|
387
|
+
it('passes correct parameters to RBAC hook', async () => {
|
|
388
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
523
389
|
|
|
524
|
-
await result.current.checkPermission('read', '
|
|
390
|
+
await result.current.checkPermission('read', 'users');
|
|
391
|
+
|
|
392
|
+
expect(mockUseRBAC().hasPermission).toHaveBeenCalledWith('read', 'users');
|
|
393
|
+
});
|
|
525
394
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
395
|
+
it('handles RBAC hook state changes', async () => {
|
|
396
|
+
const mockHasPermission = vi.fn().mockResolvedValue(true);
|
|
397
|
+
mockUseRBAC.mockReturnValue({
|
|
398
|
+
hasPermission: mockHasPermission,
|
|
399
|
+
isSuperAdmin: vi.fn().mockResolvedValue(false),
|
|
400
|
+
isOrgAdmin: vi.fn().mockResolvedValue(false),
|
|
401
|
+
isEventAdmin: vi.fn().mockResolvedValue(false),
|
|
402
|
+
// Add other required properties
|
|
403
|
+
} as any);
|
|
530
404
|
|
|
531
|
-
|
|
532
|
-
act(() => {
|
|
533
|
-
vi.advanceTimersByTime(2000);
|
|
534
|
-
});
|
|
405
|
+
const { result } = renderHook(() => usePermissionCache());
|
|
535
406
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const debugInfo = result.current.getDebugInfo();
|
|
539
|
-
expect(debugInfo.cacheSize).toBeGreaterThanOrEqual(0);
|
|
407
|
+
await result.current.checkPermission('read', 'users');
|
|
408
|
+
expect(mockHasPermission).toHaveBeenCalledWith('read', 'users');
|
|
540
409
|
});
|
|
541
410
|
});
|
|
542
|
-
});
|
|
411
|
+
});
|