package dev.lions.unionflow.server.service; import static org.assertj.core.api.Assertions.assertThat; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; import jakarta.inject.Inject; import java.util.Set; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; /** * Tests du service KeycloakService — couvre les branches authentifiées et non authentifiées. */ @QuarkusTest class KeycloakServiceTest { @Inject KeycloakService keycloakService; // ================================================================ // NON AUTHENTIFIÉ // ================================================================ @Nested @DisplayName("Sans contexte d'authentification") class SansContexte { @Test @DisplayName("isAuthenticated retourne false") void isAuthenticated_returnsFalse() { assertThat(keycloakService.isAuthenticated()).isFalse(); } @Test @DisplayName("getCurrentUserId retourne null") void getCurrentUserId_returnsNull() { assertThat(keycloakService.getCurrentUserId()).isNull(); } @Test @DisplayName("getCurrentUserEmail retourne null") void getCurrentUserEmail_returnsNull() { assertThat(keycloakService.getCurrentUserEmail()).isNull(); } @Test @DisplayName("getCurrentUserFullName retourne null") void getCurrentUserFullName_returnsNull() { assertThat(keycloakService.getCurrentUserFullName()).isNull(); } @Test @DisplayName("getCurrentUserRoles retourne set vide") void getCurrentUserRoles_returnsEmpty() { assertThat(keycloakService.getCurrentUserRoles()).isEmpty(); } @Test @DisplayName("hasRole retourne false") void hasRole_returnsFalse() { assertThat(keycloakService.hasRole("ADMIN")).isFalse(); } @Test @DisplayName("hasAnyRole retourne false") void hasAnyRole_returnsFalse() { assertThat(keycloakService.hasAnyRole("ADMIN", "TRESORIER")).isFalse(); } @Test @DisplayName("hasAllRoles retourne false") void hasAllRoles_returnsFalse() { assertThat(keycloakService.hasAllRoles("ADMIN", "TRESORIER")).isFalse(); } @Test @DisplayName("getClaim retourne null") void getClaim_returnsNull() { assertThat((Object) keycloakService.getClaim("email")).isNull(); } @Test @DisplayName("getAllClaimNames retourne set vide") void getAllClaimNames_returnsEmpty() { assertThat(keycloakService.getAllClaimNames()).isEmpty(); } @Test @DisplayName("getUserInfoForLogging retourne message non authentifié") void getUserInfoForLogging_returnsNonAuthentifieMessage() { assertThat(keycloakService.getUserInfoForLogging()).contains("non authentifié"); } @Test @DisplayName("isAdmin retourne false") void isAdmin_returnsFalse() { assertThat(keycloakService.isAdmin()).isFalse(); } @Test @DisplayName("canManageMembers retourne false") void canManageMembers_returnsFalse() { assertThat(keycloakService.canManageMembers()).isFalse(); } @Test @DisplayName("canManageFinances retourne false") void canManageFinances_returnsFalse() { assertThat(keycloakService.canManageFinances()).isFalse(); } @Test @DisplayName("canManageEvents retourne false") void canManageEvents_returnsFalse() { assertThat(keycloakService.canManageEvents()).isFalse(); } @Test @DisplayName("canManageOrganizations retourne false") void canManageOrganizations_returnsFalse() { assertThat(keycloakService.canManageOrganizations()).isFalse(); } @Test @DisplayName("getRawAccessToken retourne null") void getRawAccessToken_returnsNull() { assertThat(keycloakService.getRawAccessToken()).isNull(); } @Test @DisplayName("logSecurityInfo ne lève pas d'exception") void logSecurityInfo_noException() { keycloakService.logSecurityInfo(); // pas d'exception attendue } } // ================================================================ // AUTHENTIFIÉ — UTILISATEUR SIMPLE // ================================================================ @Nested @DisplayName("Avec utilisateur authentifié simple") class AvecUtilisateurSimple { @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("isAuthenticated retourne true") void isAuthenticated_returnsTrue() { assertThat(keycloakService.isAuthenticated()).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getCurrentUserId retourne une valeur non null") void getCurrentUserId_returnsValue() { // Le sujet JWT peut être null en test sans JWT réel, mais isAuthenticated=true // et la méthode ne doit pas lever d'exception String userId = keycloakService.getCurrentUserId(); // Pas d'exception — valeur peut être null selon le contexte test // (le try/catch dans la méthode gère le cas d'erreur) assertThat(keycloakService.isAuthenticated()).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getCurrentUserEmail ne lève pas d'exception") void getCurrentUserEmail_noException() { // Le fallback sur securityIdentity.getPrincipal().getName() est couvert String email = keycloakService.getCurrentUserEmail(); // Peut retourner le principal name comme fallback assertThat(keycloakService.isAuthenticated()).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getCurrentUserFullName ne lève pas d'exception") void getCurrentUserFullName_noException() { String fullName = keycloakService.getCurrentUserFullName(); // Peut être null si les claims given_name/family_name/preferred_username ne sont pas présents assertThat(keycloakService.isAuthenticated()).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getCurrentUserRoles retourne les rôles assignés") void getCurrentUserRoles_returnsRoles() { Set roles = keycloakService.getCurrentUserRoles(); assertThat(roles).contains("MEMBRE"); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("hasRole('MEMBRE') retourne true") void hasRole_membreRole_returnsTrue() { assertThat(keycloakService.hasRole("MEMBRE")).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("hasRole('ADMIN') retourne false quand l'utilisateur n'a pas ce rôle") void hasRole_adminRole_returnsFalse() { assertThat(keycloakService.hasRole("ADMIN")).isFalse(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("hasAnyRole avec rôle correspondant retourne true") void hasAnyRole_avecRoleCorrespondant_returnsTrue() { assertThat(keycloakService.hasAnyRole("ADMIN", "MEMBRE", "TRESORIER")).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("hasAnyRole sans rôle correspondant retourne false") void hasAnyRole_sansRoleCorrespondant_returnsFalse() { assertThat(keycloakService.hasAnyRole("ADMIN", "TRESORIER", "PRESIDENT")).isFalse(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("hasAllRoles avec tous les rôles présents retourne true") void hasAllRoles_tousRolesPresents_returnsTrue() { assertThat(keycloakService.hasAllRoles("MEMBRE")).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("hasAllRoles avec un rôle manquant retourne false") void hasAllRoles_roleManquant_returnsFalse() { assertThat(keycloakService.hasAllRoles("MEMBRE", "ADMIN")).isFalse(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getAllClaimNames ne lève pas d'exception") void getAllClaimNames_noException() { Set claimNames = keycloakService.getAllClaimNames(); assertThat(claimNames).isNotNull(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getUserInfoForLogging retourne une chaîne formatée") void getUserInfoForLogging_returnsFormattedString() { String info = keycloakService.getUserInfoForLogging(); assertThat(info).isNotNull(); assertThat(info).contains("Utilisateur:"); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getRawAccessToken ne lève pas d'exception") void getRawAccessToken_noException() { String token = keycloakService.getRawAccessToken(); // peut être null si jwt n'est pas un OidcJwtCallerPrincipal en mode test assertThat(keycloakService.isAuthenticated()).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getClaim ne lève pas d'exception") void getClaim_noException() { Object claim = keycloakService.getClaim("sub"); assertThat(keycloakService.isAuthenticated()).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("isAdmin retourne false pour un simple membre") void isAdmin_membreRole_returnsFalse() { assertThat(keycloakService.isAdmin()).isFalse(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("canManageMembers retourne false pour un simple membre") void canManageMembers_membreRole_returnsFalse() { assertThat(keycloakService.canManageMembers()).isFalse(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("canManageFinances retourne false pour un simple membre") void canManageFinances_membreRole_returnsFalse() { assertThat(keycloakService.canManageFinances()).isFalse(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("canManageEvents retourne false pour un simple membre") void canManageEvents_membreRole_returnsFalse() { assertThat(keycloakService.canManageEvents()).isFalse(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("canManageOrganizations retourne false pour un simple membre") void canManageOrganizations_membreRole_returnsFalse() { assertThat(keycloakService.canManageOrganizations()).isFalse(); } } // ================================================================ // AUTHENTIFIÉ — ADMIN // ================================================================ @Nested @DisplayName("Avec utilisateur ADMIN") class AvecAdmin { @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("isAdmin avec rôle ADMIN retourne true") void isAdmin_adminRole_returnsTrue() { assertThat(keycloakService.isAdmin()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("isAdmin avec rôle admin (minuscule) retourne true") void isAdmin_adminLowercase_returnsTrue() { assertThat(keycloakService.isAdmin()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageMembers avec rôle ADMIN retourne true") void canManageMembers_adminRole_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageFinances avec rôle ADMIN retourne true") void canManageFinances_adminRole_returnsTrue() { assertThat(keycloakService.canManageFinances()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageEvents avec rôle ADMIN retourne true") void canManageEvents_adminRole_returnsTrue() { assertThat(keycloakService.canManageEvents()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageOrganizations avec rôle ADMIN retourne true") void canManageOrganizations_adminRole_returnsTrue() { assertThat(keycloakService.canManageOrganizations()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("hasAllRoles avec un seul rôle correct retourne true") void hasAllRoles_singleRoleAdmin_returnsTrue() { assertThat(keycloakService.hasAllRoles("ADMIN")).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("hasAnyRole avec ADMIN en premier retourne true immédiatement") void hasAnyRole_adminFirst_returnsTrue() { assertThat(keycloakService.hasAnyRole("ADMIN", "TRESORIER")).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("hasAnyRole avec ADMIN en dernier retourne true après itération complète") void hasAnyRole_adminLast_returnsTrue() { assertThat(keycloakService.hasAnyRole("TRESORIER", "GESTIONNAIRE_MEMBRE", "ADMIN")).isTrue(); } } // ================================================================ // AUTHENTIFIÉ — logSecurityInfo en mode debug // ================================================================ @Nested @DisplayName("logSecurityInfo") class LogSecurityInfo { @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("logSecurityInfo ne lève pas d'exception quand authentifié") void logSecurityInfo_authenticated_noException() { keycloakService.logSecurityInfo(); // pas d'exception attendue — le debug peut être désactivé, ce qui est normal } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getUserInfoForLogging retourne les rôles de l'utilisateur authentifié") void getUserInfoForLogging_authenticated_containsRoles() { String info = keycloakService.getUserInfoForLogging(); assertThat(info).isNotNull(); assertThat(info).contains("Rôles:"); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getClaim avec claim existante retourne une valeur ou null sans exception") void getClaim_existingClaim_noException() { // En contexte test la JWT n'est pas une vraie OIDC JWT — pas de crash attendu Object claim = keycloakService.getClaim("preferred_username"); // Le résultat peut être null ou une valeur selon le contexte test assertThat(keycloakService.isAuthenticated()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN", "TRESORIER"}) @DisplayName("hasAllRoles avec plusieurs rôles correspondants retourne true") void hasAllRoles_multipleRolesPresent_returnsTrue() { assertThat(keycloakService.hasAllRoles("ADMIN", "TRESORIER")).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN", "TRESORIER"}) @DisplayName("hasAnyRole avec plusieurs rôles dont l'un correspond retourne true") void hasAnyRole_multipleRolesOneMatches_returnsTrue() { assertThat(keycloakService.hasAnyRole("MEMBRE", "TRESORIER", "SECRETAIRE")).isTrue(); } } // ================================================================ // AUTHENTIFIÉ — RÔLES SPÉCIALISÉS // ================================================================ @Nested @DisplayName("Avec rôles spécialisés") class AvecRolesSpecialises { @Test @TestSecurity(user = "gestionnaire@test.com", roles = {"GESTIONNAIRE_MEMBRE"}) @DisplayName("canManageMembers avec rôle GESTIONNAIRE_MEMBRE retourne true") void canManageMembers_gestionnaireMembreRole_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "tresorier@test.com", roles = {"TRESORIER"}) @DisplayName("canManageFinances avec rôle TRESORIER retourne true") void canManageFinances_tresorierRole_returnsTrue() { assertThat(keycloakService.canManageFinances()).isTrue(); } @Test @TestSecurity(user = "organisateur@test.com", roles = {"ORGANISATEUR_EVENEMENT"}) @DisplayName("canManageEvents avec rôle ORGANISATEUR_EVENEMENT retourne true") void canManageEvents_organisateurRole_returnsTrue() { assertThat(keycloakService.canManageEvents()).isTrue(); } @Test @TestSecurity(user = "president@test.com", roles = {"PRESIDENT"}) @DisplayName("canManageOrganizations avec rôle PRESIDENT retourne true") void canManageOrganizations_presidentRole_returnsTrue() { assertThat(keycloakService.canManageOrganizations()).isTrue(); } @Test @TestSecurity(user = "president@test.com", roles = {"PRESIDENT"}) @DisplayName("canManageMembers avec rôle PRESIDENT retourne true") void canManageMembers_presidentRole_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "president@test.com", roles = {"PRESIDENT"}) @DisplayName("canManageFinances avec rôle PRESIDENT retourne true") void canManageFinances_presidentRole_returnsTrue() { assertThat(keycloakService.canManageFinances()).isTrue(); } @Test @TestSecurity(user = "secretaire@test.com", roles = {"SECRETAIRE"}) @DisplayName("canManageMembers avec rôle SECRETAIRE retourne true") void canManageMembers_secretaireRole_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "secretaire@test.com", roles = {"SECRETAIRE"}) @DisplayName("canManageEvents avec rôle SECRETAIRE retourne true") void canManageEvents_secretaireRole_returnsTrue() { assertThat(keycloakService.canManageEvents()).isTrue(); } @Test @TestSecurity(user = "tresorier@test.com", roles = {"TRESORIER"}) @DisplayName("canManageFinances avec rôle tresorier (minuscule) retourne true") void canManageFinances_tresorierLowercase_returnsTrue() { assertThat(keycloakService.canManageFinances()).isTrue(); } @Test @TestSecurity(user = "gestionnaire@test.com", roles = {"GESTIONNAIRE_MEMBRE"}) @DisplayName("canManageMembers avec rôle gestionnaire_membre (minuscule) retourne true") void canManageMembers_gestionnaireLowercase_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "multi@test.com", roles = {"TRESORIER", "GESTIONNAIRE_MEMBRE"}) @DisplayName("hasAllRoles avec plusieurs rôles tous présents retourne true") void hasAllRoles_multipleRolesAllPresent_returnsTrue() { assertThat(keycloakService.hasAllRoles("TRESORIER", "GESTIONNAIRE_MEMBRE")).isTrue(); } @Test @TestSecurity(user = "multi@test.com", roles = {"TRESORIER", "GESTIONNAIRE_MEMBRE"}) @DisplayName("hasAllRoles avec un rôle absent retourne false") void hasAllRoles_multipleRolesOneAbsent_returnsFalse() { assertThat(keycloakService.hasAllRoles("TRESORIER", "GESTIONNAIRE_MEMBRE", "PRESIDENT")).isFalse(); } @Test @TestSecurity(user = "organisateur@test.com", roles = {"ORGANISATEUR_EVENEMENT"}) @DisplayName("canManageEvents avec rôle organisateur_evenement (minuscule) retourne true") void canManageEvents_organisateurLowercase_returnsTrue() { assertThat(keycloakService.canManageEvents()).isTrue(); } @Test @TestSecurity(user = "president@test.com", roles = {"PRESIDENT"}) @DisplayName("canManageOrganizations avec rôle president (minuscule) retourne true") void canManageOrganizations_presidentLowercase_returnsTrue() { assertThat(keycloakService.canManageOrganizations()).isTrue(); } @Test @TestSecurity(user = "secretaire@test.com", roles = {"SECRETAIRE"}) @DisplayName("canManageMembers avec rôle secretaire (minuscule) retourne true") void canManageMembers_secretaireLowercase_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "secretaire@test.com", roles = {"SECRETAIRE"}) @DisplayName("canManageEvents avec rôle secretaire (minuscule) retourne true") void canManageEvents_secretaireLowercase_returnsTrue() { assertThat(keycloakService.canManageEvents()).isTrue(); } @Test @TestSecurity(user = "president@test.com", roles = {"PRESIDENT"}) @DisplayName("canManageFinances avec rôle president (minuscule) retourne true") void canManageFinances_presidentLowercase_returnsTrue() { assertThat(keycloakService.canManageFinances()).isTrue(); } @Test @TestSecurity(user = "president@test.com", roles = {"PRESIDENT"}) @DisplayName("canManageMembers avec rôle president (minuscule) retourne true") void canManageMembers_presidentLowercase_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageMembers avec rôle admin (minuscule) retourne true") void canManageMembers_adminLowercase_returnsTrue() { assertThat(keycloakService.canManageMembers()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageFinances avec rôle admin (minuscule) retourne true") void canManageFinances_adminLowercase_returnsTrue() { assertThat(keycloakService.canManageFinances()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageEvents avec rôle admin (minuscule) retourne true") void canManageEvents_adminLowercase_returnsTrue() { assertThat(keycloakService.canManageEvents()).isTrue(); } @Test @TestSecurity(user = "admin@test.com", roles = {"ADMIN"}) @DisplayName("canManageOrganizations avec rôle admin (minuscule) retourne true") void canManageOrganizations_adminLowercase_returnsTrue() { assertThat(keycloakService.canManageOrganizations()).isTrue(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getAllClaimNames avec authentification retourne set non-null") void getAllClaimNames_authenticated_returnsNonNull() { Set claimNames = keycloakService.getAllClaimNames(); assertThat(claimNames).isNotNull(); } @Test @TestSecurity(user = "alice@test.com", roles = {"MEMBRE"}) @DisplayName("getRawAccessToken authentifié ne lève pas d'exception") void getRawAccessToken_authenticated_noException() { // En mode test, jwt.getRawToken() peut retourner null ou une valeur // L'important est que la méthode ne lève pas d'exception String token = keycloakService.getRawAccessToken(); assertThat(keycloakService.isAuthenticated()).isTrue(); } } }