@jmruthers/pace-core 0.5.190 → 0.5.191
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/{DataTable-IVYljGJ6.d.ts → DataTable-Be6dH_dR.d.ts} +1 -1
- package/dist/{DataTable-ON3IXISJ.js → DataTable-WKRZD47S.js} +6 -6
- package/dist/{PublicPageProvider-C4uxosp6.d.ts → PublicPageProvider-ULXC_u6U.d.ts} +1 -1
- package/dist/{UnifiedAuthProvider-X5NXANVI.js → UnifiedAuthProvider-FTSG5XH7.js} +3 -3
- package/dist/{api-I6UCQ5S6.js → api-IHKALJZD.js} +2 -2
- package/dist/{chunk-J2XXC7R5.js → chunk-6LTQQAT6.js} +77 -111
- package/dist/chunk-6LTQQAT6.js.map +1 -0
- package/dist/{chunk-STYK4OH2.js → chunk-6TQDD426.js} +10 -10
- package/dist/chunk-6TQDD426.js.map +1 -0
- package/dist/{chunk-DZWK57KZ.js → chunk-G37KK66H.js} +1 -1
- package/dist/{chunk-DZWK57KZ.js.map → chunk-G37KK66H.js.map} +1 -1
- package/dist/{chunk-73HSNNOQ.js → chunk-LOMZXPSN.js} +13 -13
- package/dist/{chunk-Y4BUBBHD.js → chunk-OETXORNB.js} +3 -3
- package/dist/{chunk-RUYZKXOD.js → chunk-ROXMHMY2.js} +5 -3
- package/dist/chunk-ROXMHMY2.js.map +1 -0
- package/dist/{chunk-SDMHPX3X.js → chunk-ULHIJK66.js} +56 -21
- package/dist/{chunk-SDMHPX3X.js.map → chunk-ULHIJK66.js.map} +1 -1
- package/dist/{chunk-VVBAW5A5.js → chunk-VKB2CO4Z.js} +46 -35
- package/dist/chunk-VKB2CO4Z.js.map +1 -0
- package/dist/{chunk-HQVPB5MZ.js → chunk-VRGWKHDB.js} +6 -6
- package/dist/{chunk-NIU6J6OX.js → chunk-XNYQOL3Z.js} +16 -16
- package/dist/chunk-XNYQOL3Z.js.map +1 -0
- package/dist/{chunk-4QYC5L4K.js → chunk-XYXSXPUK.js} +22 -27
- package/dist/chunk-XYXSXPUK.js.map +1 -0
- package/dist/components.d.ts +3 -3
- package/dist/components.js +8 -8
- package/dist/{database.generated-DI89OQeI.d.ts → database.generated-CzIvgcPu.d.ts} +165 -201
- package/dist/hooks.d.ts +12 -12
- package/dist/hooks.js +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +18 -23
- package/dist/index.js.map +1 -1
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +6 -6
- package/dist/{types-Bwgl--Xo.d.ts → types-CEpcvwwF.d.ts} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-DxIDS4bC.d.ts → usePublicRouteParams-TZe0gy-4.d.ts} +1 -1
- package/dist/utils.d.ts +8 -8
- package/dist/utils.js +2 -2
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/Logger.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 +1 -1
- package/docs/api/classes/RBACAuditManager.md +2 -2
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +2 -2
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +5 -5
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/enums/LogLevel.md +1 -1
- package/docs/api/enums/RBACErrorCode.md +1 -1
- package/docs/api/enums/RPCFunction.md +1 -1
- package/docs/api/interfaces/AddressFieldProps.md +1 -1
- package/docs/api/interfaces/AddressFieldRef.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/AutocompleteOptions.md +1 -1
- package/docs/api/interfaces/AvatarProps.md +1 -1
- package/docs/api/interfaces/BadgeProps.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CalendarProps.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/ComplianceResult.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/DatabaseComplianceResult.md +1 -1
- package/docs/api/interfaces/DatabaseIssue.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventAppRoleData.md +1 -1
- package/docs/api/interfaces/ExportColumn.md +1 -1
- package/docs/api/interfaces/ExportOptions.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/FormFieldProps.md +1 -1
- package/docs/api/interfaces/FormProps.md +1 -1
- package/docs/api/interfaces/GrantEventAppRoleParams.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/LoggerConfig.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 +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/ParsedAddress.md +2 -2
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/ProgressProps.md +1 -1
- package/docs/api/interfaces/ProtectedRouteProps.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/QuickFix.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
- package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
- package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
- package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +2 -2
- package/docs/api/interfaces/RBACContext.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
- package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
- package/docs/api/interfaces/RBACResult.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
- package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
- package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
- package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
- package/docs/api/interfaces/RBACRolesListParams.md +1 -1
- package/docs/api/interfaces/RBACRolesListResult.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
- package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
- package/docs/api/interfaces/ResourcePermissions.md +1 -1
- package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RoleManagementResult.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
- package/docs/api/interfaces/SetupIssue.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/TabsContentProps.md +1 -1
- package/docs/api/interfaces/TabsListProps.md +1 -1
- package/docs/api/interfaces/TabsProps.md +1 -1
- package/docs/api/interfaces/TabsTriggerProps.md +1 -1
- package/docs/api/interfaces/TextareaProps.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/UseFormDialogOptions.md +1 -1
- package/docs/api/interfaces/UseFormDialogReturn.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 +2 -2
- 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/UsePublicFileDisplayOptions.md +2 -2
- package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UseResourcePermissionsOptions.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 +16 -16
- package/docs/migration/README.md +18 -0
- package/docs/migration/database-changes-december-2025.md +767 -0
- package/docs/migration/person-scoped-profiles-migration-guide.md +472 -0
- package/package.json +1 -1
- package/src/__tests__/public-recipe-view.test.ts +10 -10
- package/src/__tests__/rls-policies.test.ts +13 -13
- package/src/components/AddressField/README.md +6 -6
- package/src/components/OrganisationSelector/OrganisationSelector.tsx +35 -15
- package/src/components/Select/Select.test.tsx +4 -1
- package/src/components/Select/Select.tsx +60 -15
- package/src/hooks/__tests__/usePermissionCache.simple.test.ts +192 -0
- package/src/hooks/__tests__/usePermissionCache.unit.test.ts +741 -0
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +703 -0
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +581 -0
- package/src/hooks/__tests__/useSecureDataAccess.unit.test.tsx +9 -8
- package/src/hooks/public/usePublicEvent.ts +8 -8
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/useFileDisplay.ts +8 -9
- package/src/hooks/useQueryCache.ts +6 -6
- package/src/hooks/useSecureDataAccess.test.ts +8 -8
- package/src/hooks/useSecureDataAccess.ts +15 -11
- package/src/providers/__tests__/OrganisationProvider.test.tsx +27 -21
- package/src/rbac/hooks/useRBAC.simple.test.ts +95 -0
- package/src/rbac/utils/__tests__/eventContext.test.ts +2 -2
- package/src/rbac/utils/__tests__/eventContext.unit.test.ts +490 -0
- package/src/rbac/utils/eventContext.ts +5 -2
- package/src/services/AuthService.ts +37 -8
- package/src/services/OrganisationService.ts +92 -139
- package/src/services/__tests__/OrganisationService.pagination.test.ts +34 -8
- package/src/services/__tests__/OrganisationService.test.ts +218 -86
- package/src/types/database.generated.ts +166 -201
- package/src/types/supabase.ts +2 -2
- package/src/utils/__tests__/secureDataAccess.unit.test.ts +3 -2
- package/src/utils/file-reference/index.ts +4 -4
- package/src/utils/google-places/googlePlacesUtils.ts +1 -1
- package/src/utils/google-places/types.ts +1 -1
- package/src/utils/request-deduplication.ts +4 -4
- package/src/utils/security/secureDataAccess.test.ts +1 -1
- package/src/utils/security/secureDataAccess.ts +7 -4
- package/src/utils/storage/README.md +1 -1
- package/dist/chunk-4QYC5L4K.js.map +0 -1
- package/dist/chunk-J2XXC7R5.js.map +0 -1
- package/dist/chunk-NIU6J6OX.js.map +0 -1
- package/dist/chunk-RUYZKXOD.js.map +0 -1
- package/dist/chunk-STYK4OH2.js.map +0 -1
- package/dist/chunk-VVBAW5A5.js.map +0 -1
- /package/dist/{DataTable-ON3IXISJ.js.map → DataTable-WKRZD47S.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-X5NXANVI.js.map → UnifiedAuthProvider-FTSG5XH7.js.map} +0 -0
- /package/dist/{api-I6UCQ5S6.js.map → api-IHKALJZD.js.map} +0 -0
- /package/dist/{chunk-73HSNNOQ.js.map → chunk-LOMZXPSN.js.map} +0 -0
- /package/dist/{chunk-Y4BUBBHD.js.map → chunk-OETXORNB.js.map} +0 -0
- /package/dist/{chunk-HQVPB5MZ.js.map → chunk-VRGWKHDB.js.map} +0 -0
|
@@ -94,7 +94,7 @@ describe('OrganisationService', () => {
|
|
|
94
94
|
// Mock the from().select() chain to return organisations
|
|
95
95
|
// The select() method should return a promise that resolves to { data, error }
|
|
96
96
|
mockSupabase.from.mockImplementation((table: string) => {
|
|
97
|
-
if (table === '
|
|
97
|
+
if (table === 'core_organisations') {
|
|
98
98
|
return {
|
|
99
99
|
select: vi.fn().mockResolvedValue({
|
|
100
100
|
data: [mockOrganisation, mockOrganisation2],
|
|
@@ -132,19 +132,28 @@ describe('OrganisationService', () => {
|
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
it('should load user organisations on initialization', async () => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
// Mock the direct query to rbac_organisation_roles with join
|
|
136
|
+
const mockQueryBuilder = {
|
|
137
|
+
select: vi.fn().mockReturnThis(),
|
|
138
|
+
eq: vi.fn().mockReturnThis(),
|
|
139
|
+
is: vi.fn().mockResolvedValue({
|
|
140
|
+
data: [{
|
|
141
|
+
id: mockMembership.id,
|
|
142
|
+
user_id: mockUser.id,
|
|
143
|
+
organisation_id: mockOrganisation.id,
|
|
144
|
+
role: mockMembership.role,
|
|
145
|
+
status: mockMembership.status,
|
|
146
|
+
granted_at: mockMembership.granted_at,
|
|
147
|
+
revoked_at: mockMembership.revoked_at,
|
|
148
|
+
core_organisations: mockOrganisation
|
|
149
|
+
}],
|
|
150
|
+
error: null
|
|
151
|
+
})
|
|
152
|
+
};
|
|
139
153
|
|
|
140
154
|
mockSupabase.from.mockImplementation((table: string) => {
|
|
141
|
-
if (table === '
|
|
142
|
-
return
|
|
143
|
-
select: vi.fn().mockResolvedValue({
|
|
144
|
-
data: [mockOrganisation],
|
|
145
|
-
error: null
|
|
146
|
-
})
|
|
147
|
-
};
|
|
155
|
+
if (table === 'rbac_organisation_roles') {
|
|
156
|
+
return mockQueryBuilder;
|
|
148
157
|
}
|
|
149
158
|
return {
|
|
150
159
|
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
@@ -153,10 +162,10 @@ describe('OrganisationService', () => {
|
|
|
153
162
|
|
|
154
163
|
await organisationService.initialize();
|
|
155
164
|
|
|
156
|
-
expect(mockSupabase.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
165
|
+
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_organisation_roles');
|
|
166
|
+
expect(mockQueryBuilder.eq).toHaveBeenCalledWith('user_id', mockUser.id);
|
|
167
|
+
expect(mockQueryBuilder.eq).toHaveBeenCalledWith('status', 'active');
|
|
168
|
+
expect(mockQueryBuilder.is).toHaveBeenCalledWith('revoked_at', null);
|
|
160
169
|
});
|
|
161
170
|
|
|
162
171
|
it('should handle missing dependencies gracefully', async () => {
|
|
@@ -209,6 +218,33 @@ describe('OrganisationService', () => {
|
|
|
209
218
|
|
|
210
219
|
it('should refresh organisations', async () => {
|
|
211
220
|
// First initialize the service to set up the basic state
|
|
221
|
+
const mockQueryBuilder = {
|
|
222
|
+
select: vi.fn().mockReturnThis(),
|
|
223
|
+
eq: vi.fn().mockReturnThis(),
|
|
224
|
+
is: vi.fn().mockResolvedValue({
|
|
225
|
+
data: [{
|
|
226
|
+
id: mockMembership.id,
|
|
227
|
+
user_id: mockUser.id,
|
|
228
|
+
organisation_id: mockOrganisation.id,
|
|
229
|
+
role: mockMembership.role,
|
|
230
|
+
status: mockMembership.status,
|
|
231
|
+
granted_at: mockMembership.granted_at,
|
|
232
|
+
revoked_at: mockMembership.revoked_at,
|
|
233
|
+
core_organisations: mockOrganisation
|
|
234
|
+
}],
|
|
235
|
+
error: null
|
|
236
|
+
})
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
240
|
+
if (table === 'rbac_organisation_roles') {
|
|
241
|
+
return mockQueryBuilder;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
|
|
212
248
|
await organisationService.initialize();
|
|
213
249
|
|
|
214
250
|
const newMembership: OrganisationMembership = {
|
|
@@ -229,44 +265,49 @@ describe('OrganisationService', () => {
|
|
|
229
265
|
updated_at: '2024-01-01T00:00:00Z'
|
|
230
266
|
};
|
|
231
267
|
|
|
232
|
-
|
|
233
|
-
|
|
268
|
+
// Update mock for refresh
|
|
269
|
+
mockQueryBuilder.is.mockResolvedValue({
|
|
270
|
+
data: [
|
|
271
|
+
{
|
|
272
|
+
id: mockMembership.id,
|
|
273
|
+
user_id: mockUser.id,
|
|
274
|
+
organisation_id: mockOrganisation.id,
|
|
275
|
+
role: mockMembership.role,
|
|
276
|
+
status: mockMembership.status,
|
|
277
|
+
granted_at: mockMembership.granted_at,
|
|
278
|
+
revoked_at: mockMembership.revoked_at,
|
|
279
|
+
core_organisations: mockOrganisation
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: mockMembership2.id,
|
|
283
|
+
user_id: mockUser.id,
|
|
284
|
+
organisation_id: mockOrganisation2.id,
|
|
285
|
+
role: mockMembership2.role,
|
|
286
|
+
status: mockMembership2.status,
|
|
287
|
+
granted_at: mockMembership2.granted_at,
|
|
288
|
+
revoked_at: mockMembership2.revoked_at,
|
|
289
|
+
core_organisations: mockOrganisation2
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: newMembership.id,
|
|
293
|
+
user_id: mockUser.id,
|
|
294
|
+
organisation_id: newOrg.id,
|
|
295
|
+
role: newMembership.role,
|
|
296
|
+
status: newMembership.status,
|
|
297
|
+
granted_at: newMembership.granted_at,
|
|
298
|
+
revoked_at: newMembership.revoked_at,
|
|
299
|
+
core_organisations: newOrg
|
|
300
|
+
}
|
|
301
|
+
],
|
|
234
302
|
error: null
|
|
235
303
|
});
|
|
236
304
|
|
|
237
|
-
mockSupabase.from.mockImplementation((table: string) => {
|
|
238
|
-
if (table === 'organisations') {
|
|
239
|
-
return {
|
|
240
|
-
select: vi.fn().mockResolvedValue({
|
|
241
|
-
data: [mockOrganisation, mockOrganisation2, newOrg],
|
|
242
|
-
error: null
|
|
243
|
-
})
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
248
|
-
};
|
|
249
|
-
});
|
|
250
|
-
|
|
251
305
|
await organisationService.refreshOrganisations();
|
|
252
306
|
|
|
253
|
-
expect(mockSupabase.
|
|
254
|
-
|
|
255
|
-
// Since the mock might not work correctly, let's verify the refresh was called
|
|
256
|
-
// and then manually set the expected state to verify the method works
|
|
257
|
-
const roleMap = new Map<string, string>();
|
|
258
|
-
roleMap.set('org-1', 'org_admin');
|
|
259
|
-
roleMap.set('org-2', 'member');
|
|
260
|
-
roleMap.set('org-3', 'member');
|
|
261
|
-
|
|
262
|
-
organisationService.setTestState(
|
|
263
|
-
[mockOrganisation, mockOrganisation2, newOrg],
|
|
264
|
-
[mockMembership, mockMembership2, newMembership],
|
|
265
|
-
roleMap,
|
|
266
|
-
mockOrganisation
|
|
267
|
-
);
|
|
307
|
+
expect(mockSupabase.from).toHaveBeenCalledWith('rbac_organisation_roles');
|
|
268
308
|
|
|
269
|
-
|
|
309
|
+
// Verify the refresh was called and state was updated
|
|
310
|
+
expect(organisationService.getOrganisations().length).toBeGreaterThanOrEqual(1);
|
|
270
311
|
});
|
|
271
312
|
|
|
272
313
|
it('should ensure organisation context', () => {
|
|
@@ -548,20 +589,53 @@ describe('OrganisationService', () => {
|
|
|
548
589
|
|
|
549
590
|
describe('Error Handling', () => {
|
|
550
591
|
it('should handle RPC errors gracefully', async () => {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
592
|
+
const mockQueryBuilder = {
|
|
593
|
+
select: vi.fn().mockReturnThis(),
|
|
594
|
+
eq: vi.fn().mockReturnThis(),
|
|
595
|
+
is: vi.fn().mockResolvedValue({
|
|
596
|
+
data: null,
|
|
597
|
+
error: { message: 'RPC error', code: 'PGRST_ERROR' }
|
|
598
|
+
})
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
602
|
+
if (table === 'rbac_organisation_roles') {
|
|
603
|
+
return mockQueryBuilder;
|
|
604
|
+
}
|
|
605
|
+
return {
|
|
606
|
+
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
607
|
+
};
|
|
554
608
|
});
|
|
555
609
|
|
|
556
610
|
await organisationService.initialize();
|
|
557
611
|
|
|
558
612
|
expect(organisationService.getError()).toBeDefined();
|
|
559
|
-
|
|
613
|
+
// The error object is thrown, so check the message property
|
|
614
|
+
const error = organisationService.getError();
|
|
615
|
+
expect(error).toBeDefined();
|
|
616
|
+
if (error && typeof error === 'object' && 'message' in error) {
|
|
617
|
+
expect((error as any).message).toBe('RPC error');
|
|
618
|
+
} else {
|
|
619
|
+
expect(String(error)).toContain('RPC error');
|
|
620
|
+
}
|
|
560
621
|
expect(organisationService.getOrganisations()).toEqual([]);
|
|
561
622
|
});
|
|
562
623
|
|
|
563
624
|
it('should handle network errors', async () => {
|
|
564
|
-
|
|
625
|
+
const mockQueryBuilder = {
|
|
626
|
+
select: vi.fn().mockReturnThis(),
|
|
627
|
+
eq: vi.fn().mockReturnThis(),
|
|
628
|
+
is: vi.fn().mockRejectedValue(new Error('Network error'))
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
632
|
+
if (table === 'rbac_organisation_roles') {
|
|
633
|
+
return mockQueryBuilder;
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
637
|
+
};
|
|
638
|
+
});
|
|
565
639
|
|
|
566
640
|
await organisationService.initialize();
|
|
567
641
|
|
|
@@ -570,9 +644,22 @@ describe('OrganisationService', () => {
|
|
|
570
644
|
});
|
|
571
645
|
|
|
572
646
|
it('should handle no memberships error', async () => {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
647
|
+
const mockQueryBuilder = {
|
|
648
|
+
select: vi.fn().mockReturnThis(),
|
|
649
|
+
eq: vi.fn().mockReturnThis(),
|
|
650
|
+
is: vi.fn().mockResolvedValue({
|
|
651
|
+
data: [],
|
|
652
|
+
error: null
|
|
653
|
+
})
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
657
|
+
if (table === 'rbac_organisation_roles') {
|
|
658
|
+
return mockQueryBuilder;
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
662
|
+
};
|
|
576
663
|
});
|
|
577
664
|
|
|
578
665
|
await organisationService.initialize();
|
|
@@ -582,31 +669,67 @@ describe('OrganisationService', () => {
|
|
|
582
669
|
});
|
|
583
670
|
|
|
584
671
|
it('should handle invalid organisation IDs', async () => {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
672
|
+
const mockQueryBuilder = {
|
|
673
|
+
select: vi.fn().mockReturnThis(),
|
|
674
|
+
eq: vi.fn().mockReturnThis(),
|
|
675
|
+
is: vi.fn().mockResolvedValue({
|
|
676
|
+
data: [{
|
|
677
|
+
id: mockMembership.id,
|
|
678
|
+
user_id: mockUser.id,
|
|
679
|
+
organisation_id: 'invalid-id', // Invalid UUID
|
|
680
|
+
role: mockMembership.role,
|
|
681
|
+
status: mockMembership.status,
|
|
682
|
+
granted_at: mockMembership.granted_at,
|
|
683
|
+
revoked_at: mockMembership.revoked_at,
|
|
684
|
+
core_organisations: null // No organisation data due to invalid ID
|
|
685
|
+
}],
|
|
686
|
+
error: null
|
|
687
|
+
})
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
691
|
+
if (table === 'rbac_organisation_roles') {
|
|
692
|
+
return mockQueryBuilder;
|
|
693
|
+
}
|
|
694
|
+
return {
|
|
695
|
+
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
696
|
+
};
|
|
588
697
|
});
|
|
589
698
|
|
|
590
699
|
await organisationService.initialize();
|
|
591
700
|
|
|
592
701
|
expect(organisationService.getError()).toBeDefined();
|
|
593
|
-
|
|
702
|
+
// The service will throw "No organisations found in role data" when organisations array is empty
|
|
703
|
+
expect(organisationService.getError()?.message).toContain('No organisations found');
|
|
594
704
|
});
|
|
595
705
|
|
|
596
706
|
it('should handle no active organisations', async () => {
|
|
597
707
|
const inactiveOrg = { ...mockOrganisation, is_active: false };
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
708
|
+
const mockQueryBuilder = {
|
|
709
|
+
select: vi.fn().mockReturnThis(),
|
|
710
|
+
eq: vi.fn().mockReturnThis(),
|
|
711
|
+
is: vi.fn().mockResolvedValue({
|
|
712
|
+
data: [{
|
|
713
|
+
id: mockMembership.id,
|
|
714
|
+
user_id: mockUser.id,
|
|
715
|
+
organisation_id: mockOrganisation.id,
|
|
716
|
+
role: mockMembership.role,
|
|
717
|
+
status: mockMembership.status,
|
|
718
|
+
granted_at: mockMembership.granted_at,
|
|
719
|
+
revoked_at: mockMembership.revoked_at,
|
|
720
|
+
core_organisations: inactiveOrg
|
|
721
|
+
}],
|
|
608
722
|
error: null
|
|
609
723
|
})
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
mockSupabase.from.mockImplementation((table: string) => {
|
|
727
|
+
if (table === 'rbac_organisation_roles') {
|
|
728
|
+
return mockQueryBuilder;
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
732
|
+
};
|
|
610
733
|
});
|
|
611
734
|
|
|
612
735
|
await organisationService.initialize();
|
|
@@ -792,7 +915,7 @@ describe('OrganisationService', () => {
|
|
|
792
915
|
})
|
|
793
916
|
};
|
|
794
917
|
}
|
|
795
|
-
if (table === '
|
|
918
|
+
if (table === 'core_organisations') {
|
|
796
919
|
return {
|
|
797
920
|
select: vi.fn().mockResolvedValue({
|
|
798
921
|
data: [mockOrganisation],
|
|
@@ -847,19 +970,27 @@ describe('OrganisationService', () => {
|
|
|
847
970
|
});
|
|
848
971
|
|
|
849
972
|
it('should handle invalid organisation IDs in memberships', async () => {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
973
|
+
const mockQueryBuilder = {
|
|
974
|
+
select: vi.fn().mockReturnThis(),
|
|
975
|
+
eq: vi.fn().mockReturnThis(),
|
|
976
|
+
is: vi.fn().mockResolvedValue({
|
|
977
|
+
data: [{
|
|
978
|
+
id: mockMembership.id,
|
|
979
|
+
user_id: mockUser.id,
|
|
980
|
+
organisation_id: '', // Empty/invalid ID
|
|
981
|
+
role: mockMembership.role,
|
|
982
|
+
status: mockMembership.status,
|
|
983
|
+
granted_at: mockMembership.granted_at,
|
|
984
|
+
revoked_at: mockMembership.revoked_at,
|
|
985
|
+
core_organisations: null // No organisation data due to invalid ID
|
|
986
|
+
}],
|
|
987
|
+
error: null
|
|
988
|
+
})
|
|
989
|
+
};
|
|
854
990
|
|
|
855
991
|
mockSupabase.from.mockImplementation((table: string) => {
|
|
856
|
-
if (table === '
|
|
857
|
-
return
|
|
858
|
-
select: vi.fn().mockResolvedValue({
|
|
859
|
-
data: [],
|
|
860
|
-
error: null
|
|
861
|
-
})
|
|
862
|
-
};
|
|
992
|
+
if (table === 'rbac_organisation_roles') {
|
|
993
|
+
return mockQueryBuilder;
|
|
863
994
|
}
|
|
864
995
|
return {
|
|
865
996
|
select: vi.fn().mockResolvedValue({ data: [], error: null })
|
|
@@ -869,7 +1000,8 @@ describe('OrganisationService', () => {
|
|
|
869
1000
|
await organisationService.initialize();
|
|
870
1001
|
|
|
871
1002
|
expect(organisationService.getError()).toBeDefined();
|
|
872
|
-
|
|
1003
|
+
// The service throws "No organisations found in role data" when organisations array is empty
|
|
1004
|
+
expect(organisationService.getError()?.message).toContain('No organisations found');
|
|
873
1005
|
});
|
|
874
1006
|
|
|
875
1007
|
it('should handle non-UUID organisation IDs', async () => {
|
|
@@ -879,7 +1011,7 @@ describe('OrganisationService', () => {
|
|
|
879
1011
|
});
|
|
880
1012
|
|
|
881
1013
|
mockSupabase.from.mockImplementation((table: string) => {
|
|
882
|
-
if (table === '
|
|
1014
|
+
if (table === 'core_organisations') {
|
|
883
1015
|
return {
|
|
884
1016
|
select: vi.fn().mockResolvedValue({
|
|
885
1017
|
data: [],
|
|
@@ -952,7 +1084,7 @@ describe('OrganisationService', () => {
|
|
|
952
1084
|
});
|
|
953
1085
|
|
|
954
1086
|
mockSupabase.from.mockImplementation((table: string) => {
|
|
955
|
-
if (table === '
|
|
1087
|
+
if (table === 'core_organisations') {
|
|
956
1088
|
return {
|
|
957
1089
|
select: vi.fn().mockResolvedValue({
|
|
958
1090
|
data: [mockOrganisation],
|
|
@@ -980,7 +1112,7 @@ describe('OrganisationService', () => {
|
|
|
980
1112
|
});
|
|
981
1113
|
|
|
982
1114
|
mockSupabase.from.mockImplementation((table: string) => {
|
|
983
|
-
if (table === '
|
|
1115
|
+
if (table === 'core_organisations') {
|
|
984
1116
|
return {
|
|
985
1117
|
select: vi.fn().mockResolvedValue({
|
|
986
1118
|
data: [mockOrganisation],
|