/** * Tests d'intégration pour l'authentification Keycloak * Vérifie que l'intégration fonctionne correctement avec l'application existante */ import React from 'react'; import { render, screen, waitFor, fireEvent } from '@testing-library/react'; import { useRouter } from 'next/navigation'; import { AuthProvider, useAuth } from '../../contexts/AuthContext'; import ProtectedRoute from '../../components/auth/ProtectedRoute'; import ProtectedLayout from '../../components/ProtectedLayout'; // Mock Next.js router jest.mock('next/navigation', () => ({ useRouter: jest.fn(), useSearchParams: jest.fn(() => ({ get: jest.fn(() => null) })) })); // Mock Keycloak const mockKeycloak = { init: jest.fn(() => Promise.resolve(false)), login: jest.fn(), logout: jest.fn(), updateToken: jest.fn(() => Promise.resolve(false)), authenticated: false, token: null, refreshToken: null, tokenParsed: null, onTokenExpired: null, onAuthRefreshSuccess: null, onAuthRefreshError: null, onAuthLogout: null, }; jest.mock('../../config/keycloak', () => ({ keycloak: mockKeycloak, keycloakInitOptions: {}, KEYCLOAK_TIMEOUTS: { TOKEN_REFRESH_BEFORE_EXPIRY: 30, SESSION_CHECK_INTERVAL: 60, SILENT_CHECK_SSO_TIMEOUT: 5000, }, RoleUtils: { hasRole: jest.fn(() => false), hasPermission: jest.fn(() => false), hasAnyRole: jest.fn(() => false), getHighestRole: jest.fn(() => 'USER'), } })); // Mock PrimeReact components jest.mock('primereact/api', () => ({ PrimeReactProvider: ({ children }: { children: React.ReactNode }) =>
{children}
})); // Mock Layout components jest.mock('../../layout/context/layoutcontext', () => ({ LayoutProvider: ({ children }: { children: React.ReactNode }) =>
{children}
})); const mockPush = jest.fn(); const mockBack = jest.fn(); beforeEach(() => { jest.clearAllMocks(); (useRouter as jest.Mock).mockReturnValue({ push: mockPush, back: mockBack, pathname: '/dashboard', query: {}, }); }); // Composant de test pour vérifier l'authentification const TestAuthComponent = () => { const { isAuthenticated, isLoading, user, login, logout } = useAuth(); if (isLoading) { return
Chargement...
; } return (
{isAuthenticated ? 'Authentifié' : 'Non authentifié'}
{user && (
{user.username} - {user.email}
)}
); }; // Composant de test pour vérifier la protection des routes const TestProtectedComponent = () => { return
Contenu protégé
; }; describe('Intégration Authentification Keycloak', () => { describe('AuthProvider', () => { it('devrait initialiser Keycloak au montage', async () => { render( ); await waitFor(() => { expect(mockKeycloak.init).toHaveBeenCalledWith(expect.any(Object)); }); }); it('devrait afficher le statut de chargement initial', () => { render( ); expect(screen.getByTestId('loading')).toBeInTheDocument(); }); it('devrait afficher le statut non authentifié après initialisation', async () => { mockKeycloak.authenticated = false; render( ); await waitFor(() => { expect(screen.getByTestId('auth-status')).toHaveTextContent('Non authentifié'); }); }); it('devrait appeler keycloak.login lors du clic sur connexion', async () => { mockKeycloak.authenticated = false; render( ); await waitFor(() => { expect(screen.getByTestId('login-btn')).toBeInTheDocument(); }); fireEvent.click(screen.getByTestId('login-btn')); expect(mockKeycloak.login).toHaveBeenCalled(); }); }); describe('ProtectedRoute', () => { it('devrait rediriger vers login si non authentifié', async () => { mockKeycloak.authenticated = false; render( ); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith(expect.stringContaining('/auth/login')); }); }); it('devrait afficher le contenu si authentifié', async () => { mockKeycloak.authenticated = true; mockKeycloak.tokenParsed = { preferred_username: 'testuser', email: 'test@example.com', realm_access: { roles: ['USER'] } }; render( ); await waitFor(() => { expect(screen.getByTestId('protected-content')).toBeInTheDocument(); }); }); it('devrait rediriger vers forbidden si rôle insuffisant', async () => { mockKeycloak.authenticated = true; mockKeycloak.tokenParsed = { preferred_username: 'testuser', email: 'test@example.com', realm_access: { roles: ['USER'] } }; render( ); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith('/auth/forbidden'); }); }); }); describe('ProtectedLayout', () => { it('devrait protéger le layout principal', async () => { mockKeycloak.authenticated = false; render(
Contenu du layout
); await waitFor(() => { expect(mockPush).toHaveBeenCalledWith(expect.stringContaining('/auth/login')); }); }); it('devrait afficher le contenu du layout si authentifié', async () => { mockKeycloak.authenticated = true; mockKeycloak.tokenParsed = { preferred_username: 'testuser', email: 'test@example.com', realm_access: { roles: ['USER'] } }; render(
Contenu du layout
); await waitFor(() => { expect(screen.getByTestId('layout-content')).toBeInTheDocument(); }); }); }); describe('Gestion des erreurs', () => { it('devrait gérer les erreurs d\'initialisation Keycloak', async () => { mockKeycloak.init.mockRejectedValueOnce(new Error('Erreur Keycloak')); render( ); await waitFor(() => { expect(screen.getByTestId('auth-status')).toHaveTextContent('Non authentifié'); }); }); it('devrait gérer les erreurs de rafraîchissement de token', async () => { mockKeycloak.authenticated = true; mockKeycloak.updateToken.mockRejectedValueOnce(new Error('Token expiré')); render( ); await waitFor(() => { expect(mockKeycloak.logout).toHaveBeenCalled(); }); }); }); }); describe('Intégration avec les services API', () => { it('devrait ajouter le token Keycloak aux requêtes API', async () => { // Ce test nécessiterait de mocker axios et de vérifier que les interceptors // ajoutent correctement le token Keycloak aux en-têtes // Pour l'instant, nous vérifions juste que les services sont importables const { ApiService } = await import('../../services/ApiService'); expect(ApiService).toBeDefined(); }); });