diff --git a/src/main/java/com/gbcm/server/impl/entity/User.java b/src/main/java/com/gbcm/server/impl/entity/User.java index 46b6a1b..8bc9261 100644 --- a/src/main/java/com/gbcm/server/impl/entity/User.java +++ b/src/main/java/com/gbcm/server/impl/entity/User.java @@ -4,6 +4,7 @@ import java.time.LocalDateTime; import java.util.List; import com.gbcm.server.api.enums.UserRole; +import com.gbcm.server.api.enums.UserStatus; import io.quarkus.security.jpa.Password; import io.quarkus.security.jpa.Roles; @@ -44,8 +45,8 @@ import jakarta.validation.constraints.Size; @NamedQueries({ @NamedQuery(name = "User.findByEmail", query = "SELECT u FROM User u WHERE u.email = :email AND u.deleted = false"), - @NamedQuery(name = "User.findActiveUsers", - query = "SELECT u FROM User u WHERE u.active = true AND u.deleted = false"), + @NamedQuery(name = "User.findActiveUsers", + query = "SELECT u FROM User u WHERE u.status = 'ACTIVE' AND u.deleted = false"), @NamedQuery(name = "User.findByRole", query = "SELECT u FROM User u WHERE u.role = :role AND u.deleted = false"), @NamedQuery(name = "User.searchByNameOrEmail", @@ -113,6 +114,15 @@ public class User extends BaseEntity { @NotNull(message = "Le rôle est obligatoire") private UserRole role; + /** + * Statut de l'utilisateur dans le système. + * Détermine si l'utilisateur peut se connecter et utiliser le système. + */ + @Enumerated(EnumType.STRING) + @Column(name = "status", nullable = false, length = 20) + @NotNull(message = "Le statut est obligatoire") + private UserStatus status = UserStatus.ACTIVE; + /** * Rôle de l'utilisateur sous forme de String pour Quarkus Security JPA. * Cette propriété est utilisée par le système de sécurité pour l'authentification. @@ -286,7 +296,7 @@ public class User extends BaseEntity { /** * Méthode de recherche par email. - * + * * @param email l'adresse email à rechercher * @return l'utilisateur trouvé ou null */ @@ -294,6 +304,16 @@ public class User extends BaseEntity { return find("#User.findByEmail", email).firstResult(); } + /** + * Méthode de vérification d'existence par email. + * + * @param email l'adresse email à vérifier + * @return true si l'utilisateur existe, false sinon + */ + public static boolean existsByEmail(String email) { + return find("#User.findByEmail", email).count() > 0; + } + /** * Méthode de recherche des utilisateurs actifs. * @@ -382,6 +402,14 @@ public class User extends BaseEntity { this.role = role; } + public UserStatus getStatus() { + return status; + } + + public void setStatus(UserStatus status) { + this.status = status; + } + public boolean isActive() { return active; } diff --git a/src/test/java/com/gbcm/server/impl/entity/UserEntityTest.java b/src/test/java/com/gbcm/server/impl/entity/UserEntityTest.java new file mode 100644 index 0000000..cc1f44d --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/entity/UserEntityTest.java @@ -0,0 +1,338 @@ +package com.gbcm.server.impl.entity; + +import com.gbcm.server.api.enums.UserRole; +import com.gbcm.server.api.enums.UserStatus; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static org.assertj.core.api.Assertions.*; + +/** + * Tests unitaires pour l'entité User. + * + * Vérifie le comportement de l'entité User GBCM + * incluant la persistance, les validations et les méthodes métier. + * + * @author GBCM Team + * @version 1.0.0 + * @since 2024-01-01 + */ +@QuarkusTest +@DisplayName("Tests unitaires - User Entity") +class UserEntityTest { + + @Inject + EntityManager entityManager; + + /** + * Test de création d'un utilisateur. + * Vérifie qu'un utilisateur peut être créé et persisté. + */ + @Test + @Transactional + @DisplayName("Création d'un utilisateur") + void testCreateUser() { + // Given + User user = new User(); + user.setFirstName("John"); + user.setLastName("Doe"); + user.setEmail("john.doe@gbcm.com"); + user.setPasswordHash("$2a$10$hashedpassword"); + user.setRole(UserRole.CLIENT); + user.setStatus(UserStatus.ACTIVE); + + // When + user.persist(); + entityManager.flush(); + + // Then + assertThat(user.getId()).isNotNull(); + assertThat(user.getCreatedAt()).isNotNull(); + assertThat(user.getUpdatedAt()).isNotNull(); + assertThat(user.getFirstName()).isEqualTo("John"); + assertThat(user.getLastName()).isEqualTo("Doe"); + assertThat(user.getEmail()).isEqualTo("john.doe@gbcm.com"); + assertThat(user.getRole()).isEqualTo(UserRole.CLIENT); + assertThat(user.getStatus()).isEqualTo(UserStatus.ACTIVE); + assertThat(user.isDeleted()).isFalse(); + } + + /** + * Test de recherche d'utilisateur par email. + * Vérifie qu'un utilisateur peut être trouvé par son email. + */ + @Test + @Transactional + @DisplayName("Recherche d'utilisateur par email") + void testFindByEmail() { + // Given + User user = new User(); + user.setFirstName("Jane"); + user.setLastName("Smith"); + user.setEmail("jane.smith@gbcm.com"); + user.setPasswordHash("$2a$10$hashedpassword"); + user.setRole(UserRole.COACH); + user.setStatus(UserStatus.ACTIVE); + user.persist(); + entityManager.flush(); + + // When + User foundUser = User.findByEmail("jane.smith@gbcm.com"); + + // Then + assertThat(foundUser).isNotNull(); + assertThat(foundUser.getEmail()).isEqualTo("jane.smith@gbcm.com"); + assertThat(foundUser.getFirstName()).isEqualTo("Jane"); + assertThat(foundUser.getLastName()).isEqualTo("Smith"); + assertThat(foundUser.getRole()).isEqualTo(UserRole.COACH); + } + + /** + * Test de recherche d'utilisateur par email inexistant. + * Vérifie qu'aucun utilisateur n'est trouvé. + */ + @Test + @DisplayName("Recherche d'utilisateur par email inexistant") + void testFindByEmail_NotFound() { + // When + User foundUser = User.findByEmail("nonexistent@gbcm.com"); + + // Then + assertThat(foundUser).isNull(); + } + + /** + * Test de vérification d'existence par email. + * Vérifie qu'un utilisateur existant est détecté. + */ + @Test + @Transactional + @DisplayName("Vérification d'existence par email") + void testExistsByEmail() { + // Given + User user = new User(); + user.setFirstName("Bob"); + user.setLastName("Wilson"); + user.setEmail("bob.wilson@gbcm.com"); + user.setPasswordHash("$2a$10$hashedpassword"); + user.setRole(UserRole.CLIENT); + user.setStatus(UserStatus.ACTIVE); + user.persist(); + entityManager.flush(); + + // When + boolean exists = User.existsByEmail("bob.wilson@gbcm.com"); + boolean notExists = User.existsByEmail("nonexistent@gbcm.com"); + + // Then + assertThat(exists).isTrue(); + assertThat(notExists).isFalse(); + } + + /** + * Test de recherche d'utilisateurs actifs. + * Vérifie que seuls les utilisateurs actifs sont retournés. + */ + @Test + @Transactional + @DisplayName("Recherche d'utilisateurs actifs") + void testFindActiveUsers() { + // Given + User activeUser = new User(); + activeUser.setFirstName("Active"); + activeUser.setLastName("User"); + activeUser.setEmail("active@gbcm.com"); + activeUser.setPasswordHash("$2a$10$hashedpassword"); + activeUser.setRole(UserRole.CLIENT); + activeUser.setStatus(UserStatus.ACTIVE); + activeUser.persist(); + + User inactiveUser = new User(); + inactiveUser.setFirstName("Inactive"); + inactiveUser.setLastName("User"); + inactiveUser.setEmail("inactive@gbcm.com"); + inactiveUser.setPasswordHash("$2a$10$hashedpassword"); + inactiveUser.setRole(UserRole.CLIENT); + inactiveUser.setStatus(UserStatus.INACTIVE); + inactiveUser.persist(); + + entityManager.flush(); + + // When + var activeUsers = User.findActiveUsers(); + + // Then + assertThat(activeUsers).isNotEmpty(); + assertThat(activeUsers).allMatch(user -> user.getStatus() == UserStatus.ACTIVE); + } + + /** + * Test de la méthode getRoleString. + * Vérifie que le rôle est retourné sous forme de chaîne. + */ + @Test + @DisplayName("Méthode getRoleString") + void testGetRoleString() { + // Given + User user = new User(); + user.setRole(UserRole.ADMIN); + + // When + String roleString = user.getRoleString(); + + // Then + assertThat(roleString).isEqualTo("ADMIN"); + } + + /** + * Test de la méthode getRoleString avec rôle null. + * Vérifie qu'une chaîne vide est retournée. + */ + @Test + @DisplayName("Méthode getRoleString avec rôle null") + void testGetRoleString_NullRole() { + // Given + User user = new User(); + user.setRole(null); + + // When + String roleString = user.getRoleString(); + + // Then + assertThat(roleString).isEmpty(); + } + + /** + * Test de mise à jour automatique du timestamp. + * Vérifie que updatedAt est mis à jour automatiquement. + */ + @Test + @Transactional + @DisplayName("Mise à jour automatique du timestamp") + void testAutoUpdateTimestamp() { + // Given + User user = new User(); + user.setFirstName("Test"); + user.setLastName("User"); + user.setEmail("test@gbcm.com"); + user.setPasswordHash("$2a$10$hashedpassword"); + user.setRole(UserRole.CLIENT); + user.setStatus(UserStatus.ACTIVE); + user.persist(); + entityManager.flush(); + + LocalDateTime originalUpdatedAt = user.getUpdatedAt(); + + // When + try { + Thread.sleep(1000); // Attendre 1 seconde + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + user.setFirstName("Updated"); + user.persist(); + entityManager.flush(); + + // Then + assertThat(user.getUpdatedAt()).isAfter(originalUpdatedAt); + } + + /** + * Test de suppression logique. + * Vérifie que la suppression logique fonctionne. + */ + @Test + @Transactional + @DisplayName("Suppression logique") + void testSoftDelete() { + // Given + User user = new User(); + user.setFirstName("ToDelete"); + user.setLastName("User"); + user.setEmail("todelete@gbcm.com"); + user.setPasswordHash("$2a$10$hashedpassword"); + user.setRole(UserRole.CLIENT); + user.setStatus(UserStatus.ACTIVE); + user.persist(); + entityManager.flush(); + + Long userId = user.getId(); + + // When + user.setDeleted(true); + user.persist(); + entityManager.flush(); + + // Then + User foundUser = User.findById(userId); + assertThat(foundUser).isNotNull(); + assertThat(foundUser.isDeleted()).isTrue(); + } + + /** + * Test de validation des contraintes. + * Vérifie que les contraintes de validation sont respectées. + */ + @Test + @Transactional + @DisplayName("Validation des contraintes") + void testValidationConstraints() { + // Given + User user = new User(); + user.setFirstName("Valid"); + user.setLastName("User"); + user.setEmail("valid@gbcm.com"); + user.setPasswordHash("$2a$10$hashedpassword"); + user.setRole(UserRole.CLIENT); + user.setStatus(UserStatus.ACTIVE); + + // When & Then + assertThatCode(() -> { + user.persist(); + entityManager.flush(); + }).doesNotThrowAnyException(); + + assertThat(user.getId()).isNotNull(); + assertThat(user.getCreatedAt()).isNotNull(); + assertThat(user.getUpdatedAt()).isNotNull(); + } + + /** + * Test de performance des requêtes. + * Vérifie que les requêtes sont performantes. + */ + @Test + @Transactional + @DisplayName("Performance des requêtes") + void testQueryPerformance() { + // Given + for (int i = 0; i < 10; i++) { + User user = new User(); + user.setFirstName("User" + i); + user.setLastName("Test"); + user.setEmail("user" + i + "@gbcm.com"); + user.setPasswordHash("$2a$10$hashedpassword"); + user.setRole(UserRole.CLIENT); + user.setStatus(UserStatus.ACTIVE); + user.persist(); + } + entityManager.flush(); + + // When + long startTime = System.currentTimeMillis(); + var users = User.findActiveUsers(); + long endTime = System.currentTimeMillis(); + + // Then + assertThat(users).isNotEmpty(); + assertThat(endTime - startTime).isLessThan(1000); // Moins de 1 seconde + } +} diff --git a/src/test/java/com/gbcm/server/impl/resource/AuthResourceIT.java b/src/test/java/com/gbcm/server/impl/resource/AuthResourceIT.java new file mode 100644 index 0000000..39a4d5b --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/resource/AuthResourceIT.java @@ -0,0 +1,318 @@ +package com.gbcm.server.impl.resource; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.notNullValue; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.gbcm.server.api.dto.auth.LoginRequestDTO; + +import io.quarkus.test.junit.QuarkusTest; +import static io.restassured.RestAssured.given; +import io.restassured.http.ContentType; + +/** + * Tests d'intégration pour AuthResource. + * + * Vérifie le comportement de tous les endpoints d'authentification GBCM + * incluant la connexion, déconnexion et réinitialisation de mot de passe. + * + * @author GBCM Team + * @version 1.0.0 + * @since 2024-01-01 + */ +@QuarkusTest +@DisplayName("Tests d'intégration - AuthResource") +class AuthResourceIT { + + /** + * Test de connexion avec des identifiants valides. + * Vérifie qu'un utilisateur peut se connecter avec succès. + */ + @Test + @DisplayName("Connexion avec identifiants valides") + void testLogin_ValidCredentials() { + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail("admin@gbcm.com"); + loginRequest.setPassword("admin123"); + + given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(200) + .body("accessToken", notNullValue()) + .body("refreshToken", notNullValue()) + .body("user", notNullValue()) + .body("user.email", equalTo("admin@gbcm.com")); + } + + /** + * Test de connexion avec des identifiants invalides. + * Vérifie qu'une erreur 401 est retournée. + */ + @Test + @DisplayName("Connexion avec identifiants invalides") + void testLogin_InvalidCredentials() { + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail("admin@gbcm.com"); + loginRequest.setPassword("wrongpassword"); + + given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(401); + } + + /** + * Test de connexion avec email null. + * Vérifie qu'une erreur 400 est retournée. + */ + @Test + @DisplayName("Connexion avec email null") + void testLogin_NullEmail() { + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail(null); + loginRequest.setPassword("admin123"); + + given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(400); + } + + /** + * Test de connexion avec mot de passe null. + * Vérifie qu'une erreur 400 est retournée. + */ + @Test + @DisplayName("Connexion avec mot de passe null") + void testLogin_NullPassword() { + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail("admin@gbcm.com"); + loginRequest.setPassword(null); + + given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(400); + } + + /** + * Test de déconnexion. + * Vérifie qu'un utilisateur peut se déconnecter. + */ + @Test + @DisplayName("Déconnexion") + void testLogout() { + given() + .contentType(ContentType.JSON) + .when() + .post("/api/auth/logout") + .then() + .statusCode(200); + } + + /** + * Test de demande de réinitialisation de mot de passe. + * Vérifie qu'une demande de réinitialisation peut être faite. + */ + @Test + @DisplayName("Demande de réinitialisation de mot de passe") + void testRequestPasswordReset() { + given() + .contentType("application/x-www-form-urlencoded") + .formParam("email", "admin@gbcm.com") + .when() + .post("/api/auth/request-password-reset") + .then() + .statusCode(200); + } + + /** + * Test de demande de réinitialisation avec email invalide. + * Vérifie qu'une erreur 404 est retournée. + */ + @Test + @DisplayName("Demande de réinitialisation avec email invalide") + void testRequestPasswordReset_InvalidEmail() { + given() + .contentType("application/x-www-form-urlencoded") + .formParam("email", "nonexistent@gbcm.com") + .when() + .post("/api/auth/request-password-reset") + .then() + .statusCode(404); + } + + /** + * Test de demande de réinitialisation avec email null. + * Vérifie qu'une erreur 400 est retournée. + */ + @Test + @DisplayName("Demande de réinitialisation avec email null") + void testRequestPasswordReset_NullEmail() { + given() + .contentType("application/x-www-form-urlencoded") + .formParam("email", (String) null) + .when() + .post("/api/auth/request-password-reset") + .then() + .statusCode(400); + } + + /** + * Test de validation de token. + * Vérifie qu'un token peut être validé. + */ + @Test + @DisplayName("Validation de token") + void testValidateToken() { + // D'abord se connecter pour obtenir un token + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail("admin@gbcm.com"); + loginRequest.setPassword("admin123"); + + String accessToken = given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(200) + .extract() + .path("accessToken"); + + // Valider le token + given() + .contentType(ContentType.JSON) + .formParam("token", accessToken) + .when() + .post("/api/auth/validate-token") + .then() + .statusCode(200); + } + + /** + * Test de validation de token invalide. + * Vérifie qu'une erreur 401 est retournée. + */ + @Test + @DisplayName("Validation de token invalide") + void testValidateToken_Invalid() { + given() + .contentType("application/x-www-form-urlencoded") + .formParam("token", "invalid-token") + .when() + .post("/api/auth/validate-token") + .then() + .statusCode(401); + } + + /** + * Test de validation de token null. + * Vérifie qu'une erreur 400 est retournée. + */ + @Test + @DisplayName("Validation de token null") + void testValidateToken_Null() { + given() + .contentType("application/x-www-form-urlencoded") + .formParam("token", (String) null) + .when() + .post("/api/auth/validate-token") + .then() + .statusCode(400); + } + + /** + * Test de rafraîchissement de token. + * Vérifie qu'un token peut être rafraîchi. + */ + @Test + @DisplayName("Rafraîchissement de token") + void testRefreshToken() { + // D'abord se connecter pour obtenir un refresh token + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail("admin@gbcm.com"); + loginRequest.setPassword("admin123"); + + String refreshToken = given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(200) + .extract() + .path("refreshToken"); + + // Rafraîchir le token + given() + .contentType("application/x-www-form-urlencoded") + .formParam("refreshToken", refreshToken) + .when() + .post("/api/auth/refresh-token") + .then() + .statusCode(200) + .body("accessToken", notNullValue()) + .body("refreshToken", notNullValue()); + } + + /** + * Test de rafraîchissement avec token invalide. + * Vérifie qu'une erreur 401 est retournée. + */ + @Test + @DisplayName("Rafraîchissement avec token invalide") + void testRefreshToken_Invalid() { + given() + .contentType("application/x-www-form-urlencoded") + .formParam("refreshToken", "invalid-refresh-token") + .when() + .post("/api/auth/refresh-token") + .then() + .statusCode(401); + } + + /** + * Test de performance des endpoints d'authentification. + * Vérifie que les endpoints répondent rapidement. + */ + @Test + @DisplayName("Performance des endpoints d'authentification") + void testAuthPerformance() { + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail("admin@gbcm.com"); + loginRequest.setPassword("admin123"); + + long startTime = System.currentTimeMillis(); + + given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(200) + .time(lessThan(5000L)); // Moins de 5 secondes + + long endTime = System.currentTimeMillis(); + long duration = endTime - startTime; + + // Vérifier que la connexion prend moins de 3 secondes + assert duration < 3000; + } +} diff --git a/src/test/java/com/gbcm/server/impl/resource/UserResourceIT.java b/src/test/java/com/gbcm/server/impl/resource/UserResourceIT.java new file mode 100644 index 0000000..32dc483 --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/resource/UserResourceIT.java @@ -0,0 +1,375 @@ +package com.gbcm.server.impl.resource; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.notNullValue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.gbcm.server.api.dto.auth.LoginRequestDTO; +import com.gbcm.server.api.dto.user.CreateUserDTO; +import com.gbcm.server.api.dto.user.UpdateUserDTO; +import com.gbcm.server.api.enums.UserRole; +import com.gbcm.server.api.enums.UserStatus; + +import io.quarkus.test.junit.QuarkusTest; +import static io.restassured.RestAssured.given; +import io.restassured.http.ContentType; + +/** + * Tests d'intégration pour UserResource. + * + * Vérifie le comportement de tous les endpoints de gestion des utilisateurs GBCM + * incluant la création, lecture, mise à jour et suppression d'utilisateurs. + * + * @author GBCM Team + * @version 1.0.0 + * @since 2024-01-01 + */ +@QuarkusTest +@DisplayName("Tests d'intégration - UserResource") +class UserResourceIT { + + private String adminToken; + + /** + * Configuration initiale avant chaque test. + * Obtient un token d'administrateur pour les tests. + */ + @BeforeEach + void setUp() { + LoginRequestDTO loginRequest = new LoginRequestDTO(); + loginRequest.setEmail("admin@gbcm.com"); + loginRequest.setPassword("admin123"); + + adminToken = given() + .contentType(ContentType.JSON) + .body(loginRequest) + .when() + .post("/api/auth/login") + .then() + .statusCode(200) + .extract() + .path("accessToken"); + } + + /** + * Test de récupération de tous les utilisateurs. + * Vérifie qu'un administrateur peut récupérer la liste des utilisateurs. + */ + @Test + @DisplayName("Récupération de tous les utilisateurs") + void testGetUsers() { + given() + .header("Authorization", "Bearer " + adminToken) + .when() + .get("/api/users") + .then() + .statusCode(200) + .body("content", notNullValue()) + .body("content.size()", greaterThan(0)) + .body("totalElements", greaterThan(0)) + .body("totalPages", greaterThan(0)); + } + + /** + * Test de récupération des utilisateurs avec pagination. + * Vérifie que la pagination fonctionne correctement. + */ + @Test + @DisplayName("Récupération des utilisateurs avec pagination") + void testGetUsers_WithPagination() { + given() + .header("Authorization", "Bearer " + adminToken) + .queryParam("page", 0) + .queryParam("size", 5) + .when() + .get("/api/users") + .then() + .statusCode(200) + .body("content", notNullValue()) + .body("size", equalTo(5)) + .body("number", equalTo(0)); + } + + /** + * Test de récupération d'un utilisateur par ID. + * Vérifie qu'un utilisateur existant peut être récupéré. + */ + @Test + @DisplayName("Récupération d'un utilisateur par ID") + void testGetUserById() { + given() + .header("Authorization", "Bearer " + adminToken) + .when() + .get("/api/users/1") + .then() + .statusCode(200) + .body("id", equalTo(1)) + .body("email", notNullValue()) + .body("firstName", notNullValue()) + .body("lastName", notNullValue()) + .body("role", notNullValue()); + } + + /** + * Test de récupération d'un utilisateur inexistant. + * Vérifie qu'une erreur 404 est retournée. + */ + @Test + @DisplayName("Récupération d'un utilisateur inexistant") + void testGetUserById_NotFound() { + given() + .header("Authorization", "Bearer " + adminToken) + .when() + .get("/api/users/99999") + .then() + .statusCode(404); + } + + /** + * Test de création d'un nouvel utilisateur. + * Vérifie qu'un administrateur peut créer un utilisateur. + */ + @Test + @DisplayName("Création d'un nouvel utilisateur") + void testCreateUser() { + CreateUserDTO createUser = new CreateUserDTO(); + createUser.setFirstName("Test"); + createUser.setLastName("User"); + createUser.setEmail("test.user@gbcm.com"); + createUser.setRole(UserRole.CLIENT); + + given() + .header("Authorization", "Bearer " + adminToken) + .contentType(ContentType.JSON) + .body(createUser) + .when() + .post("/api/users") + .then() + .statusCode(201) + .body("firstName", equalTo("Test")) + .body("lastName", equalTo("User")) + .body("email", equalTo("test.user@gbcm.com")) + .body("role", equalTo("CLIENT")) + .body("id", notNullValue()); + } + + /** + * Test de création d'un utilisateur avec email existant. + * Vérifie qu'une erreur 409 est retournée. + */ + @Test + @DisplayName("Création d'un utilisateur avec email existant") + void testCreateUser_EmailExists() { + CreateUserDTO createUser = new CreateUserDTO(); + createUser.setFirstName("Test"); + createUser.setLastName("User"); + createUser.setEmail("admin@gbcm.com"); // Email déjà existant + createUser.setRole(UserRole.CLIENT); + + given() + .header("Authorization", "Bearer " + adminToken) + .contentType(ContentType.JSON) + .body(createUser) + .when() + .post("/api/users") + .then() + .statusCode(409); + } + + /** + * Test de création d'un utilisateur avec données invalides. + * Vérifie qu'une erreur 400 est retournée. + */ + @Test + @DisplayName("Création d'un utilisateur avec données invalides") + void testCreateUser_InvalidData() { + CreateUserDTO createUser = new CreateUserDTO(); + createUser.setFirstName(""); // Prénom vide + createUser.setLastName("User"); + createUser.setEmail("invalid-email"); // Email invalide + createUser.setRole(UserRole.CLIENT); + + given() + .header("Authorization", "Bearer " + adminToken) + .contentType(ContentType.JSON) + .body(createUser) + .when() + .post("/api/users") + .then() + .statusCode(400); + } + + /** + * Test de mise à jour d'un utilisateur. + * Vérifie qu'un utilisateur peut être mis à jour. + */ + @Test + @DisplayName("Mise à jour d'un utilisateur") + void testUpdateUser() { + UpdateUserDTO updateUser = new UpdateUserDTO(); + updateUser.setFirstName("Updated"); + updateUser.setLastName("User"); + updateUser.setEmail("updated.user@gbcm.com"); + updateUser.setRole(UserRole.COACH); + + given() + .header("Authorization", "Bearer " + adminToken) + .contentType(ContentType.JSON) + .body(updateUser) + .when() + .put("/api/users/2") + .then() + .statusCode(200) + .body("firstName", equalTo("Updated")) + .body("lastName", equalTo("User")) + .body("email", equalTo("updated.user@gbcm.com")) + .body("role", equalTo("COACH")); + } + + /** + * Test de mise à jour d'un utilisateur inexistant. + * Vérifie qu'une erreur 404 est retournée. + */ + @Test + @DisplayName("Mise à jour d'un utilisateur inexistant") + void testUpdateUser_NotFound() { + UpdateUserDTO updateUser = new UpdateUserDTO(); + updateUser.setFirstName("Updated"); + updateUser.setLastName("User"); + updateUser.setEmail("updated.user@gbcm.com"); + updateUser.setRole(UserRole.COACH); + + given() + .header("Authorization", "Bearer " + adminToken) + .contentType(ContentType.JSON) + .body(updateUser) + .when() + .put("/api/users/99999") + .then() + .statusCode(404); + } + + /** + * Test de suppression d'un utilisateur. + * Vérifie qu'un utilisateur peut être supprimé. + */ + @Test + @DisplayName("Suppression d'un utilisateur") + void testDeleteUser() { + given() + .header("Authorization", "Bearer " + adminToken) + .when() + .delete("/api/users/3") + .then() + .statusCode(204); + } + + /** + * Test de suppression d'un utilisateur inexistant. + * Vérifie qu'une erreur 404 est retournée. + */ + @Test + @DisplayName("Suppression d'un utilisateur inexistant") + void testDeleteUser_NotFound() { + given() + .header("Authorization", "Bearer " + adminToken) + .when() + .delete("/api/users/99999") + .then() + .statusCode(404); + } + + /** + * Test de changement de statut d'un utilisateur. + * Vérifie qu'un statut d'utilisateur peut être changé. + */ + @Test + @DisplayName("Changement de statut d'un utilisateur") + void testChangeUserStatus() { + given() + .header("Authorization", "Bearer " + adminToken) + .contentType("application/x-www-form-urlencoded") + .formParam("status", UserStatus.INACTIVE.name()) + .when() + .put("/api/users/2/status") + .then() + .statusCode(200) + .body("status", equalTo("INACTIVE")); + } + + /** + * Test de recherche d'utilisateurs. + * Vérifie que la recherche d'utilisateurs fonctionne. + */ + @Test + @DisplayName("Recherche d'utilisateurs") + void testSearchUsers() { + given() + .header("Authorization", "Bearer " + adminToken) + .queryParam("term", "admin") + .when() + .get("/api/users/search") + .then() + .statusCode(200) + .body("content", notNullValue()) + .body("content.size()", greaterThan(0)); + } + + /** + * Test de recherche d'utilisateurs avec terme vide. + * Vérifie qu'une erreur 400 est retournée. + */ + @Test + @DisplayName("Recherche d'utilisateurs avec terme vide") + void testSearchUsers_EmptyTerm() { + given() + .header("Authorization", "Bearer " + adminToken) + .queryParam("term", "") + .when() + .get("/api/users/search") + .then() + .statusCode(400); + } + + /** + * Test d'accès non autorisé. + * Vérifie qu'une erreur 401 est retournée sans token. + */ + @Test + @DisplayName("Accès non autorisé") + void testUnauthorizedAccess() { + given() + .when() + .get("/api/users") + .then() + .statusCode(401); + } + + /** + * Test de performance des endpoints utilisateurs. + * Vérifie que les endpoints répondent rapidement. + */ + @Test + @DisplayName("Performance des endpoints utilisateurs") + void testUserEndpointsPerformance() { + given() + .header("Authorization", "Bearer " + adminToken) + .when() + .get("/api/users") + .then() + .statusCode(200) + .time(lessThan(3000L)); // Moins de 3 secondes + + given() + .header("Authorization", "Bearer " + adminToken) + .when() + .get("/api/users/1") + .then() + .statusCode(200) + .time(lessThan(2000L)); // Moins de 2 secondes + } +} diff --git a/src/test/java/com/gbcm/server/impl/service/UserServiceImplTest.java b/src/test/java/com/gbcm/server/impl/service/UserServiceImplTest.java new file mode 100644 index 0000000..1e172a0 --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/service/UserServiceImplTest.java @@ -0,0 +1,310 @@ +package com.gbcm.server.impl.service; + +import com.gbcm.server.api.dto.common.PagedResultDTO; +import com.gbcm.server.api.dto.user.CreateUserDTO; +import com.gbcm.server.api.dto.user.UpdateUserDTO; +import com.gbcm.server.api.dto.user.UserDTO; +import com.gbcm.server.api.enums.UserRole; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +/** + * Tests unitaires pour UserServiceImpl. + * + * Vérifie le comportement de tous les services de gestion des utilisateurs GBCM + * incluant la création, modification, suppression et recherche d'utilisateurs. + * + * @author GBCM Team + * @version 1.0.0 + * @since 2024-01-01 + */ +@QuarkusTest +@DisplayName("Tests unitaires - UserServiceImpl") +class UserServiceImplTest { + + @Inject + UserServiceImpl userService; + + private CreateUserDTO validCreateUserDTO; + private UpdateUserDTO validUpdateUserDTO; + + /** + * Configuration initiale avant chaque test. + * Prépare les objets de test. + */ + @BeforeEach + void setUp() { + // Préparation des données de test + validCreateUserDTO = new CreateUserDTO(); + validCreateUserDTO.setFirstName("John"); + validCreateUserDTO.setLastName("Doe"); + validCreateUserDTO.setEmail("john.doe@gbcm.com"); + validCreateUserDTO.setPassword("password123"); + validCreateUserDTO.setRole(UserRole.CLIENT); + + validUpdateUserDTO = new UpdateUserDTO(); + validUpdateUserDTO.setFirstName("Jane"); + validUpdateUserDTO.setLastName("Smith"); + validUpdateUserDTO.setEmail("jane.smith@gbcm.com"); + validUpdateUserDTO.setRole(UserRole.COACH); + } + + /** + * Test de service non null. + * Vérifie que l'injection de dépendance fonctionne. + */ + @Test + @DisplayName("Service injecté correctement") + void testServiceInjection() { + assertThat(userService).isNotNull(); + } + + /** + * Test de récupération des utilisateurs avec pagination. + * Vérifie que la pagination fonctionne correctement. + */ + @Test + @DisplayName("Récupération des utilisateurs avec pagination") + void testGetUsers_WithPagination() throws Exception { + // When + PagedResultDTO result = userService.getUsers(0, 10, null, null, null, null); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getContent()).isNotNull(); + assertThat(result.getTotalElements()).isGreaterThanOrEqualTo(0); + assertThat(result.getTotalPages()).isGreaterThanOrEqualTo(0); + assertThat(result.getPage()).isEqualTo(0); + assertThat(result.getSize()).isEqualTo(10); + } + + /** + * Test de récupération d'un utilisateur par ID. + * Vérifie qu'un utilisateur existant est retourné. + */ + @Test + @DisplayName("Récupération d'utilisateur par ID") + void testGetUserById_Success() throws Exception { + // Given + Long userId = 1L; + + // When + UserDTO result = userService.getUserById(userId); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(userId); + assertThat(result.getFirstName()).isNotBlank(); + assertThat(result.getLastName()).isNotBlank(); + assertThat(result.getEmail()).isNotBlank(); + assertThat(result.getRole()).isNotNull(); + } + + /** + * Test de récupération d'un utilisateur avec ID null. + * Vérifie qu'une IllegalArgumentException est levée. + */ + @Test + @DisplayName("Récupération d'utilisateur avec ID null") + void testGetUserById_NullId() { + // When & Then + assertThatThrownBy(() -> userService.getUserById(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("ID utilisateur requis"); + } + + /** + * Test de création d'un nouvel utilisateur. + * Vérifie qu'un utilisateur est créé avec succès. + */ + @Test + @DisplayName("Création d'un nouvel utilisateur") + void testCreateUser_Success() throws Exception { + // When + UserDTO result = userService.createUser(validCreateUserDTO); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isNotNull(); + assertThat(result.getFirstName()).isEqualTo(validCreateUserDTO.getFirstName()); + assertThat(result.getLastName()).isEqualTo(validCreateUserDTO.getLastName()); + assertThat(result.getEmail()).isEqualTo(validCreateUserDTO.getEmail()); + assertThat(result.getRole()).isEqualTo(validCreateUserDTO.getRole()); + } + + /** + * Test de création d'un utilisateur avec DTO null. + * Vérifie qu'une IllegalArgumentException est levée. + */ + @Test + @DisplayName("Création d'utilisateur avec DTO null") + void testCreateUser_NullDTO() { + // When & Then + assertThatThrownBy(() -> userService.createUser(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Données utilisateur requises"); + } + + /** + * Test de mise à jour d'un utilisateur existant. + * Vérifie qu'un utilisateur est mis à jour avec succès. + */ + @Test + @DisplayName("Mise à jour d'un utilisateur existant") + void testUpdateUser_Success() throws Exception { + // Given + Long userId = 1L; + + // When + UserDTO result = userService.updateUser(userId, validUpdateUserDTO); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(userId); + assertThat(result.getFirstName()).isEqualTo(validUpdateUserDTO.getFirstName()); + assertThat(result.getLastName()).isEqualTo(validUpdateUserDTO.getLastName()); + assertThat(result.getEmail()).isEqualTo(validUpdateUserDTO.getEmail()); + assertThat(result.getRole()).isEqualTo(validUpdateUserDTO.getRole()); + } + + /** + * Test de mise à jour d'un utilisateur avec ID null. + * Vérifie qu'une IllegalArgumentException est levée. + */ + @Test + @DisplayName("Mise à jour d'utilisateur avec ID null") + void testUpdateUser_NullId() { + // When & Then + assertThatThrownBy(() -> userService.updateUser(null, validUpdateUserDTO)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("ID utilisateur requis"); + } + + /** + * Test de suppression d'un utilisateur. + * Vérifie qu'un utilisateur est supprimé avec succès. + */ + @Test + @DisplayName("Suppression d'un utilisateur") + void testDeleteUser_Success() throws Exception { + // Given + Long userId = 1L; + + // When & Then + assertThatCode(() -> userService.deleteUser(userId)) + .doesNotThrowAnyException(); + } + + /** + * Test de suppression d'un utilisateur avec ID null. + * Vérifie qu'une IllegalArgumentException est levée. + */ + @Test + @DisplayName("Suppression d'utilisateur avec ID null") + void testDeleteUser_NullId() { + // When & Then + assertThatThrownBy(() -> userService.deleteUser(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("ID utilisateur requis"); + } + + /** + * Test de changement de statut d'un utilisateur. + * Vérifie qu'un statut est changé avec succès. + */ + @Test + @DisplayName("Changement de statut d'un utilisateur") + void testChangeUserStatus_Success() throws Exception { + // Given + Long userId = 1L; + Boolean newStatus = false; + + // When + UserDTO result = userService.changeUserStatus(userId, newStatus); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(userId); + } + + /** + * Test de recherche d'utilisateurs. + * Vérifie que la recherche fonctionne correctement. + */ + @Test + @DisplayName("Recherche d'utilisateurs") + void testSearchUsers_Success() throws Exception { + // Given + String searchTerm = "admin"; + + // When + PagedResultDTO result = userService.searchUsers(searchTerm, 0, 10); + + // Then + assertThat(result).isNotNull(); + assertThat(result.getContent()).isNotNull(); + assertThat(result.getTotalElements()).isGreaterThanOrEqualTo(0); + assertThat(result.getPage()).isEqualTo(0); + assertThat(result.getSize()).isEqualTo(10); + } + + /** + * Test de recherche avec terme null. + * Vérifie qu'une IllegalArgumentException est levée. + */ + @Test + @DisplayName("Recherche avec terme null") + void testSearchUsers_NullTerm() { + // When & Then + assertThatThrownBy(() -> userService.searchUsers(null, 0, 10)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Terme de recherche requis"); + } + + /** + * Test de validation des paramètres de pagination. + * Vérifie que les paramètres invalides sont rejetés. + */ + @Test + @DisplayName("Validation des paramètres de pagination") + void testPaginationValidation() { + // Test avec page négative + assertThatThrownBy(() -> userService.getUsers(-1, 10, null, null, null, null)) + .isInstanceOf(IllegalArgumentException.class); + + // Test avec taille de page négative + assertThatThrownBy(() -> userService.getUsers(0, -1, null, null, null, null)) + .isInstanceOf(IllegalArgumentException.class); + + // Test avec taille de page trop grande + assertThatThrownBy(() -> userService.getUsers(0, 1000, null, null, null, null)) + .isInstanceOf(IllegalArgumentException.class); + } + + /** + * Test de performance basique. + * Vérifie que les opérations sont raisonnablement rapides. + */ + @Test + @DisplayName("Performance basique") + void testBasicPerformance() throws Exception { + // Given + long startTime = System.currentTimeMillis(); + + // When + PagedResultDTO result = userService.getUsers(0, 5, null, null, null, null); + + // Then + long endTime = System.currentTimeMillis(); + long duration = endTime - startTime; + + assertThat(result).isNotNull(); + assertThat(duration).isLessThan(5000); // Moins de 5 secondes + } +}