@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
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { renderHook, waitFor, act } from '@testing-library/react';
|
|
3
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
4
|
+
import { usePublicEvent, clearPublicEventCache, getPublicEventCacheStats } from '../public/usePublicEvent';
|
|
5
|
+
import { usePublicPageContext } from '../../components/PublicLayout/PublicPageProvider';
|
|
6
|
+
|
|
7
|
+
// Mock the PublicPageProvider
|
|
8
|
+
vi.mock('../../components/PublicLayout/PublicPageProvider', () => ({
|
|
9
|
+
usePublicPageContext: vi.fn(() => ({
|
|
10
|
+
environment: {
|
|
11
|
+
supabaseUrl: 'https://test.supabase.co',
|
|
12
|
+
supabaseKey: 'test-anon-key'
|
|
13
|
+
}
|
|
14
|
+
}))
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// Mock Supabase client
|
|
18
|
+
const mockSupabaseClient = {
|
|
19
|
+
rpc: vi.fn(),
|
|
20
|
+
from: vi.fn(() => ({
|
|
21
|
+
select: vi.fn(() => ({
|
|
22
|
+
eq: vi.fn(() => ({
|
|
23
|
+
eq: vi.fn(() => ({
|
|
24
|
+
not: vi.fn(() => ({
|
|
25
|
+
limit: vi.fn(() => ({
|
|
26
|
+
single: vi.fn()
|
|
27
|
+
}))
|
|
28
|
+
}))
|
|
29
|
+
}))
|
|
30
|
+
}))
|
|
31
|
+
}))
|
|
32
|
+
}))
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Helper to create table query mocks
|
|
36
|
+
const createTableQueryMock = () => ({
|
|
37
|
+
data: null,
|
|
38
|
+
error: null,
|
|
39
|
+
mockResolvedValueOnce: vi.fn(),
|
|
40
|
+
mockResolvedValue: vi.fn()
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Helper to create a complete table query chain
|
|
44
|
+
const createTableQueryChain = (finalResult: any) => {
|
|
45
|
+
const mockChain = {
|
|
46
|
+
select: vi.fn().mockReturnThis(),
|
|
47
|
+
eq: vi.fn().mockReturnThis(),
|
|
48
|
+
not: vi.fn().mockReturnThis(),
|
|
49
|
+
limit: vi.fn().mockReturnThis(),
|
|
50
|
+
single: vi.fn().mockResolvedValue(finalResult)
|
|
51
|
+
};
|
|
52
|
+
return mockChain;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Mock createClient
|
|
56
|
+
vi.mock('@supabase/supabase-js', () => ({
|
|
57
|
+
createClient: vi.fn(() => mockSupabaseClient)
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
// Mock environment variables
|
|
61
|
+
const originalEnv = import.meta.env;
|
|
62
|
+
|
|
63
|
+
describe('usePublicEvent', () => {
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
vi.clearAllMocks();
|
|
66
|
+
clearPublicEventCache();
|
|
67
|
+
|
|
68
|
+
// Reset environment
|
|
69
|
+
Object.defineProperty(import.meta, 'env', {
|
|
70
|
+
value: {
|
|
71
|
+
VITE_SUPABASE_URL: 'https://test.supabase.co',
|
|
72
|
+
VITE_SUPABASE_ANON_KEY: 'test-anon-key'
|
|
73
|
+
},
|
|
74
|
+
writable: true
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Mock window object
|
|
78
|
+
Object.defineProperty(window, 'location', {
|
|
79
|
+
value: { href: 'https://test.com' },
|
|
80
|
+
writable: true
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Re-establish mock implementations after clearAllMocks
|
|
84
|
+
mockSupabaseClient.rpc.mockImplementation(() =>
|
|
85
|
+
Promise.resolve({
|
|
86
|
+
data: null,
|
|
87
|
+
error: null
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Ensure the mock is properly configured
|
|
92
|
+
expect(mockSupabaseClient.rpc).toBeDefined();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
vi.clearAllMocks();
|
|
97
|
+
clearPublicEventCache();
|
|
98
|
+
Object.defineProperty(import.meta, 'env', {
|
|
99
|
+
value: originalEnv,
|
|
100
|
+
writable: true
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('Basic Functionality', () => {
|
|
105
|
+
it('should initialize with loading state', () => {
|
|
106
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
107
|
+
|
|
108
|
+
expect(result.current.isLoading).toBe(true);
|
|
109
|
+
expect(result.current.event).toBe(null);
|
|
110
|
+
expect(result.current.error).toBe(null);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should fetch event data successfully via RPC', async () => {
|
|
114
|
+
const mockEventData = {
|
|
115
|
+
event_id: '123',
|
|
116
|
+
event_name: 'Test Event',
|
|
117
|
+
event_date: '2024-01-01',
|
|
118
|
+
event_venue: 'Test Venue',
|
|
119
|
+
event_participants: 100,
|
|
120
|
+
event_colours: { primary: '#000000' },
|
|
121
|
+
organisation_id: 'org-123',
|
|
122
|
+
event_days: 1,
|
|
123
|
+
event_typicalunit: 'km',
|
|
124
|
+
event_rounddown: false,
|
|
125
|
+
event_youthmultiplier: 1.0,
|
|
126
|
+
event_catering_email: 'test@example.com',
|
|
127
|
+
event_news: 'Test news',
|
|
128
|
+
event_billing: 'Test billing',
|
|
129
|
+
event_footer: 'Test footer',
|
|
130
|
+
event_email: 'event@example.com',
|
|
131
|
+
event_logo: null
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
135
|
+
data: [mockEventData],
|
|
136
|
+
error: null
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
140
|
+
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(result.current.isLoading).toBe(false);
|
|
143
|
+
}, { interval: 10 });
|
|
144
|
+
|
|
145
|
+
expect(result.current.event).toEqual({
|
|
146
|
+
id: '123',
|
|
147
|
+
event_id: '123',
|
|
148
|
+
event_name: 'Test Event',
|
|
149
|
+
event_code: 'test-event',
|
|
150
|
+
event_date: '2024-01-01',
|
|
151
|
+
event_venue: 'Test Venue',
|
|
152
|
+
event_participants: 100,
|
|
153
|
+
event_logo: null,
|
|
154
|
+
event_colours: { primary: '#000000' },
|
|
155
|
+
organisation_id: 'org-123',
|
|
156
|
+
is_visible: true,
|
|
157
|
+
created_at: expect.any(String),
|
|
158
|
+
updated_at: expect.any(String)
|
|
159
|
+
});
|
|
160
|
+
expect(result.current.error).toBe(null);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should handle event not found', async () => {
|
|
164
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
165
|
+
data: [],
|
|
166
|
+
error: null
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const { result } = renderHook(() => usePublicEvent('nonexistent-event'));
|
|
170
|
+
|
|
171
|
+
await waitFor(() => {
|
|
172
|
+
expect(result.current.isLoading).toBe(false);
|
|
173
|
+
}, { interval: 10 });
|
|
174
|
+
|
|
175
|
+
expect(result.current.event).toBe(null);
|
|
176
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should handle invalid event code', async () => {
|
|
180
|
+
const { result } = renderHook(() => usePublicEvent(''));
|
|
181
|
+
|
|
182
|
+
await waitFor(() => {
|
|
183
|
+
expect(result.current.isLoading).toBe(false);
|
|
184
|
+
}, { interval: 10 });
|
|
185
|
+
|
|
186
|
+
expect(result.current.event).toBe(null);
|
|
187
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
188
|
+
expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('Caching', () => {
|
|
193
|
+
it('should cache event data', async () => {
|
|
194
|
+
const mockEventData = {
|
|
195
|
+
event_id: '123',
|
|
196
|
+
event_name: 'Test Event',
|
|
197
|
+
event_date: '2024-01-01',
|
|
198
|
+
event_venue: 'Test Venue',
|
|
199
|
+
event_participants: 100,
|
|
200
|
+
event_colours: { primary: '#000000' },
|
|
201
|
+
organisation_id: 'org-123',
|
|
202
|
+
event_days: 1,
|
|
203
|
+
event_typicalunit: 'km',
|
|
204
|
+
event_rounddown: false,
|
|
205
|
+
event_youthmultiplier: 1.0,
|
|
206
|
+
event_catering_email: 'test@example.com',
|
|
207
|
+
event_news: 'Test news',
|
|
208
|
+
event_billing: 'Test billing',
|
|
209
|
+
event_footer: 'Test footer',
|
|
210
|
+
event_email: 'event@example.com'
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
214
|
+
data: [mockEventData],
|
|
215
|
+
error: null
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const { result, rerender } = renderHook(() => usePublicEvent('test-event'));
|
|
219
|
+
|
|
220
|
+
await waitFor(() => {
|
|
221
|
+
expect(result.current.isLoading).toBe(false);
|
|
222
|
+
}, { interval: 10 });
|
|
223
|
+
|
|
224
|
+
// Rerender with same event code - should use cache
|
|
225
|
+
rerender();
|
|
226
|
+
|
|
227
|
+
// Should not call RPC again
|
|
228
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(1);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should respect cache TTL', async () => {
|
|
232
|
+
const mockEventData = {
|
|
233
|
+
event_id: '123',
|
|
234
|
+
event_name: 'Test Event',
|
|
235
|
+
event_date: '2024-01-01',
|
|
236
|
+
event_venue: 'Test Venue',
|
|
237
|
+
event_participants: 100,
|
|
238
|
+
event_colours: { primary: '#000000' },
|
|
239
|
+
organisation_id: 'org-123',
|
|
240
|
+
event_days: 1,
|
|
241
|
+
event_typicalunit: 'km',
|
|
242
|
+
event_rounddown: false,
|
|
243
|
+
event_youthmultiplier: 1.0,
|
|
244
|
+
event_catering_email: 'test@example.com',
|
|
245
|
+
event_news: 'Test news',
|
|
246
|
+
event_billing: 'Test billing',
|
|
247
|
+
event_footer: 'Test footer',
|
|
248
|
+
event_email: 'event@example.com'
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
252
|
+
data: [mockEventData],
|
|
253
|
+
error: null
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const { result } = renderHook(() => usePublicEvent('test-event', { cacheTtl: 100 }));
|
|
257
|
+
|
|
258
|
+
await waitFor(() => {
|
|
259
|
+
expect(result.current.isLoading).toBe(false);
|
|
260
|
+
}, { interval: 10 });
|
|
261
|
+
|
|
262
|
+
// Wait for cache to expire
|
|
263
|
+
await new Promise(resolve => setTimeout(resolve, 150));
|
|
264
|
+
|
|
265
|
+
// Rerender should fetch again
|
|
266
|
+
const { result: result2 } = renderHook(() => usePublicEvent('test-event', { cacheTtl: 100 }));
|
|
267
|
+
|
|
268
|
+
await waitFor(() => {
|
|
269
|
+
expect(result2.current.isLoading).toBe(false);
|
|
270
|
+
}, { interval: 10 });
|
|
271
|
+
|
|
272
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should disable caching when requested', async () => {
|
|
276
|
+
const mockEventData = {
|
|
277
|
+
event_id: '123',
|
|
278
|
+
event_name: 'Test Event',
|
|
279
|
+
event_date: '2024-01-01',
|
|
280
|
+
event_venue: 'Test Venue',
|
|
281
|
+
event_participants: 100,
|
|
282
|
+
event_colours: { primary: '#000000' },
|
|
283
|
+
organisation_id: 'org-123',
|
|
284
|
+
event_days: 1,
|
|
285
|
+
event_typicalunit: 'km',
|
|
286
|
+
event_rounddown: false,
|
|
287
|
+
event_youthmultiplier: 1.0,
|
|
288
|
+
event_catering_email: 'test@example.com',
|
|
289
|
+
event_news: 'Test news',
|
|
290
|
+
event_billing: 'Test billing',
|
|
291
|
+
event_footer: 'Test footer',
|
|
292
|
+
event_email: 'event@example.com',
|
|
293
|
+
event_logo: null
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// Clear any existing cache
|
|
297
|
+
const { clearPublicEventCache } = await import('../public/usePublicEvent');
|
|
298
|
+
clearPublicEventCache();
|
|
299
|
+
|
|
300
|
+
// Use mockImplementation to handle multiple calls
|
|
301
|
+
let callCount = 0;
|
|
302
|
+
mockSupabaseClient.rpc.mockImplementation(() => {
|
|
303
|
+
callCount++;
|
|
304
|
+
return Promise.resolve({
|
|
305
|
+
data: [mockEventData],
|
|
306
|
+
error: null
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const { result, rerender } = renderHook(
|
|
311
|
+
({ eventCode }) => usePublicEvent(eventCode, { enableCache: false }),
|
|
312
|
+
{ initialProps: { eventCode: 'test-event' } }
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
await waitFor(() => {
|
|
316
|
+
expect(result.current.isLoading).toBe(false);
|
|
317
|
+
}, { interval: 10 });
|
|
318
|
+
|
|
319
|
+
// Change the event code to force a new fetch
|
|
320
|
+
rerender({ eventCode: 'test-event-2' });
|
|
321
|
+
|
|
322
|
+
await waitFor(() => {
|
|
323
|
+
expect(result.current.isLoading).toBe(false);
|
|
324
|
+
}, { interval: 10 });
|
|
325
|
+
|
|
326
|
+
expect(callCount).toBe(2);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
describe('Error Handling', () => {
|
|
331
|
+
it('should handle RPC errors', async () => {
|
|
332
|
+
// Mock the RPC call to return an error object
|
|
333
|
+
const testError = { message: 'Database error', details: 'Test error details', hint: null, code: 'TEST_ERROR' };
|
|
334
|
+
mockSupabaseClient.rpc.mockImplementation(() =>
|
|
335
|
+
Promise.resolve({
|
|
336
|
+
data: null,
|
|
337
|
+
error: testError
|
|
338
|
+
})
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
342
|
+
|
|
343
|
+
await waitFor(() => {
|
|
344
|
+
expect(result.current.isLoading).toBe(false);
|
|
345
|
+
}, { interval: 10 });
|
|
346
|
+
|
|
347
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
348
|
+
expect(result.current.error?.message).toBe('Database error');
|
|
349
|
+
expect(result.current.event).toBe(null);
|
|
350
|
+
|
|
351
|
+
// Verify the mock was called
|
|
352
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledWith('get_public_event_by_code', {
|
|
353
|
+
event_code_param: 'test-event'
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('should handle missing Supabase client', async () => {
|
|
358
|
+
// Mock the PublicPageProvider to return null environment
|
|
359
|
+
const mockUsePublicPageContext = vi.mocked(usePublicPageContext);
|
|
360
|
+
mockUsePublicPageContext.mockReturnValueOnce({
|
|
361
|
+
environment: {
|
|
362
|
+
supabaseUrl: '',
|
|
363
|
+
supabaseKey: ''
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Mock createClient to return null when environment is empty
|
|
368
|
+
const { createClient } = await import('@supabase/supabase-js');
|
|
369
|
+
vi.mocked(createClient).mockReturnValueOnce(null as any);
|
|
370
|
+
|
|
371
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
372
|
+
|
|
373
|
+
await waitFor(() => {
|
|
374
|
+
expect(result.current.isLoading).toBe(false);
|
|
375
|
+
}, { interval: 10 });
|
|
376
|
+
|
|
377
|
+
expect(result.current.error).toBeInstanceOf(Error);
|
|
378
|
+
expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
|
|
379
|
+
expect(result.current.event).toBe(null);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('Provider Context Integration', () => {
|
|
384
|
+
it('should use PublicPageContext when available', async () => {
|
|
385
|
+
const mockContext = {
|
|
386
|
+
environment: {
|
|
387
|
+
supabaseUrl: 'https://context.supabase.co',
|
|
388
|
+
supabaseKey: 'context-anon-key'
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
vi.mocked(usePublicPageContext).mockReturnValue(mockContext);
|
|
393
|
+
|
|
394
|
+
const mockEventData = {
|
|
395
|
+
event_id: '123',
|
|
396
|
+
event_name: 'Test Event',
|
|
397
|
+
event_date: '2024-01-01',
|
|
398
|
+
event_venue: 'Test Venue',
|
|
399
|
+
event_participants: 100,
|
|
400
|
+
event_colours: { primary: '#000000' },
|
|
401
|
+
organisation_id: 'org-123',
|
|
402
|
+
event_days: 1,
|
|
403
|
+
event_typicalunit: 'km',
|
|
404
|
+
event_rounddown: false,
|
|
405
|
+
event_youthmultiplier: 1.0,
|
|
406
|
+
event_catering_email: 'test@example.com',
|
|
407
|
+
event_news: 'Test news',
|
|
408
|
+
event_billing: 'Test billing',
|
|
409
|
+
event_footer: 'Test footer',
|
|
410
|
+
event_email: 'event@example.com',
|
|
411
|
+
event_logo: null
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
415
|
+
data: [mockEventData],
|
|
416
|
+
error: null
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
420
|
+
|
|
421
|
+
await waitFor(() => {
|
|
422
|
+
expect(result.current.isLoading).toBe(false);
|
|
423
|
+
}, { interval: 10 });
|
|
424
|
+
|
|
425
|
+
expect(result.current.event).toBeTruthy();
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
describe('Refetch Functionality', () => {
|
|
430
|
+
it('should refetch data when refetch is called', async () => {
|
|
431
|
+
const mockEventData = {
|
|
432
|
+
event_id: '123',
|
|
433
|
+
event_name: 'Test Event',
|
|
434
|
+
event_date: '2024-01-01',
|
|
435
|
+
event_venue: 'Test Venue',
|
|
436
|
+
event_participants: 100,
|
|
437
|
+
event_colours: { primary: '#000000' },
|
|
438
|
+
organisation_id: 'org-123',
|
|
439
|
+
event_days: 1,
|
|
440
|
+
event_typicalunit: 'km',
|
|
441
|
+
event_rounddown: false,
|
|
442
|
+
event_youthmultiplier: 1.0,
|
|
443
|
+
event_catering_email: 'test@example.com',
|
|
444
|
+
event_news: 'Test news',
|
|
445
|
+
event_billing: 'Test billing',
|
|
446
|
+
event_footer: 'Test footer',
|
|
447
|
+
event_email: 'event@example.com'
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
mockSupabaseClient.rpc.mockResolvedValue({
|
|
451
|
+
data: [mockEventData],
|
|
452
|
+
error: null
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
456
|
+
|
|
457
|
+
await waitFor(() => {
|
|
458
|
+
expect(result.current.isLoading).toBe(false);
|
|
459
|
+
}, { interval: 10 });
|
|
460
|
+
|
|
461
|
+
// Call refetch
|
|
462
|
+
await act(async () => {
|
|
463
|
+
await result.current.refetch();
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('should clear cache when refetch is called', async () => {
|
|
470
|
+
const mockEventData = {
|
|
471
|
+
event_id: '123',
|
|
472
|
+
event_name: 'Test Event',
|
|
473
|
+
event_date: '2024-01-01',
|
|
474
|
+
event_venue: 'Test Venue',
|
|
475
|
+
event_participants: 100,
|
|
476
|
+
event_colours: { primary: '#000000' },
|
|
477
|
+
organisation_id: 'org-123',
|
|
478
|
+
event_days: 1,
|
|
479
|
+
event_typicalunit: 'km',
|
|
480
|
+
event_rounddown: false,
|
|
481
|
+
event_youthmultiplier: 1.0,
|
|
482
|
+
event_catering_email: 'test@example.com',
|
|
483
|
+
event_news: 'Test news',
|
|
484
|
+
event_billing: 'Test billing',
|
|
485
|
+
event_footer: 'Test footer',
|
|
486
|
+
event_email: 'event@example.com'
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
mockSupabaseClient.rpc.mockResolvedValue({
|
|
490
|
+
data: [mockEventData],
|
|
491
|
+
error: null
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
495
|
+
|
|
496
|
+
await waitFor(() => {
|
|
497
|
+
expect(result.current.isLoading).toBe(false);
|
|
498
|
+
}, { interval: 10 });
|
|
499
|
+
|
|
500
|
+
// Call refetch
|
|
501
|
+
await act(async () => {
|
|
502
|
+
await result.current.refetch();
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// Should call RPC again (cache was cleared)
|
|
506
|
+
expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
describe('Cache Management Utilities', () => {
|
|
511
|
+
it('should clear public event cache', () => {
|
|
512
|
+
// Add some data to cache
|
|
513
|
+
const stats = getPublicEventCacheStats();
|
|
514
|
+
expect(stats.size).toBe(0);
|
|
515
|
+
|
|
516
|
+
// Clear cache
|
|
517
|
+
clearPublicEventCache();
|
|
518
|
+
|
|
519
|
+
const statsAfter = getPublicEventCacheStats();
|
|
520
|
+
expect(statsAfter.size).toBe(0);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('should get cache statistics', () => {
|
|
524
|
+
const stats = getPublicEventCacheStats();
|
|
525
|
+
expect(stats).toEqual({
|
|
526
|
+
size: 0,
|
|
527
|
+
keys: []
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
describe('Edge Cases', () => {
|
|
533
|
+
it('should handle null event data from RPC', async () => {
|
|
534
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
535
|
+
data: null,
|
|
536
|
+
error: null
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
540
|
+
|
|
541
|
+
await waitFor(() => {
|
|
542
|
+
expect(result.current.isLoading).toBe(false);
|
|
543
|
+
}, { interval: 10 });
|
|
544
|
+
|
|
545
|
+
expect(result.current.event).toBe(null);
|
|
546
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('should handle empty event data array from RPC', async () => {
|
|
550
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
551
|
+
data: [],
|
|
552
|
+
error: null
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
556
|
+
|
|
557
|
+
await waitFor(() => {
|
|
558
|
+
expect(result.current.isLoading).toBe(false);
|
|
559
|
+
}, { interval: 10 });
|
|
560
|
+
|
|
561
|
+
expect(result.current.event).toBe(null);
|
|
562
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it('should handle undefined event data from RPC', async () => {
|
|
566
|
+
mockSupabaseClient.rpc.mockResolvedValueOnce({
|
|
567
|
+
data: [undefined],
|
|
568
|
+
error: null
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
const { result } = renderHook(() => usePublicEvent('test-event'));
|
|
572
|
+
|
|
573
|
+
await waitFor(() => {
|
|
574
|
+
expect(result.current.isLoading).toBe(false);
|
|
575
|
+
}, { interval: 10 });
|
|
576
|
+
|
|
577
|
+
expect(result.current.event).toBe(null);
|
|
578
|
+
expect(result.current.error).toEqual(new Error('Event not found'));
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
});
|
|
@@ -400,7 +400,7 @@ describe('useSecureDataAccess', () => {
|
|
|
400
400
|
|
|
401
401
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
402
402
|
|
|
403
|
-
const data = await result.current.secureUpdate('
|
|
403
|
+
const data = await result.current.secureUpdate('core_events', { name: 'Updated' }, { id: 1 });
|
|
404
404
|
|
|
405
405
|
expect(data).toEqual([{ id: 1, name: 'Updated', organisation_id: 'org-123' }]);
|
|
406
406
|
});
|
|
@@ -421,7 +421,7 @@ describe('useSecureDataAccess', () => {
|
|
|
421
421
|
|
|
422
422
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
423
423
|
|
|
424
|
-
await expect(result.current.secureUpdate('
|
|
424
|
+
await expect(result.current.secureUpdate('core_events', { name: 'Updated' }, { id: 1 })).rejects.toThrow('Update failed');
|
|
425
425
|
});
|
|
426
426
|
|
|
427
427
|
it('should return empty array when no data updated', async () => {
|
|
@@ -440,7 +440,7 @@ describe('useSecureDataAccess', () => {
|
|
|
440
440
|
|
|
441
441
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
442
442
|
|
|
443
|
-
const data = await result.current.secureUpdate('
|
|
443
|
+
const data = await result.current.secureUpdate('core_events', { name: 'Updated' }, { id: 1 });
|
|
444
444
|
|
|
445
445
|
expect(data).toEqual([]);
|
|
446
446
|
});
|
|
@@ -461,9 +461,9 @@ describe('useSecureDataAccess', () => {
|
|
|
461
461
|
|
|
462
462
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
463
463
|
|
|
464
|
-
await result.current.secureDelete('
|
|
464
|
+
await result.current.secureDelete('core_events', { id: 1 });
|
|
465
465
|
|
|
466
|
-
expect(mockSupabase.from).toHaveBeenCalledWith('
|
|
466
|
+
expect(mockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
467
467
|
});
|
|
468
468
|
|
|
469
469
|
it('should handle delete errors', async () => {
|
|
@@ -480,7 +480,8 @@ describe('useSecureDataAccess', () => {
|
|
|
480
480
|
|
|
481
481
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
482
482
|
|
|
483
|
-
|
|
483
|
+
// The implementation throws the error, so the promise should reject
|
|
484
|
+
await expect(result.current.secureDelete('core_events', { id: 1 })).rejects.toThrow('Delete failed');
|
|
484
485
|
});
|
|
485
486
|
});
|
|
486
487
|
|
|
@@ -605,12 +606,12 @@ describe('useSecureDataAccess', () => {
|
|
|
605
606
|
const { result } = renderHook(() => useSecureDataAccess());
|
|
606
607
|
|
|
607
608
|
// Verify the actual behavior: secure update works correctly
|
|
608
|
-
const updateResult = await result.current.secureUpdate('
|
|
609
|
+
const updateResult = await result.current.secureUpdate('core_events', { name: 'Updated' }, { id: 1 });
|
|
609
610
|
|
|
610
611
|
// Test the actual functionality: update executes and returns results
|
|
611
612
|
expect(updateResult).toEqual([{ id: 1, name: 'Updated' }]);
|
|
612
613
|
// Verify security: organisation filter is applied
|
|
613
|
-
expect(mockSupabase.from).toHaveBeenCalledWith('
|
|
614
|
+
expect(mockSupabase.from).toHaveBeenCalledWith('core_events');
|
|
614
615
|
// Verify the operation completed successfully (organisation filter is applied internally)
|
|
615
616
|
|
|
616
617
|
consoleSpy.mockRestore();
|
|
@@ -210,7 +210,7 @@ export function usePublicEvent(
|
|
|
210
210
|
|
|
211
211
|
// Fallback: Direct table access with public RLS policy
|
|
212
212
|
const tableResponse2 = await (supabase as any)
|
|
213
|
-
.from('
|
|
213
|
+
.from('core_events')
|
|
214
214
|
.select(`
|
|
215
215
|
event_id,
|
|
216
216
|
event_name,
|
|
@@ -247,11 +247,11 @@ export function usePublicEvent(
|
|
|
247
247
|
return;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
// Get event logo from
|
|
250
|
+
// Get event logo from core_file_references
|
|
251
251
|
const logoResponse = await (supabase as any)
|
|
252
|
-
.from('
|
|
252
|
+
.from('core_file_references')
|
|
253
253
|
.select('file_path')
|
|
254
|
-
.eq('table_name', '
|
|
254
|
+
.eq('table_name', 'core_events')
|
|
255
255
|
.eq('record_id', tableData.event_id)
|
|
256
256
|
.eq('is_public', true)
|
|
257
257
|
.eq('file_metadata->>category', 'event_logos')
|
|
@@ -286,7 +286,7 @@ export function usePublicEvent(
|
|
|
286
286
|
logger.warn('usePublicEvent', 'RPC call failed, falling back to direct table access:', rpcError);
|
|
287
287
|
|
|
288
288
|
const tableResponse = await (supabase as any)
|
|
289
|
-
.from('
|
|
289
|
+
.from('core_events')
|
|
290
290
|
.select(`
|
|
291
291
|
event_id,
|
|
292
292
|
event_name,
|
|
@@ -323,11 +323,11 @@ export function usePublicEvent(
|
|
|
323
323
|
return;
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
// Get event logo from
|
|
326
|
+
// Get event logo from core_file_references
|
|
327
327
|
const logoResponse = await (supabase as any)
|
|
328
|
-
.from('
|
|
328
|
+
.from('core_file_references')
|
|
329
329
|
.select('file_path')
|
|
330
|
-
.eq('table_name', '
|
|
330
|
+
.eq('table_name', 'core_events')
|
|
331
331
|
.eq('record_id', tableData.event_id)
|
|
332
332
|
.eq('is_public', true)
|
|
333
333
|
.eq('file_metadata->>category', 'event_logos')
|