import { TestBed } from '@angular/core/testing'; import { Router, UrlTree } from '@angular/router'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable, of, throwError } from 'rxjs'; import { isObservable } from 'rxjs'; import { authGuard } from './auth.guard'; import { AuthService } from '../services/auth.service'; /** * Tests unitaires de la garde `authGuard` (CanActivateFn). * * Tâche 10.4 — Exigences 4.6 (routes protégées), 9.3 (redirection onboarding/login). * La garde est exécutée via `TestBed.runInInjectionContext` pour disposer du * contexte d'injection (`inject(...)`). * * La garde retourne un `Observable` : en présence d'un jeton local, * elle valide la session côté serveur via `AuthService.validerSession()` avant de * conclure (Exigence 9 : rejet d'un jeton résiduel dont le compte n'existe plus). */ describe('authGuard', () => { let estAuthentifieValeur: boolean; let urlTreeFactice: UrlTree; let createUrlTreeSpy: jasmine.Spy; let validerSessionSpy: jasmine.Spy; // Paramètres de la CanActivateFn (non utilisés par la garde, mais requis). const route = {} as ActivatedRouteSnapshot; const state = { url: '/consultation' } as RouterStateSnapshot; beforeEach(() => { urlTreeFactice = {} as UrlTree; validerSessionSpy = jasmine.createSpy('validerSession'); const authServiceMock: Partial = { estAuthentifie: (() => estAuthentifieValeur) as AuthService['estAuthentifie'], validerSession: validerSessionSpy as unknown as AuthService['validerSession'] }; createUrlTreeSpy = jasmine.createSpy('createUrlTree').and.returnValue(urlTreeFactice); const routerMock: Partial = { createUrlTree: createUrlTreeSpy as unknown as Router['createUrlTree'] }; TestBed.configureTestingModule({ providers: [ { provide: AuthService, useValue: authServiceMock }, { provide: Router, useValue: routerMock } ] }); }); /** Résout l'Observable (ou la valeur synchrone) retourné par la garde. */ function resoudre(resultat: unknown): Promise { return isObservable(resultat) ? new Promise((resolve) => (resultat as Observable).subscribe((v) => resolve(v))) : Promise.resolve(resultat); } it('retourne true lorsque l utilisateur est authentifié et que la session est valide côté serveur', async () => { estAuthentifieValeur = true; validerSessionSpy.and.returnValue(of({ authentifie: true })); const resultat = await resoudre( TestBed.runInInjectionContext(() => authGuard(route, state)) ); expect(resultat).toBeTrue(); expect(validerSessionSpy).toHaveBeenCalled(); expect(createUrlTreeSpy).not.toHaveBeenCalled(); }); it('retourne un UrlTree vers /login lorsque aucun jeton local n est présent', async () => { estAuthentifieValeur = false; const resultat = await resoudre( TestBed.runInInjectionContext(() => authGuard(route, state)) ); expect(validerSessionSpy).not.toHaveBeenCalled(); expect(createUrlTreeSpy).toHaveBeenCalledWith(['/login']); expect(resultat).toBe(urlTreeFactice); }); it('retourne un UrlTree vers /login lorsque la validation de session échoue (jeton résiduel invalide)', async () => { estAuthentifieValeur = true; validerSessionSpy.and.returnValue(throwError(() => new Error('401'))); const resultat = await resoudre( TestBed.runInInjectionContext(() => authGuard(route, state)) ); expect(validerSessionSpy).toHaveBeenCalled(); expect(createUrlTreeSpy).toHaveBeenCalledWith(['/login']); expect(resultat).toBe(urlTreeFactice); }); });