diff --git a/src/main/java/com/gbcm/server/impl/resource/ClientResource.java b/src/main/java/com/gbcm/server/impl/resource/ClientResource.java new file mode 100644 index 0000000..afa2cf5 --- /dev/null +++ b/src/main/java/com/gbcm/server/impl/resource/ClientResource.java @@ -0,0 +1,309 @@ +package com.gbcm.server.impl.resource; + +import com.gbcm.server.api.dto.client.ClientDTO; +import com.gbcm.server.api.dto.client.CreateClientDTO; +import com.gbcm.server.api.dto.client.UpdateClientDTO; +import com.gbcm.server.api.dto.common.PagedResponseDTO; +import com.gbcm.server.api.enums.ServiceType; +import com.gbcm.server.api.exception.GBCMException; +import com.gbcm.server.api.service.ClientService; +import com.gbcm.server.impl.entity.Client.ClientStatus; +import jakarta.annotation.security.RolesAllowed; +import jakarta.inject.Inject; +import jakarta.validation.Valid; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.Parameter; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Contrôleur REST pour la gestion des clients de la plateforme GBCM. + * Expose tous les endpoints CRUD pour les clients avec sécurité basée sur les rôles. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@Path("/api/clients") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Tag(name = "Clients", description = "Gestion des clients GBCM") +public class ClientResource { + + private static final Logger logger = LoggerFactory.getLogger(ClientResource.class); + + @Inject + ClientService clientService; + + /** + * Endpoint pour récupérer la liste paginée des clients. + * + * @param page numéro de page (commence à 0) + * @param size taille de page + * @param sort critères de tri + * @param status filtre par statut + * @param industry filtre par secteur d'activité + * @param search terme de recherche + * @return liste paginée des clients + */ + @GET + @RolesAllowed({"ADMIN", "MANAGER", "COACH"}) + @Operation( + summary = "Liste des clients", + description = "Récupère la liste paginée des clients avec filtres optionnels" + ) + @APIResponses({ + @APIResponse( + responseCode = "200", + description = "Liste récupérée avec succès", + content = @Content(schema = @Schema(implementation = PagedResponseDTO.class)) + ), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response getClients( + @Parameter(description = "Numéro de page (commence à 0)", example = "0") + @QueryParam("page") @DefaultValue("0") int page, + + @Parameter(description = "Taille de page", example = "20") + @QueryParam("size") @DefaultValue("20") int size, + + @Parameter(description = "Tri (ex: companyName,asc ou convertedAt,desc)") + @QueryParam("sort") String sort, + + @Parameter(description = "Filtre par statut") + @QueryParam("status") ClientStatus status, + + @Parameter(description = "Filtre par secteur d'activité") + @QueryParam("industry") String industry, + + @Parameter(description = "Terme de recherche (nom entreprise, email)") + @QueryParam("search") String search + ) { + try { + logger.info("Récupération de la liste des clients - page: {}, size: {}", page, size); + + PagedResponseDTO clients = clientService.getClients(page, size, sort, status, industry, search); + + logger.info("Liste des clients récupérée avec succès - {} éléments", clients.getTotalElements()); + return Response.ok(clients).build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la récupération des clients: {}", e.getMessage()); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Erreur: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la récupération des clients: {}", e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour récupérer un client par son ID. + * + * @param id l'identifiant du client + * @return le client trouvé + */ + @GET + @Path("/{id}") + @RolesAllowed({"ADMIN", "MANAGER", "COACH", "CLIENT"}) + @Operation( + summary = "Récupérer un client", + description = "Récupère un client par son identifiant" + ) + @APIResponses({ + @APIResponse( + responseCode = "200", + description = "Client trouvé", + content = @Content(schema = @Schema(implementation = ClientDTO.class)) + ), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "404", description = "Client non trouvé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response getClientById( + @Parameter(description = "Identifiant du client", required = true) + @PathParam("id") Long id + ) { + try { + logger.info("Récupération du client avec l'ID: {}", id); + + ClientDTO client = clientService.getClientById(id); + + logger.info("Client récupéré avec succès: {}", client.getCompanyName()); + return Response.ok(client).build(); + + } catch (GBCMException e) { + logger.warn("Client non trouvé avec l'ID {}: {}", id, e.getMessage()); + return Response.status(Response.Status.NOT_FOUND) + .entity("Client non trouvé: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la récupération du client {}: {}", id, e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour créer un nouveau client. + * + * @param createClientDTO les données du nouveau client + * @return le client créé + */ + @POST + @RolesAllowed({"ADMIN", "MANAGER"}) + @Operation( + summary = "Créer un client", + description = "Crée un nouveau client dans le système" + ) + @APIResponses({ + @APIResponse( + responseCode = "201", + description = "Client créé avec succès", + content = @Content(schema = @Schema(implementation = ClientDTO.class)) + ), + @APIResponse(responseCode = "400", description = "Données invalides"), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "409", description = "Client déjà existant"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response createClient( + @Parameter(description = "Données du nouveau client", required = true) + @Valid CreateClientDTO createClientDTO + ) { + try { + logger.info("Création d'un nouveau client: {}", createClientDTO.getCompanyName()); + + ClientDTO client = clientService.createClient(createClientDTO); + + logger.info("Client créé avec succès avec l'ID: {}", client.getId()); + return Response.status(Response.Status.CREATED).entity(client).build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la création du client: {}", e.getMessage()); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Erreur: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la création du client: {}", e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour mettre à jour un client existant. + * + * @param id l'identifiant du client + * @param updateClientDTO les données de mise à jour + * @return le client mis à jour + */ + @PUT + @Path("/{id}") + @RolesAllowed({"ADMIN", "MANAGER", "CLIENT"}) + @Operation( + summary = "Mettre à jour un client", + description = "Met à jour les informations d'un client existant" + ) + @APIResponses({ + @APIResponse( + responseCode = "200", + description = "Client mis à jour avec succès", + content = @Content(schema = @Schema(implementation = ClientDTO.class)) + ), + @APIResponse(responseCode = "400", description = "Données invalides"), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "404", description = "Client non trouvé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response updateClient( + @Parameter(description = "Identifiant du client", required = true) + @PathParam("id") Long id, + + @Parameter(description = "Données de mise à jour", required = true) + @Valid UpdateClientDTO updateClientDTO + ) { + try { + logger.info("Mise à jour du client avec l'ID: {}", id); + + ClientDTO client = clientService.updateClient(id, updateClientDTO); + + logger.info("Client mis à jour avec succès: {}", client.getCompanyName()); + return Response.ok(client).build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la mise à jour du client {}: {}", id, e.getMessage()); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Erreur: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la mise à jour du client {}: {}", id, e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour supprimer un client (soft delete). + * + * @param id l'identifiant du client + * @return confirmation de suppression + */ + @DELETE + @Path("/{id}") + @RolesAllowed({"ADMIN"}) + @Operation( + summary = "Supprimer un client", + description = "Supprime un client du système (suppression logique)" + ) + @APIResponses({ + @APIResponse(responseCode = "204", description = "Client supprimé avec succès"), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "404", description = "Client non trouvé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response deleteClient( + @Parameter(description = "Identifiant du client", required = true) + @PathParam("id") Long id + ) { + try { + logger.info("Suppression du client avec l'ID: {}", id); + + clientService.deleteClient(id); + + logger.info("Client supprimé avec succès: {}", id); + return Response.noContent().build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la suppression du client {}: {}", id, e.getMessage()); + return Response.status(Response.Status.NOT_FOUND) + .entity("Client non trouvé: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la suppression du client {}: {}", id, e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } +} diff --git a/src/main/java/com/gbcm/server/impl/resource/CoachResource.java b/src/main/java/com/gbcm/server/impl/resource/CoachResource.java new file mode 100644 index 0000000..e65c255 --- /dev/null +++ b/src/main/java/com/gbcm/server/impl/resource/CoachResource.java @@ -0,0 +1,313 @@ +package com.gbcm.server.impl.resource; + +import com.gbcm.server.api.dto.coach.CoachDTO; +import com.gbcm.server.api.dto.coach.CreateCoachDTO; +import com.gbcm.server.api.dto.coach.UpdateCoachDTO; +import com.gbcm.server.api.dto.common.PagedResponseDTO; +import com.gbcm.server.api.enums.ServiceType; +import com.gbcm.server.api.exception.GBCMException; +import com.gbcm.server.api.service.CoachService; +import com.gbcm.server.impl.entity.Coach.CoachStatus; +import jakarta.annotation.security.RolesAllowed; +import jakarta.inject.Inject; +import jakarta.validation.Valid; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.Parameter; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponses; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Contrôleur REST pour la gestion des coaches de la plateforme GBCM. + * Expose tous les endpoints CRUD pour les coaches avec sécurité basée sur les rôles. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@Path("/api/coaches") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Tag(name = "Coaches", description = "Gestion des coaches GBCM") +public class CoachResource { + + private static final Logger logger = LoggerFactory.getLogger(CoachResource.class); + + @Inject + CoachService coachService; + + /** + * Endpoint pour récupérer la liste paginée des coaches. + * + * @param page numéro de page (commence à 0) + * @param size taille de page + * @param sort critères de tri + * @param status filtre par statut + * @param specialization filtre par spécialisation + * @param availableOnly filtre coaches disponibles uniquement + * @param search terme de recherche + * @return liste paginée des coaches + */ + @GET + @RolesAllowed({"ADMIN", "MANAGER", "COACH", "CLIENT"}) + @Operation( + summary = "Liste des coaches", + description = "Récupère la liste paginée des coaches avec filtres optionnels" + ) + @APIResponses({ + @APIResponse( + responseCode = "200", + description = "Liste récupérée avec succès", + content = @Content(schema = @Schema(implementation = PagedResponseDTO.class)) + ), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response getCoaches( + @Parameter(description = "Numéro de page (commence à 0)", example = "0") + @QueryParam("page") @DefaultValue("0") int page, + + @Parameter(description = "Taille de page", example = "20") + @QueryParam("size") @DefaultValue("20") int size, + + @Parameter(description = "Tri (ex: specialization,asc ou averageRating,desc)") + @QueryParam("sort") String sort, + + @Parameter(description = "Filtre par statut") + @QueryParam("status") CoachStatus status, + + @Parameter(description = "Filtre par spécialisation") + @QueryParam("specialization") String specialization, + + @Parameter(description = "Coaches disponibles uniquement") + @QueryParam("availableOnly") @DefaultValue("false") boolean availableOnly, + + @Parameter(description = "Terme de recherche (nom, spécialisation)") + @QueryParam("search") String search + ) { + try { + logger.info("Récupération de la liste des coaches - page: {}, size: {}", page, size); + + PagedResponseDTO coaches = coachService.getCoaches(page, size, sort, status, specialization, availableOnly, search); + + logger.info("Liste des coaches récupérée avec succès - {} éléments", coaches.getTotalElements()); + return Response.ok(coaches).build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la récupération des coaches: {}", e.getMessage()); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Erreur: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la récupération des coaches: {}", e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour récupérer un coach par son ID. + * + * @param id l'identifiant du coach + * @return le coach trouvé + */ + @GET + @Path("/{id}") + @RolesAllowed({"ADMIN", "MANAGER", "COACH", "CLIENT"}) + @Operation( + summary = "Récupérer un coach", + description = "Récupère un coach par son identifiant" + ) + @APIResponses({ + @APIResponse( + responseCode = "200", + description = "Coach trouvé", + content = @Content(schema = @Schema(implementation = CoachDTO.class)) + ), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "404", description = "Coach non trouvé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response getCoachById( + @Parameter(description = "Identifiant du coach", required = true) + @PathParam("id") Long id + ) { + try { + logger.info("Récupération du coach avec l'ID: {}", id); + + CoachDTO coach = coachService.getCoachById(id); + + logger.info("Coach récupéré avec succès: {}", coach.getSpecialization()); + return Response.ok(coach).build(); + + } catch (GBCMException e) { + logger.warn("Coach non trouvé avec l'ID {}: {}", id, e.getMessage()); + return Response.status(Response.Status.NOT_FOUND) + .entity("Coach non trouvé: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la récupération du coach {}: {}", id, e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour créer un nouveau coach. + * + * @param createCoachDTO les données du nouveau coach + * @return le coach créé + */ + @POST + @RolesAllowed({"ADMIN", "MANAGER"}) + @Operation( + summary = "Créer un coach", + description = "Crée un nouveau coach dans le système" + ) + @APIResponses({ + @APIResponse( + responseCode = "201", + description = "Coach créé avec succès", + content = @Content(schema = @Schema(implementation = CoachDTO.class)) + ), + @APIResponse(responseCode = "400", description = "Données invalides"), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "409", description = "Coach déjà existant"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response createCoach( + @Parameter(description = "Données du nouveau coach", required = true) + @Valid CreateCoachDTO createCoachDTO + ) { + try { + logger.info("Création d'un nouveau coach: {}", createCoachDTO.getSpecialization()); + + CoachDTO coach = coachService.createCoach(createCoachDTO); + + logger.info("Coach créé avec succès avec l'ID: {}", coach.getId()); + return Response.status(Response.Status.CREATED).entity(coach).build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la création du coach: {}", e.getMessage()); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Erreur: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la création du coach: {}", e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour mettre à jour un coach existant. + * + * @param id l'identifiant du coach + * @param updateCoachDTO les données de mise à jour + * @return le coach mis à jour + */ + @PUT + @Path("/{id}") + @RolesAllowed({"ADMIN", "MANAGER", "COACH"}) + @Operation( + summary = "Mettre à jour un coach", + description = "Met à jour les informations d'un coach existant" + ) + @APIResponses({ + @APIResponse( + responseCode = "200", + description = "Coach mis à jour avec succès", + content = @Content(schema = @Schema(implementation = CoachDTO.class)) + ), + @APIResponse(responseCode = "400", description = "Données invalides"), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "404", description = "Coach non trouvé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response updateCoach( + @Parameter(description = "Identifiant du coach", required = true) + @PathParam("id") Long id, + + @Parameter(description = "Données de mise à jour", required = true) + @Valid UpdateCoachDTO updateCoachDTO + ) { + try { + logger.info("Mise à jour du coach avec l'ID: {}", id); + + CoachDTO coach = coachService.updateCoach(id, updateCoachDTO); + + logger.info("Coach mis à jour avec succès: {}", coach.getSpecialization()); + return Response.ok(coach).build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la mise à jour du coach {}: {}", id, e.getMessage()); + return Response.status(Response.Status.BAD_REQUEST) + .entity("Erreur: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la mise à jour du coach {}: {}", id, e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } + + /** + * Endpoint pour supprimer un coach (soft delete). + * + * @param id l'identifiant du coach + * @return confirmation de suppression + */ + @DELETE + @Path("/{id}") + @RolesAllowed({"ADMIN"}) + @Operation( + summary = "Supprimer un coach", + description = "Supprime un coach du système (suppression logique)" + ) + @APIResponses({ + @APIResponse(responseCode = "204", description = "Coach supprimé avec succès"), + @APIResponse(responseCode = "401", description = "Non authentifié"), + @APIResponse(responseCode = "403", description = "Accès refusé"), + @APIResponse(responseCode = "404", description = "Coach non trouvé"), + @APIResponse(responseCode = "500", description = "Erreur interne du serveur") + }) + public Response deleteCoach( + @Parameter(description = "Identifiant du coach", required = true) + @PathParam("id") Long id + ) { + try { + logger.info("Suppression du coach avec l'ID: {}", id); + + coachService.deleteCoach(id); + + logger.info("Coach supprimé avec succès: {}", id); + return Response.noContent().build(); + + } catch (GBCMException e) { + logger.error("Erreur GBCM lors de la suppression du coach {}: {}", id, e.getMessage()); + return Response.status(Response.Status.NOT_FOUND) + .entity("Coach non trouvé: " + e.getMessage()) + .build(); + } catch (Exception e) { + logger.error("Erreur inattendue lors de la suppression du coach {}: {}", id, e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Erreur interne du serveur") + .build(); + } + } +} diff --git a/src/main/java/com/gbcm/server/impl/service/ClientServiceImpl.java b/src/main/java/com/gbcm/server/impl/service/ClientServiceImpl.java new file mode 100644 index 0000000..6177163 --- /dev/null +++ b/src/main/java/com/gbcm/server/impl/service/ClientServiceImpl.java @@ -0,0 +1,355 @@ +package com.gbcm.server.impl.service; + +import com.gbcm.server.api.dto.client.ClientDTO; +import com.gbcm.server.api.dto.client.CreateClientDTO; +import com.gbcm.server.api.dto.client.UpdateClientDTO; +import com.gbcm.server.api.dto.common.PagedResponseDTO; +import com.gbcm.server.api.dto.user.UserDTO; +import com.gbcm.server.api.exception.GBCMException; +import com.gbcm.server.api.service.ClientService; +import com.gbcm.server.impl.entity.Client; +import com.gbcm.server.impl.entity.Client.ClientStatus; +import com.gbcm.server.impl.entity.User; +import io.quarkus.hibernate.orm.panache.PanacheQuery; +import io.quarkus.panache.common.Parameters; +import io.quarkus.panache.common.Sort; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.transaction.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Implémentation du service de gestion des clients. + * Fournit toutes les opérations métier liées aux clients en mode simulation. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@ApplicationScoped +public class ClientServiceImpl implements ClientService { + + private static final Logger logger = LoggerFactory.getLogger(ClientServiceImpl.class); + + @Override + public PagedResponseDTO getClients(int page, int size, String sort, Object status, String industry, String search) throws GBCMException { + logger.info("SIMULATION - Récupération des clients - page: {}, size: {}, status: {}, industry: {}, search: {}", + page, size, status, industry, search); + + try { + // En mode simulation, retourner des données fictives + List clients = generateSimulatedClients(page, size, status, industry, search); + long totalElements = calculateTotalElements(status, industry, search); + + logger.info("SIMULATION - {} clients récupérés sur {} total", clients.size(), totalElements); + return new PagedResponseDTO<>(clients, page, size, totalElements, sort); + + } catch (Exception e) { + logger.error("Erreur lors de la récupération des clients: {}", e.getMessage()); + throw new GBCMException("Erreur lors de la récupération des clients", "CLIENT_RETRIEVAL_ERROR"); + } + } + + @Override + public ClientDTO getClientById(Long id) throws GBCMException { + logger.info("SIMULATION - Récupération du client avec l'ID: {}", id); + + if (id == null || id <= 0) { + throw new GBCMException("ID client invalide", "INVALID_CLIENT_ID"); + } + + try { + // En mode simulation, générer un client fictif + ClientDTO client = generateSimulatedClient(id); + + logger.info("SIMULATION - Client récupéré: {}", client.getCompanyName()); + return client; + + } catch (Exception e) { + logger.error("Erreur lors de la récupération du client {}: {}", id, e.getMessage()); + throw new GBCMException("Client non trouvé", "CLIENT_NOT_FOUND"); + } + } + + @Override + public ClientDTO getClientByUserId(Long userId) throws GBCMException { + logger.info("SIMULATION - Récupération du client pour l'utilisateur: {}", userId); + + if (userId == null || userId <= 0) { + throw new GBCMException("ID utilisateur invalide", "INVALID_USER_ID"); + } + + try { + // En mode simulation, générer un client fictif basé sur l'userId + ClientDTO client = generateSimulatedClientByUserId(userId); + + logger.info("SIMULATION - Client récupéré pour l'utilisateur {}: {}", userId, client.getCompanyName()); + return client; + + } catch (Exception e) { + logger.error("Erreur lors de la récupération du client pour l'utilisateur {}: {}", userId, e.getMessage()); + throw new GBCMException("Client non trouvé pour cet utilisateur", "CLIENT_NOT_FOUND_FOR_USER"); + } + } + + @Override + @Transactional + public ClientDTO createClient(CreateClientDTO createClientDTO) throws GBCMException { + logger.info("SIMULATION - Création d'un nouveau client: {}", createClientDTO.getCompanyName()); + + if (createClientDTO == null) { + throw new GBCMException("Données client manquantes", "MISSING_CLIENT_DATA"); + } + + if (createClientDTO.getUserId() == null) { + throw new GBCMException("ID utilisateur obligatoire", "MISSING_USER_ID"); + } + + if (createClientDTO.getCompanyName() == null || createClientDTO.getCompanyName().trim().isEmpty()) { + throw new GBCMException("Nom de l'entreprise obligatoire", "MISSING_COMPANY_NAME"); + } + + try { + // En mode simulation, créer un client fictif + ClientDTO client = new ClientDTO(); + client.setId(System.currentTimeMillis()); // ID simulé + client.setCompanyName(createClientDTO.getCompanyName()); + client.setIndustry(createClientDTO.getIndustry()); + client.setCompanySize(createClientDTO.getCompanySize()); + client.setAnnualRevenue(createClientDTO.getAnnualRevenue()); + client.setAddressLine1(createClientDTO.getAddressLine1()); + client.setAddressLine2(createClientDTO.getAddressLine2()); + client.setCity(createClientDTO.getCity()); + client.setState(createClientDTO.getState()); + client.setPostalCode(createClientDTO.getPostalCode()); + client.setCountry(createClientDTO.getCountry()); + client.setWebsite(createClientDTO.getWebsite()); + client.setStatus(createClientDTO.getStatus()); + client.setPrimaryServiceType(createClientDTO.getPrimaryServiceType()); + client.setServiceStartDate(createClientDTO.getServiceStartDate()); + client.setServiceEndDate(createClientDTO.getServiceEndDate()); + client.setNotes(createClientDTO.getNotes()); + client.setCreatedAt(LocalDateTime.now()); + client.setCreatedBy("system@gbcm.com"); + + // Simuler l'utilisateur associé + UserDTO user = new UserDTO(); + user.setId(createClientDTO.getUserId()); + user.setEmail("client" + createClientDTO.getUserId() + "@gbcm.com"); + user.setFirstName("Client"); + user.setLastName("User " + createClientDTO.getUserId()); + client.setUser(user); + + logger.info("SIMULATION - Client créé avec succès avec l'ID: {}", client.getId()); + return client; + + } catch (Exception e) { + logger.error("Erreur lors de la création du client: {}", e.getMessage()); + throw new GBCMException("Erreur lors de la création du client", "CLIENT_CREATION_ERROR"); + } + } + + @Override + @Transactional + public ClientDTO updateClient(Long id, UpdateClientDTO updateClientDTO) throws GBCMException { + logger.info("SIMULATION - Mise à jour du client avec l'ID: {}", id); + + if (id == null || id <= 0) { + throw new GBCMException("ID client invalide", "INVALID_CLIENT_ID"); + } + + if (updateClientDTO == null) { + throw new GBCMException("Données de mise à jour manquantes", "MISSING_UPDATE_DATA"); + } + + try { + // En mode simulation, récupérer et mettre à jour un client fictif + ClientDTO client = getClientById(id); + + // Appliquer les mises à jour + if (updateClientDTO.getCompanyName() != null) { + client.setCompanyName(updateClientDTO.getCompanyName()); + } + if (updateClientDTO.getIndustry() != null) { + client.setIndustry(updateClientDTO.getIndustry()); + } + if (updateClientDTO.getCompanySize() != null) { + client.setCompanySize(updateClientDTO.getCompanySize()); + } + if (updateClientDTO.getAnnualRevenue() != null) { + client.setAnnualRevenue(updateClientDTO.getAnnualRevenue()); + } + if (updateClientDTO.getAddressLine1() != null) { + client.setAddressLine1(updateClientDTO.getAddressLine1()); + } + if (updateClientDTO.getAddressLine2() != null) { + client.setAddressLine2(updateClientDTO.getAddressLine2()); + } + if (updateClientDTO.getCity() != null) { + client.setCity(updateClientDTO.getCity()); + } + if (updateClientDTO.getState() != null) { + client.setState(updateClientDTO.getState()); + } + if (updateClientDTO.getPostalCode() != null) { + client.setPostalCode(updateClientDTO.getPostalCode()); + } + if (updateClientDTO.getCountry() != null) { + client.setCountry(updateClientDTO.getCountry()); + } + if (updateClientDTO.getWebsite() != null) { + client.setWebsite(updateClientDTO.getWebsite()); + } + if (updateClientDTO.getStatus() != null) { + client.setStatus(updateClientDTO.getStatus()); + } + if (updateClientDTO.getPrimaryServiceType() != null) { + client.setPrimaryServiceType(updateClientDTO.getPrimaryServiceType()); + } + if (updateClientDTO.getServiceStartDate() != null) { + client.setServiceStartDate(updateClientDTO.getServiceStartDate()); + } + if (updateClientDTO.getServiceEndDate() != null) { + client.setServiceEndDate(updateClientDTO.getServiceEndDate()); + } + if (updateClientDTO.getNotes() != null) { + client.setNotes(updateClientDTO.getNotes()); + } + + client.setUpdatedAt(LocalDateTime.now()); + client.setUpdatedBy("system@gbcm.com"); + + logger.info("SIMULATION - Client mis à jour avec succès: {}", client.getCompanyName()); + return client; + + } catch (GBCMException e) { + throw e; + } catch (Exception e) { + logger.error("Erreur lors de la mise à jour du client {}: {}", id, e.getMessage()); + throw new GBCMException("Erreur lors de la mise à jour du client", "CLIENT_UPDATE_ERROR"); + } + } + + @Override + @Transactional + public void deleteClient(Long id) throws GBCMException { + logger.info("SIMULATION - Suppression du client avec l'ID: {}", id); + + if (id == null || id <= 0) { + throw new GBCMException("ID client invalide", "INVALID_CLIENT_ID"); + } + + try { + // En mode simulation, vérifier que le client existe + getClientById(id); + + logger.info("SIMULATION - Client supprimé avec succès: {}", id); + + } catch (GBCMException e) { + throw e; + } catch (Exception e) { + logger.error("Erreur lors de la suppression du client {}: {}", id, e.getMessage()); + throw new GBCMException("Erreur lors de la suppression du client", "CLIENT_DELETION_ERROR"); + } + } + + @Override + @Transactional + public void activateClient(Long id) throws GBCMException { + logger.info("SIMULATION - Activation du client avec l'ID: {}", id); + + UpdateClientDTO updateDTO = new UpdateClientDTO(); + updateDTO.setStatus("ACTIVE"); + updateClient(id, updateDTO); + + logger.info("SIMULATION - Client activé avec succès: {}", id); + } + + @Override + @Transactional + public void deactivateClient(Long id) throws GBCMException { + logger.info("SIMULATION - Désactivation du client avec l'ID: {}", id); + + UpdateClientDTO updateDTO = new UpdateClientDTO(); + updateDTO.setStatus("INACTIVE"); + updateClient(id, updateDTO); + + logger.info("SIMULATION - Client désactivé avec succès: {}", id); + } + + @Override + @Transactional + public void convertProspectToClient(Long id) throws GBCMException { + logger.info("SIMULATION - Conversion du prospect en client avec l'ID: {}", id); + + UpdateClientDTO updateDTO = new UpdateClientDTO(); + updateDTO.setStatus("ACTIVE"); + ClientDTO client = updateClient(id, updateDTO); + client.setConvertedAt(LocalDateTime.now()); + + logger.info("SIMULATION - Prospect converti en client avec succès: {}", id); + } + + @Override + public Object getClientStatistics() throws GBCMException { + logger.info("SIMULATION - Récupération des statistiques clients"); + + // En mode simulation, retourner des statistiques fictives + return "Statistiques clients simulées"; + } + + @Override + public PagedResponseDTO searchClients(Object searchCriteria) throws GBCMException { + logger.info("SIMULATION - Recherche avancée de clients"); + + // En mode simulation, retourner des résultats fictifs + return getClients(0, 10, null, null, null, null); + } + + // Méthodes utilitaires pour la simulation + + private List generateSimulatedClients(int page, int size, Object status, String industry, String search) { + List clients = new ArrayList<>(); + + for (int i = 0; i < size; i++) { + long id = (page * size) + i + 1; + clients.add(generateSimulatedClient(id)); + } + + return clients; + } + + private ClientDTO generateSimulatedClient(Long id) { + ClientDTO client = new ClientDTO(); + client.setId(id); + client.setCompanyName("Entreprise Simulée " + id); + client.setIndustry("Technology"); + client.setCompanySize(50 + (int)(id % 200)); + client.setStatus("ACTIVE"); + client.setCreatedAt(LocalDateTime.now().minusDays(id % 30)); + + // Utilisateur simulé + UserDTO user = new UserDTO(); + user.setId(id); + user.setEmail("client" + id + "@gbcm.com"); + user.setFirstName("Client"); + user.setLastName("User " + id); + client.setUser(user); + + return client; + } + + private ClientDTO generateSimulatedClientByUserId(Long userId) { + return generateSimulatedClient(userId); + } + + private long calculateTotalElements(Object status, String industry, String search) { + // En mode simulation, retourner un nombre fictif + return 150L; + } +} diff --git a/src/main/java/com/gbcm/server/impl/service/CoachServiceImpl.java b/src/main/java/com/gbcm/server/impl/service/CoachServiceImpl.java new file mode 100644 index 0000000..5ac0df0 --- /dev/null +++ b/src/main/java/com/gbcm/server/impl/service/CoachServiceImpl.java @@ -0,0 +1,410 @@ +package com.gbcm.server.impl.service; + +import com.gbcm.server.api.dto.coach.CoachDTO; +import com.gbcm.server.api.dto.coach.CreateCoachDTO; +import com.gbcm.server.api.dto.coach.UpdateCoachDTO; +import com.gbcm.server.api.dto.common.PagedResponseDTO; +import com.gbcm.server.api.dto.user.UserDTO; +import com.gbcm.server.api.enums.ServiceType; +import com.gbcm.server.api.exception.GBCMException; +import com.gbcm.server.api.service.CoachService; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.transaction.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Implémentation du service de gestion des coaches. + * Fournit toutes les opérations métier liées aux coaches en mode simulation. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@ApplicationScoped +public class CoachServiceImpl implements CoachService { + + private static final Logger logger = LoggerFactory.getLogger(CoachServiceImpl.class); + + @Override + public PagedResponseDTO getCoaches(int page, int size, String sort, Object status, String specialization, boolean availableOnly, String search) throws GBCMException { + logger.info("SIMULATION - Récupération des coaches - page: {}, size: {}, status: {}, specialization: {}, availableOnly: {}, search: {}", + page, size, status, specialization, availableOnly, search); + + try { + // En mode simulation, retourner des données fictives + List coaches = generateSimulatedCoaches(page, size, status, specialization, availableOnly, search); + long totalElements = calculateTotalElements(status, specialization, availableOnly, search); + + logger.info("SIMULATION - {} coaches récupérés sur {} total", coaches.size(), totalElements); + return new PagedResponseDTO<>(coaches, page, size, totalElements, sort); + + } catch (Exception e) { + logger.error("Erreur lors de la récupération des coaches: {}", e.getMessage()); + throw new GBCMException("Erreur lors de la récupération des coaches", "COACH_RETRIEVAL_ERROR"); + } + } + + @Override + public CoachDTO getCoachById(Long id) throws GBCMException { + logger.info("SIMULATION - Récupération du coach avec l'ID: {}", id); + + if (id == null || id <= 0) { + throw new GBCMException("ID coach invalide", "INVALID_COACH_ID"); + } + + try { + // En mode simulation, générer un coach fictif + CoachDTO coach = generateSimulatedCoach(id); + + logger.info("SIMULATION - Coach récupéré: {}", coach.getSpecialization()); + return coach; + + } catch (Exception e) { + logger.error("Erreur lors de la récupération du coach {}: {}", id, e.getMessage()); + throw new GBCMException("Coach non trouvé", "COACH_NOT_FOUND"); + } + } + + @Override + public CoachDTO getCoachByUserId(Long userId) throws GBCMException { + logger.info("SIMULATION - Récupération du coach pour l'utilisateur: {}", userId); + + if (userId == null || userId <= 0) { + throw new GBCMException("ID utilisateur invalide", "INVALID_USER_ID"); + } + + try { + // En mode simulation, générer un coach fictif basé sur l'userId + CoachDTO coach = generateSimulatedCoachByUserId(userId); + + logger.info("SIMULATION - Coach récupéré pour l'utilisateur {}: {}", userId, coach.getSpecialization()); + return coach; + + } catch (Exception e) { + logger.error("Erreur lors de la récupération du coach pour l'utilisateur {}: {}", userId, e.getMessage()); + throw new GBCMException("Coach non trouvé pour cet utilisateur", "COACH_NOT_FOUND_FOR_USER"); + } + } + + @Override + @Transactional + public CoachDTO createCoach(CreateCoachDTO createCoachDTO) throws GBCMException { + logger.info("SIMULATION - Création d'un nouveau coach: {}", createCoachDTO.getSpecialization()); + + if (createCoachDTO == null) { + throw new GBCMException("Données coach manquantes", "MISSING_COACH_DATA"); + } + + if (createCoachDTO.getUserId() == null) { + throw new GBCMException("ID utilisateur obligatoire", "MISSING_USER_ID"); + } + + if (createCoachDTO.getSpecialization() == null || createCoachDTO.getSpecialization().trim().isEmpty()) { + throw new GBCMException("Spécialisation obligatoire", "MISSING_SPECIALIZATION"); + } + + try { + // En mode simulation, créer un coach fictif + CoachDTO coach = new CoachDTO(); + coach.setId(System.currentTimeMillis()); // ID simulé + coach.setSpecialization(createCoachDTO.getSpecialization()); + coach.setYearsOfExperience(createCoachDTO.getYearsOfExperience()); + coach.setCertifications(createCoachDTO.getCertifications()); + coach.setBio(createCoachDTO.getBio()); + coach.setHourlyRate(createCoachDTO.getHourlyRate()); + coach.setStatus(createCoachDTO.getStatus()); + coach.setAvailableForBooking(createCoachDTO.getAvailableForBooking()); + coach.setServiceTypes(createCoachDTO.getServiceTypes()); + coach.setWorkingHoursStart(createCoachDTO.getWorkingHoursStart()); + coach.setWorkingHoursEnd(createCoachDTO.getWorkingHoursEnd()); + coach.setTimeZone(createCoachDTO.getTimeZone()); + coach.setLanguagesSpoken(createCoachDTO.getLanguagesSpoken()); + coach.setStartDate(createCoachDTO.getStartDate()); + coach.setEndDate(createCoachDTO.getEndDate()); + coach.setNotes(createCoachDTO.getNotes()); + coach.setCreatedAt(LocalDateTime.now()); + coach.setCreatedBy("system@gbcm.com"); + + // Simuler l'utilisateur associé + UserDTO user = new UserDTO(); + user.setId(createCoachDTO.getUserId()); + user.setEmail("coach" + createCoachDTO.getUserId() + "@gbcm.com"); + user.setFirstName("Coach"); + user.setLastName("User " + createCoachDTO.getUserId()); + coach.setUser(user); + + // Initialiser les statistiques + coach.setAverageRating(BigDecimal.ZERO); + coach.setTotalRatings(0); + coach.setTotalSessions(0); + coach.setTotalRevenue(BigDecimal.ZERO); + + logger.info("SIMULATION - Coach créé avec succès avec l'ID: {}", coach.getId()); + return coach; + + } catch (Exception e) { + logger.error("Erreur lors de la création du coach: {}", e.getMessage()); + throw new GBCMException("Erreur lors de la création du coach", "COACH_CREATION_ERROR"); + } + } + + @Override + @Transactional + public CoachDTO updateCoach(Long id, UpdateCoachDTO updateCoachDTO) throws GBCMException { + logger.info("SIMULATION - Mise à jour du coach avec l'ID: {}", id); + + if (id == null || id <= 0) { + throw new GBCMException("ID coach invalide", "INVALID_COACH_ID"); + } + + if (updateCoachDTO == null) { + throw new GBCMException("Données de mise à jour manquantes", "MISSING_UPDATE_DATA"); + } + + try { + // En mode simulation, récupérer et mettre à jour un coach fictif + CoachDTO coach = getCoachById(id); + + // Appliquer les mises à jour + if (updateCoachDTO.getSpecialization() != null) { + coach.setSpecialization(updateCoachDTO.getSpecialization()); + } + if (updateCoachDTO.getYearsOfExperience() != null) { + coach.setYearsOfExperience(updateCoachDTO.getYearsOfExperience()); + } + if (updateCoachDTO.getCertifications() != null) { + coach.setCertifications(updateCoachDTO.getCertifications()); + } + if (updateCoachDTO.getBio() != null) { + coach.setBio(updateCoachDTO.getBio()); + } + if (updateCoachDTO.getHourlyRate() != null) { + coach.setHourlyRate(updateCoachDTO.getHourlyRate()); + } + if (updateCoachDTO.getStatus() != null) { + coach.setStatus(updateCoachDTO.getStatus()); + } + if (updateCoachDTO.getAvailableForBooking() != null) { + coach.setAvailableForBooking(updateCoachDTO.getAvailableForBooking()); + } + if (updateCoachDTO.getServiceTypes() != null) { + coach.setServiceTypes(updateCoachDTO.getServiceTypes()); + } + if (updateCoachDTO.getWorkingHoursStart() != null) { + coach.setWorkingHoursStart(updateCoachDTO.getWorkingHoursStart()); + } + if (updateCoachDTO.getWorkingHoursEnd() != null) { + coach.setWorkingHoursEnd(updateCoachDTO.getWorkingHoursEnd()); + } + if (updateCoachDTO.getTimeZone() != null) { + coach.setTimeZone(updateCoachDTO.getTimeZone()); + } + if (updateCoachDTO.getLanguagesSpoken() != null) { + coach.setLanguagesSpoken(updateCoachDTO.getLanguagesSpoken()); + } + if (updateCoachDTO.getStartDate() != null) { + coach.setStartDate(updateCoachDTO.getStartDate()); + } + if (updateCoachDTO.getEndDate() != null) { + coach.setEndDate(updateCoachDTO.getEndDate()); + } + if (updateCoachDTO.getNotes() != null) { + coach.setNotes(updateCoachDTO.getNotes()); + } + + coach.setUpdatedAt(LocalDateTime.now()); + coach.setUpdatedBy("system@gbcm.com"); + + logger.info("SIMULATION - Coach mis à jour avec succès: {}", coach.getSpecialization()); + return coach; + + } catch (GBCMException e) { + throw e; + } catch (Exception e) { + logger.error("Erreur lors de la mise à jour du coach {}: {}", id, e.getMessage()); + throw new GBCMException("Erreur lors de la mise à jour du coach", "COACH_UPDATE_ERROR"); + } + } + + @Override + @Transactional + public void deleteCoach(Long id) throws GBCMException { + logger.info("SIMULATION - Suppression du coach avec l'ID: {}", id); + + if (id == null || id <= 0) { + throw new GBCMException("ID coach invalide", "INVALID_COACH_ID"); + } + + try { + // En mode simulation, vérifier que le coach existe + getCoachById(id); + + logger.info("SIMULATION - Coach supprimé avec succès: {}", id); + + } catch (GBCMException e) { + throw e; + } catch (Exception e) { + logger.error("Erreur lors de la suppression du coach {}: {}", id, e.getMessage()); + throw new GBCMException("Erreur lors de la suppression du coach", "COACH_DELETION_ERROR"); + } + } + + @Override + @Transactional + public void activateCoach(Long id) throws GBCMException { + logger.info("SIMULATION - Activation du coach avec l'ID: {}", id); + + UpdateCoachDTO updateDTO = new UpdateCoachDTO(); + updateDTO.setStatus("ACTIVE"); + updateDTO.setAvailableForBooking(true); + updateCoach(id, updateDTO); + + logger.info("SIMULATION - Coach activé avec succès: {}", id); + } + + @Override + @Transactional + public void deactivateCoach(Long id) throws GBCMException { + logger.info("SIMULATION - Désactivation du coach avec l'ID: {}", id); + + UpdateCoachDTO updateDTO = new UpdateCoachDTO(); + updateDTO.setStatus("INACTIVE"); + updateDTO.setAvailableForBooking(false); + updateCoach(id, updateDTO); + + logger.info("SIMULATION - Coach désactivé avec succès: {}", id); + } + + @Override + @Transactional + public void setCoachOnLeave(Long id) throws GBCMException { + logger.info("SIMULATION - Mise en congé du coach avec l'ID: {}", id); + + UpdateCoachDTO updateDTO = new UpdateCoachDTO(); + updateDTO.setStatus("ON_LEAVE"); + updateDTO.setAvailableForBooking(false); + updateCoach(id, updateDTO); + + logger.info("SIMULATION - Coach mis en congé avec succès: {}", id); + } + + @Override + public PagedResponseDTO getAvailableCoachesForService(Object serviceType) throws GBCMException { + logger.info("SIMULATION - Récupération des coaches disponibles pour le service: {}", serviceType); + + // En mode simulation, retourner des coaches disponibles fictifs + return getCoaches(0, 10, null, "ACTIVE", null, true, null); + } + + @Override + public Object getCoachStatistics() throws GBCMException { + logger.info("SIMULATION - Récupération des statistiques coaches"); + + // En mode simulation, retourner des statistiques fictives + return "Statistiques coaches simulées"; + } + + @Override + @Transactional + public void updateCoachRating(Long coachId, Double rating) throws GBCMException { + logger.info("SIMULATION - Mise à jour de la note du coach {} avec la note: {}", coachId, rating); + + if (rating == null || rating < 0 || rating > 5) { + throw new GBCMException("Note invalide (doit être entre 0 et 5)", "INVALID_RATING"); + } + + CoachDTO coach = getCoachById(coachId); + + // Simuler la mise à jour de la note moyenne + BigDecimal newRating = BigDecimal.valueOf(rating); + if (coach.getAverageRating() == null || coach.getAverageRating().equals(BigDecimal.ZERO)) { + coach.setAverageRating(newRating); + coach.setTotalRatings(1); + } else { + BigDecimal totalScore = coach.getAverageRating().multiply(new BigDecimal(coach.getTotalRatings())); + totalScore = totalScore.add(newRating); + coach.setTotalRatings(coach.getTotalRatings() + 1); + coach.setAverageRating(totalScore.divide(new BigDecimal(coach.getTotalRatings()), 2, java.math.RoundingMode.HALF_UP)); + } + + logger.info("SIMULATION - Note du coach mise à jour: nouvelle moyenne = {}", coach.getAverageRating()); + } + + @Override + public PagedResponseDTO searchCoaches(Object searchCriteria) throws GBCMException { + logger.info("SIMULATION - Recherche avancée de coaches"); + + // En mode simulation, retourner des résultats fictifs + return getCoaches(0, 10, null, null, null, false, null); + } + + // Méthodes utilitaires pour la simulation + + private List generateSimulatedCoaches(int page, int size, Object status, String specialization, boolean availableOnly, String search) { + List coaches = new ArrayList<>(); + + for (int i = 0; i < size; i++) { + long id = (page * size) + i + 1; + coaches.add(generateSimulatedCoach(id)); + } + + return coaches; + } + + private CoachDTO generateSimulatedCoach(Long id) { + CoachDTO coach = new CoachDTO(); + coach.setId(id); + coach.setSpecialization("Business Strategy " + id); + coach.setYearsOfExperience(5 + (int)(id % 15)); + coach.setCertifications("MBA, PMP"); + coach.setBio("Coach expérimenté spécialisé en stratégie d'entreprise."); + coach.setHourlyRate(new BigDecimal("150.00").add(new BigDecimal(id % 100))); + coach.setStatus("ACTIVE"); + coach.setAvailableForBooking(true); + coach.setWorkingHoursStart(LocalTime.of(9, 0)); + coach.setWorkingHoursEnd(LocalTime.of(17, 0)); + coach.setTimeZone("Europe/Paris"); + coach.setLanguagesSpoken("Français, Anglais"); + coach.setAverageRating(new BigDecimal("4.5")); + coach.setTotalRatings(20 + (int)(id % 50)); + coach.setTotalSessions(100 + (int)(id % 200)); + coach.setTotalRevenue(new BigDecimal("15000.00").add(new BigDecimal(id * 100))); + coach.setStartDate(LocalDate.now().minusYears(id % 5)); + coach.setCreatedAt(LocalDateTime.now().minusDays(id % 30)); + + // Services types simulés + Set serviceTypes = new HashSet<>(); + serviceTypes.add(ServiceType.ONE_ON_ONE_COACHING); + serviceTypes.add(ServiceType.STRATEGIC_WORKSHOP); + coach.setServiceTypes(serviceTypes); + + // Utilisateur simulé + UserDTO user = new UserDTO(); + user.setId(id); + user.setEmail("coach" + id + "@gbcm.com"); + user.setFirstName("Coach"); + user.setLastName("User " + id); + coach.setUser(user); + + return coach; + } + + private CoachDTO generateSimulatedCoachByUserId(Long userId) { + return generateSimulatedCoach(userId); + } + + private long calculateTotalElements(Object status, String specialization, boolean availableOnly, String search) { + // En mode simulation, retourner un nombre fictif + return 75L; + } +} diff --git a/src/test/java/com/gbcm/server/impl/entity/BaseEntityTest.java b/src/test/java/com/gbcm/server/impl/entity/BaseEntityTest.java new file mode 100644 index 0000000..9eced53 --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/entity/BaseEntityTest.java @@ -0,0 +1,417 @@ +package com.gbcm.server.impl.entity; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.BeforeEach; +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 BaseEntity. + * Teste toutes les fonctionnalités d'audit et de soft delete. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@QuarkusTest +@DisplayName("Tests BaseEntity") +class BaseEntityTest { + + /** + * Implémentation concrète de BaseEntity pour les tests. + */ + private static class TestEntity extends BaseEntity { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + private TestEntity testEntity; + + @BeforeEach + void setUp() { + testEntity = new TestEntity(); + testEntity.setName("Test Entity"); + } + + /** + * Test de création d'une entité. + */ + @Test + @DisplayName("Création d'une entité") + void testEntityCreation() { + assertThat(testEntity).isNotNull(); + assertThat(testEntity.getName()).isEqualTo("Test Entity"); + assertThat(testEntity.getCreatedAt()).isNull(); // Pas encore persistée + assertThat(testEntity.getUpdatedAt()).isNull(); // Pas encore persistée + assertThat(testEntity.isDeleted()).isFalse(); + } + + /** + * Test des getters et setters pour createdAt. + */ + @Test + @DisplayName("Getters et setters pour createdAt") + void testCreatedAtGetterSetter() { + // Given + LocalDateTime now = LocalDateTime.now(); + + // When + testEntity.setCreatedAt(now); + + // Then + assertThat(testEntity.getCreatedAt()).isEqualTo(now); + } + + /** + * Test des getters et setters pour updatedAt. + */ + @Test + @DisplayName("Getters et setters pour updatedAt") + void testUpdatedAtGetterSetter() { + // Given + LocalDateTime now = LocalDateTime.now(); + + // When + testEntity.setUpdatedAt(now); + + // Then + assertThat(testEntity.getUpdatedAt()).isEqualTo(now); + } + + /** + * Test des getters et setters pour createdBy. + */ + @Test + @DisplayName("Getters et setters pour createdBy") + void testCreatedByGetterSetter() { + // Given + String createdBy = "admin@gbcm.com"; + + // When + testEntity.setCreatedBy(createdBy); + + // Then + assertThat(testEntity.getCreatedBy()).isEqualTo(createdBy); + } + + /** + * Test des getters et setters pour updatedBy. + */ + @Test + @DisplayName("Getters et setters pour updatedBy") + void testUpdatedByGetterSetter() { + // Given + String updatedBy = "manager@gbcm.com"; + + // When + testEntity.setUpdatedBy(updatedBy); + + // Then + assertThat(testEntity.getUpdatedBy()).isEqualTo(updatedBy); + } + + /** + * Test des getters et setters pour deleted. + */ + @Test + @DisplayName("Getters et setters pour deleted") + void testDeletedGetterSetter() { + // Initially false + assertThat(testEntity.isDeleted()).isFalse(); + + // When + testEntity.setDeleted(true); + + // Then + assertThat(testEntity.isDeleted()).isTrue(); + } + + /** + * Test des getters et setters pour deletedAt. + */ + @Test + @DisplayName("Getters et setters pour deletedAt") + void testDeletedAtGetterSetter() { + // Given + LocalDateTime now = LocalDateTime.now(); + + // When + testEntity.setDeletedAt(now); + + // Then + assertThat(testEntity.getDeletedAt()).isEqualTo(now); + } + + /** + * Test des getters et setters pour deletedBy. + */ + @Test + @DisplayName("Getters et setters pour deletedBy") + void testDeletedByGetterSetter() { + // Given + String deletedBy = "admin@gbcm.com"; + + // When + testEntity.setDeletedBy(deletedBy); + + // Then + assertThat(testEntity.getDeletedBy()).isEqualTo(deletedBy); + } + + /** + * Test de la méthode softDelete. + */ + @Test + @DisplayName("Méthode softDelete") + void testSoftDelete() { + // Given + String deletedBy = "admin@gbcm.com"; + LocalDateTime beforeDelete = LocalDateTime.now(); + + // When + testEntity.softDelete(deletedBy); + + // Then + assertThat(testEntity.isDeleted()).isTrue(); + assertThat(testEntity.getDeletedBy()).isEqualTo(deletedBy); + assertThat(testEntity.getDeletedAt()).isNotNull(); + assertThat(testEntity.getDeletedAt()).isAfterOrEqualTo(beforeDelete); + assertThat(testEntity.getDeletedAt()).isBeforeOrEqualTo(LocalDateTime.now()); + } + + /** + * Test de la méthode softDelete avec utilisateur null. + */ + @Test + @DisplayName("Méthode softDelete avec utilisateur null") + void testSoftDelete_NullUser() { + // When + testEntity.softDelete(null); + + // Then + assertThat(testEntity.isDeleted()).isTrue(); + assertThat(testEntity.getDeletedBy()).isNull(); + assertThat(testEntity.getDeletedAt()).isNotNull(); + } + + /** + * Test de la méthode restore. + */ + @Test + @DisplayName("Méthode restore") + void testRestore() { + // Given - Entité supprimée + testEntity.softDelete("admin@gbcm.com"); + assertThat(testEntity.isDeleted()).isTrue(); + + // When + testEntity.restore(); + + // Then + assertThat(testEntity.isDeleted()).isFalse(); + assertThat(testEntity.getDeletedAt()).isNull(); + assertThat(testEntity.getDeletedBy()).isNull(); + } + + /** + * Test de la méthode restore sur une entité non supprimée. + */ + @Test + @DisplayName("Méthode restore sur une entité non supprimée") + void testRestore_NotDeleted() { + // Given - Entité non supprimée + assertThat(testEntity.isDeleted()).isFalse(); + + // When + testEntity.restore(); + + // Then + assertThat(testEntity.isDeleted()).isFalse(); + assertThat(testEntity.getDeletedAt()).isNull(); + assertThat(testEntity.getDeletedBy()).isNull(); + } + + /** + * Test de la méthode isDeleted. + */ + @Test + @DisplayName("Méthode isDeleted") + void testIsDeleted() { + // Initially false + assertThat(testEntity.isDeleted()).isFalse(); + + // After soft delete + testEntity.softDelete("admin@gbcm.com"); + assertThat(testEntity.isDeleted()).isTrue(); + + // After restore + testEntity.restore(); + assertThat(testEntity.isDeleted()).isFalse(); + } + + /** + * Test de la méthode isActive. + */ + @Test + @DisplayName("Méthode isActive") + void testIsActive() { + // Initially active (not deleted) + assertThat(testEntity.isActive()).isTrue(); + + // After soft delete + testEntity.softDelete("admin@gbcm.com"); + assertThat(testEntity.isActive()).isFalse(); + + // After restore + testEntity.restore(); + assertThat(testEntity.isActive()).isTrue(); + } + + /** + * Test de cycle complet de soft delete et restore. + */ + @Test + @DisplayName("Cycle complet de soft delete et restore") + void testSoftDeleteRestoreCycle() { + // Given + String deletedBy = "admin@gbcm.com"; + + // Initially active + assertThat(testEntity.isActive()).isTrue(); + assertThat(testEntity.isDeleted()).isFalse(); + + // Soft delete + testEntity.softDelete(deletedBy); + assertThat(testEntity.isActive()).isFalse(); + assertThat(testEntity.isDeleted()).isTrue(); + assertThat(testEntity.getDeletedBy()).isEqualTo(deletedBy); + assertThat(testEntity.getDeletedAt()).isNotNull(); + + // Restore + testEntity.restore(); + assertThat(testEntity.isActive()).isTrue(); + assertThat(testEntity.isDeleted()).isFalse(); + assertThat(testEntity.getDeletedBy()).isNull(); + assertThat(testEntity.getDeletedAt()).isNull(); + } + + /** + * Test de multiples soft delete. + */ + @Test + @DisplayName("Multiples soft delete") + void testMultipleSoftDelete() { + // Given + String firstDeletedBy = "admin@gbcm.com"; + String secondDeletedBy = "manager@gbcm.com"; + + // First soft delete + testEntity.softDelete(firstDeletedBy); + LocalDateTime firstDeletedAt = testEntity.getDeletedAt(); + + // Wait a bit to ensure different timestamps + try { + Thread.sleep(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + // Second soft delete (should update the fields) + testEntity.softDelete(secondDeletedBy); + + // Then + assertThat(testEntity.isDeleted()).isTrue(); + assertThat(testEntity.getDeletedBy()).isEqualTo(secondDeletedBy); + assertThat(testEntity.getDeletedAt()).isAfterOrEqualTo(firstDeletedAt); + } + + /** + * Test de l'état initial d'une nouvelle entité. + */ + @Test + @DisplayName("État initial d'une nouvelle entité") + void testInitialState() { + // Given + TestEntity newEntity = new TestEntity(); + + // Then + assertThat(newEntity.getCreatedAt()).isNull(); + assertThat(newEntity.getUpdatedAt()).isNull(); + assertThat(newEntity.getCreatedBy()).isNull(); + assertThat(newEntity.getUpdatedBy()).isNull(); + assertThat(newEntity.isDeleted()).isFalse(); + assertThat(newEntity.getDeletedAt()).isNull(); + assertThat(newEntity.getDeletedBy()).isNull(); + assertThat(newEntity.isActive()).isTrue(); + } + + /** + * Test de modification des champs d'audit. + */ + @Test + @DisplayName("Modification des champs d'audit") + void testAuditFieldsModification() { + // Given + LocalDateTime createdAt = LocalDateTime.now().minusDays(1); + LocalDateTime updatedAt = LocalDateTime.now(); + String createdBy = "system@gbcm.com"; + String updatedBy = "admin@gbcm.com"; + + // When + testEntity.setCreatedAt(createdAt); + testEntity.setUpdatedAt(updatedAt); + testEntity.setCreatedBy(createdBy); + testEntity.setUpdatedBy(updatedBy); + + // Then + assertThat(testEntity.getCreatedAt()).isEqualTo(createdAt); + assertThat(testEntity.getUpdatedAt()).isEqualTo(updatedAt); + assertThat(testEntity.getCreatedBy()).isEqualTo(createdBy); + assertThat(testEntity.getUpdatedBy()).isEqualTo(updatedBy); + } + + /** + * Test de cohérence des timestamps. + */ + @Test + @DisplayName("Cohérence des timestamps") + void testTimestampConsistency() { + // Given + LocalDateTime baseTime = LocalDateTime.now(); + LocalDateTime createdAt = baseTime.minusHours(1); + LocalDateTime updatedAt = baseTime; + + // When + testEntity.setCreatedAt(createdAt); + testEntity.setUpdatedAt(updatedAt); + + // Then + assertThat(testEntity.getUpdatedAt()).isAfterOrEqualTo(testEntity.getCreatedAt()); + } + + /** + * Test de soft delete avec chaîne vide. + */ + @Test + @DisplayName("Soft delete avec chaîne vide") + void testSoftDelete_EmptyString() { + // When + testEntity.softDelete(""); + + // Then + assertThat(testEntity.isDeleted()).isTrue(); + assertThat(testEntity.getDeletedBy()).isEqualTo(""); + assertThat(testEntity.getDeletedAt()).isNotNull(); + } +} diff --git a/src/test/java/com/gbcm/server/impl/entity/ClientEntityTest.java b/src/test/java/com/gbcm/server/impl/entity/ClientEntityTest.java new file mode 100644 index 0000000..ab635fd --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/entity/ClientEntityTest.java @@ -0,0 +1,519 @@ +package com.gbcm.server.impl.entity; + +import com.gbcm.server.api.enums.ServiceType; +import com.gbcm.server.api.enums.UserRole; +import com.gbcm.server.api.enums.UserStatus; +import com.gbcm.server.impl.entity.Client.ClientStatus; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.*; + +/** + * Tests unitaires pour l'entité Client. + * Teste toutes les fonctionnalités de l'entité Client. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@QuarkusTest +@DisplayName("Tests Client Entity") +class ClientEntityTest { + + private Client client; + private User user; + + @BeforeEach + void setUp() { + // Création d'un utilisateur de test + user = new User(); + user.setId(1L); + user.setFirstName("John"); + user.setLastName("Doe"); + user.setEmail("john.doe@gbcm.com"); + user.setRole(UserRole.CLIENT); + user.setStatus(UserStatus.ACTIVE); + user.setActive(true); + + // Création d'un client de test + client = new Client(); + client.setUser(user); + client.setCompanyName("GBCM Test Company"); + client.setIndustry("Technology"); + client.setCompanySize(50); + client.setAnnualRevenue(new BigDecimal("1000000.00")); + client.setAddressLine1("123 Main Street"); + client.setCity("Paris"); + client.setState("Île-de-France"); + client.setPostalCode("75001"); + client.setCountry("France"); + client.setWebsite("https://www.gbcm-test.com"); + client.setStatus(ClientStatus.ACTIVE); + } + + /** + * Test de création d'un client. + */ + @Test + @DisplayName("Création d'un client") + void testClientCreation() { + assertThat(client).isNotNull(); + assertThat(client.getUser()).isEqualTo(user); + assertThat(client.getCompanyName()).isEqualTo("GBCM Test Company"); + assertThat(client.getStatus()).isEqualTo(ClientStatus.ACTIVE); + } + + /** + * Test des getters et setters pour l'ID. + */ + @Test + @DisplayName("Getters et setters pour l'ID") + void testIdGetterSetter() { + // Given + Long id = 123L; + + // When + client.setId(id); + + // Then + assertThat(client.getId()).isEqualTo(id); + } + + /** + * Test des getters et setters pour l'utilisateur. + */ + @Test + @DisplayName("Getters et setters pour l'utilisateur") + void testUserGetterSetter() { + // Given + User newUser = new User(); + newUser.setId(2L); + newUser.setEmail("jane.doe@gbcm.com"); + + // When + client.setUser(newUser); + + // Then + assertThat(client.getUser()).isEqualTo(newUser); + } + + /** + * Test des getters et setters pour le nom de l'entreprise. + */ + @Test + @DisplayName("Getters et setters pour le nom de l'entreprise") + void testCompanyNameGetterSetter() { + // Given + String companyName = "New Company Name"; + + // When + client.setCompanyName(companyName); + + // Then + assertThat(client.getCompanyName()).isEqualTo(companyName); + } + + /** + * Test des getters et setters pour le secteur d'activité. + */ + @Test + @DisplayName("Getters et setters pour le secteur d'activité") + void testIndustryGetterSetter() { + // Given + String industry = "Healthcare"; + + // When + client.setIndustry(industry); + + // Then + assertThat(client.getIndustry()).isEqualTo(industry); + } + + /** + * Test des getters et setters pour la taille de l'entreprise. + */ + @Test + @DisplayName("Getters et setters pour la taille de l'entreprise") + void testCompanySizeGetterSetter() { + // Given + Integer companySize = 100; + + // When + client.setCompanySize(companySize); + + // Then + assertThat(client.getCompanySize()).isEqualTo(companySize); + } + + /** + * Test des getters et setters pour le chiffre d'affaires. + */ + @Test + @DisplayName("Getters et setters pour le chiffre d'affaires") + void testAnnualRevenueGetterSetter() { + // Given + BigDecimal revenue = new BigDecimal("2500000.50"); + + // When + client.setAnnualRevenue(revenue); + + // Then + assertThat(client.getAnnualRevenue()).isEqualTo(revenue); + } + + /** + * Test des getters et setters pour l'adresse ligne 1. + */ + @Test + @DisplayName("Getters et setters pour l'adresse ligne 1") + void testAddressLine1GetterSetter() { + // Given + String address = "456 Business Avenue"; + + // When + client.setAddressLine1(address); + + // Then + assertThat(client.getAddressLine1()).isEqualTo(address); + } + + /** + * Test des getters et setters pour l'adresse ligne 2. + */ + @Test + @DisplayName("Getters et setters pour l'adresse ligne 2") + void testAddressLine2GetterSetter() { + // Given + String address = "Suite 200"; + + // When + client.setAddressLine2(address); + + // Then + assertThat(client.getAddressLine2()).isEqualTo(address); + } + + /** + * Test des getters et setters pour la ville. + */ + @Test + @DisplayName("Getters et setters pour la ville") + void testCityGetterSetter() { + // Given + String city = "Lyon"; + + // When + client.setCity(city); + + // Then + assertThat(client.getCity()).isEqualTo(city); + } + + /** + * Test des getters et setters pour l'état. + */ + @Test + @DisplayName("Getters et setters pour l'état") + void testStateGetterSetter() { + // Given + String state = "Rhône-Alpes"; + + // When + client.setState(state); + + // Then + assertThat(client.getState()).isEqualTo(state); + } + + /** + * Test des getters et setters pour le code postal. + */ + @Test + @DisplayName("Getters et setters pour le code postal") + void testPostalCodeGetterSetter() { + // Given + String postalCode = "69000"; + + // When + client.setPostalCode(postalCode); + + // Then + assertThat(client.getPostalCode()).isEqualTo(postalCode); + } + + /** + * Test des getters et setters pour le pays. + */ + @Test + @DisplayName("Getters et setters pour le pays") + void testCountryGetterSetter() { + // Given + String country = "Canada"; + + // When + client.setCountry(country); + + // Then + assertThat(client.getCountry()).isEqualTo(country); + } + + /** + * Test des getters et setters pour le site web. + */ + @Test + @DisplayName("Getters et setters pour le site web") + void testWebsiteGetterSetter() { + // Given + String website = "https://www.newcompany.com"; + + // When + client.setWebsite(website); + + // Then + assertThat(client.getWebsite()).isEqualTo(website); + } + + /** + * Test des getters et setters pour le statut. + */ + @Test + @DisplayName("Getters et setters pour le statut") + void testStatusGetterSetter() { + // Given + ClientStatus status = ClientStatus.INACTIVE; + + // When + client.setStatus(status); + + // Then + assertThat(client.getStatus()).isEqualTo(status); + } + + /** + * Test des getters et setters pour la date de conversion. + */ + @Test + @DisplayName("Getters et setters pour la date de conversion") + void testConvertedAtGetterSetter() { + // Given + LocalDateTime convertedAt = LocalDateTime.now(); + + // When + client.setConvertedAt(convertedAt); + + // Then + assertThat(client.getConvertedAt()).isEqualTo(convertedAt); + } + + /** + * Test des getters et setters pour le type de service principal. + */ + @Test + @DisplayName("Getters et setters pour le type de service principal") + void testPrimaryServiceTypeGetterSetter() { + // Given + ServiceType serviceType = ServiceType.STRATEGIC_WORKSHOP; + + // When + client.setPrimaryServiceType(serviceType); + + // Then + assertThat(client.getPrimaryServiceType()).isEqualTo(serviceType); + } + + /** + * Test des getters et setters pour la date de début de service. + */ + @Test + @DisplayName("Getters et setters pour la date de début de service") + void testServiceStartDateGetterSetter() { + // Given + LocalDate startDate = LocalDate.now(); + + // When + client.setServiceStartDate(startDate); + + // Then + assertThat(client.getServiceStartDate()).isEqualTo(startDate); + } + + /** + * Test des getters et setters pour la date de fin de service. + */ + @Test + @DisplayName("Getters et setters pour la date de fin de service") + void testServiceEndDateGetterSetter() { + // Given + LocalDate endDate = LocalDate.now().plusMonths(6); + + // When + client.setServiceEndDate(endDate); + + // Then + assertThat(client.getServiceEndDate()).isEqualTo(endDate); + } + + /** + * Test des getters et setters pour les notes. + */ + @Test + @DisplayName("Getters et setters pour les notes") + void testNotesGetterSetter() { + // Given + String notes = "Client très satisfait des services fournis."; + + // When + client.setNotes(notes); + + // Then + assertThat(client.getNotes()).isEqualTo(notes); + } + + /** + * Test du statut par défaut. + */ + @Test + @DisplayName("Statut par défaut") + void testDefaultStatus() { + // Given + Client newClient = new Client(); + + // Then + assertThat(newClient.getStatus()).isEqualTo(ClientStatus.PROSPECT); + } + + /** + * Test de tous les statuts ClientStatus. + */ + @Test + @DisplayName("Tous les statuts ClientStatus") + void testAllClientStatuses() { + // Test PROSPECT + client.setStatus(ClientStatus.PROSPECT); + assertThat(client.getStatus()).isEqualTo(ClientStatus.PROSPECT); + + // Test ACTIVE + client.setStatus(ClientStatus.ACTIVE); + assertThat(client.getStatus()).isEqualTo(ClientStatus.ACTIVE); + + // Test INACTIVE + client.setStatus(ClientStatus.INACTIVE); + assertThat(client.getStatus()).isEqualTo(ClientStatus.INACTIVE); + + // Test SUSPENDED + client.setStatus(ClientStatus.SUSPENDED); + assertThat(client.getStatus()).isEqualTo(ClientStatus.SUSPENDED); + } + + /** + * Test de conversion de prospect à client. + */ + @Test + @DisplayName("Conversion de prospect à client") + void testProspectToClientConversion() { + // Given + client.setStatus(ClientStatus.PROSPECT); + assertThat(client.getConvertedAt()).isNull(); + + // When + LocalDateTime conversionTime = LocalDateTime.now(); + client.setStatus(ClientStatus.ACTIVE); + client.setConvertedAt(conversionTime); + + // Then + assertThat(client.getStatus()).isEqualTo(ClientStatus.ACTIVE); + assertThat(client.getConvertedAt()).isEqualTo(conversionTime); + } + + /** + * Test de l'adresse complète. + */ + @Test + @DisplayName("Adresse complète") + void testFullAddress() { + // Given + client.setAddressLine1("123 Main Street"); + client.setAddressLine2("Suite 456"); + client.setCity("Paris"); + client.setState("Île-de-France"); + client.setPostalCode("75001"); + client.setCountry("France"); + + // Then + assertThat(client.getAddressLine1()).isEqualTo("123 Main Street"); + assertThat(client.getAddressLine2()).isEqualTo("Suite 456"); + assertThat(client.getCity()).isEqualTo("Paris"); + assertThat(client.getState()).isEqualTo("Île-de-France"); + assertThat(client.getPostalCode()).isEqualTo("75001"); + assertThat(client.getCountry()).isEqualTo("France"); + } + + /** + * Test avec des valeurs null optionnelles. + */ + @Test + @DisplayName("Valeurs null optionnelles") + void testOptionalNullValues() { + // Given + Client minimalClient = new Client(); + minimalClient.setUser(user); + minimalClient.setCompanyName("Minimal Company"); + + // Then - Les champs optionnels peuvent être null + assertThat(minimalClient.getIndustry()).isNull(); + assertThat(minimalClient.getCompanySize()).isNull(); + assertThat(minimalClient.getAnnualRevenue()).isNull(); + assertThat(minimalClient.getAddressLine2()).isNull(); + assertThat(minimalClient.getWebsite()).isNull(); + assertThat(minimalClient.getConvertedAt()).isNull(); + assertThat(minimalClient.getPrimaryServiceType()).isNull(); + assertThat(minimalClient.getServiceStartDate()).isNull(); + assertThat(minimalClient.getServiceEndDate()).isNull(); + assertThat(minimalClient.getNotes()).isNull(); + } + + /** + * Test de l'héritage de BaseEntity. + */ + @Test + @DisplayName("Héritage de BaseEntity") + void testBaseEntityInheritance() { + // Given + String createdBy = "admin@gbcm.com"; + + // When + client.setCreatedBy(createdBy); + + // Then + assertThat(client.getCreatedBy()).isEqualTo(createdBy); + assertThat(client.isActive()).isTrue(); // Méthode héritée de BaseEntity + assertThat(client.isDeleted()).isFalse(); // Méthode héritée de BaseEntity + } + + /** + * Test de soft delete sur un client. + */ + @Test + @DisplayName("Soft delete sur un client") + void testClientSoftDelete() { + // Given + String deletedBy = "admin@gbcm.com"; + + // When + client.softDelete(deletedBy); + + // Then + assertThat(client.isDeleted()).isTrue(); + assertThat(client.getDeletedBy()).isEqualTo(deletedBy); + assertThat(client.getDeletedAt()).isNotNull(); + assertThat(client.isActive()).isFalse(); + } +} diff --git a/src/test/java/com/gbcm/server/impl/entity/CoachEntityTest.java b/src/test/java/com/gbcm/server/impl/entity/CoachEntityTest.java new file mode 100644 index 0000000..df96c5f --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/entity/CoachEntityTest.java @@ -0,0 +1,550 @@ +package com.gbcm.server.impl.entity; + +import com.gbcm.server.api.enums.ServiceType; +import com.gbcm.server.api.enums.UserRole; +import com.gbcm.server.api.enums.UserStatus; +import com.gbcm.server.impl.entity.Coach.CoachStatus; +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.*; + +/** + * Tests unitaires pour l'entité Coach. + * Teste toutes les fonctionnalités de l'entité Coach. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@QuarkusTest +@DisplayName("Tests Coach Entity") +class CoachEntityTest { + + private Coach coach; + private User user; + + @BeforeEach + void setUp() { + // Création d'un utilisateur de test + user = new User(); + user.setId(1L); + user.setFirstName("Jane"); + user.setLastName("Smith"); + user.setEmail("jane.smith@gbcm.com"); + user.setRole(UserRole.COACH); + user.setStatus(UserStatus.ACTIVE); + user.setActive(true); + + // Création d'un coach de test + coach = new Coach(); + coach.setUser(user); + coach.setSpecialization("Business Strategy"); + coach.setYearsOfExperience(10); + coach.setCertifications("MBA, PMP, Agile Coach"); + coach.setBio("Experienced business coach with 10+ years in strategic consulting."); + coach.setHourlyRate(new BigDecimal("150.00")); + coach.setStatus(CoachStatus.ACTIVE); + coach.setAvailableForBooking(true); + coach.setWorkingHoursStart(LocalTime.of(9, 0)); + coach.setWorkingHoursEnd(LocalTime.of(17, 0)); + coach.setTimeZone("Europe/Paris"); + } + + /** + * Test de création d'un coach. + */ + @Test + @DisplayName("Création d'un coach") + void testCoachCreation() { + assertThat(coach).isNotNull(); + assertThat(coach.getUser()).isEqualTo(user); + assertThat(coach.getSpecialization()).isEqualTo("Business Strategy"); + assertThat(coach.getStatus()).isEqualTo(CoachStatus.ACTIVE); + } + + /** + * Test des getters et setters pour l'ID. + */ + @Test + @DisplayName("Getters et setters pour l'ID") + void testIdGetterSetter() { + // Given + Long id = 123L; + + // When + coach.setId(id); + + // Then + assertThat(coach.getId()).isEqualTo(id); + } + + /** + * Test des getters et setters pour l'utilisateur. + */ + @Test + @DisplayName("Getters et setters pour l'utilisateur") + void testUserGetterSetter() { + // Given + User newUser = new User(); + newUser.setId(2L); + newUser.setEmail("john.coach@gbcm.com"); + + // When + coach.setUser(newUser); + + // Then + assertThat(coach.getUser()).isEqualTo(newUser); + } + + /** + * Test des getters et setters pour la spécialisation. + */ + @Test + @DisplayName("Getters et setters pour la spécialisation") + void testSpecializationGetterSetter() { + // Given + String specialization = "Leadership Development"; + + // When + coach.setSpecialization(specialization); + + // Then + assertThat(coach.getSpecialization()).isEqualTo(specialization); + } + + /** + * Test des getters et setters pour les années d'expérience. + */ + @Test + @DisplayName("Getters et setters pour les années d'expérience") + void testYearsOfExperienceGetterSetter() { + // Given + Integer years = 15; + + // When + coach.setYearsOfExperience(years); + + // Then + assertThat(coach.getYearsOfExperience()).isEqualTo(years); + } + + /** + * Test des getters et setters pour les certifications. + */ + @Test + @DisplayName("Getters et setters pour les certifications") + void testCertificationsGetterSetter() { + // Given + String certifications = "ICF-PCC, SCRUM Master, Six Sigma Black Belt"; + + // When + coach.setCertifications(certifications); + + // Then + assertThat(coach.getCertifications()).isEqualTo(certifications); + } + + /** + * Test des getters et setters pour la biographie. + */ + @Test + @DisplayName("Getters et setters pour la biographie") + void testBioGetterSetter() { + // Given + String bio = "Passionate coach specializing in executive leadership and organizational transformation."; + + // When + coach.setBio(bio); + + // Then + assertThat(coach.getBio()).isEqualTo(bio); + } + + /** + * Test des getters et setters pour le tarif horaire. + */ + @Test + @DisplayName("Getters et setters pour le tarif horaire") + void testHourlyRateGetterSetter() { + // Given + BigDecimal rate = new BigDecimal("200.50"); + + // When + coach.setHourlyRate(rate); + + // Then + assertThat(coach.getHourlyRate()).isEqualTo(rate); + } + + /** + * Test des getters et setters pour le statut. + */ + @Test + @DisplayName("Getters et setters pour le statut") + void testStatusGetterSetter() { + // Given + CoachStatus status = CoachStatus.ON_LEAVE; + + // When + coach.setStatus(status); + + // Then + assertThat(coach.getStatus()).isEqualTo(status); + } + + /** + * Test des getters et setters pour la disponibilité de réservation. + */ + @Test + @DisplayName("Getters et setters pour la disponibilité de réservation") + void testAvailableForBookingGetterSetter() { + // Given + Boolean available = false; + + // When + coach.setAvailableForBooking(available); + + // Then + assertThat(coach.isAvailableForBooking()).isEqualTo(available); + } + + /** + * Test des getters et setters pour les heures de travail. + */ + @Test + @DisplayName("Getters et setters pour les heures de travail") + void testWorkingHoursGetterSetter() { + // Given + LocalTime start = LocalTime.of(8, 30); + LocalTime end = LocalTime.of(18, 30); + + // When + coach.setWorkingHoursStart(start); + coach.setWorkingHoursEnd(end); + + // Then + assertThat(coach.getWorkingHoursStart()).isEqualTo(start); + assertThat(coach.getWorkingHoursEnd()).isEqualTo(end); + } + + /** + * Test des getters et setters pour le fuseau horaire. + */ + @Test + @DisplayName("Getters et setters pour le fuseau horaire") + void testTimeZoneGetterSetter() { + // Given + String timeZone = "America/New_York"; + + // When + coach.setTimeZone(timeZone); + + // Then + assertThat(coach.getTimeZone()).isEqualTo(timeZone); + } + + /** + * Test des getters et setters pour les langues parlées. + */ + @Test + @DisplayName("Getters et setters pour les langues parlées") + void testLanguagesSpokenGetterSetter() { + // Given + String languages = "French, English, Spanish"; + + // When + coach.setLanguagesSpoken(languages); + + // Then + assertThat(coach.getLanguagesSpoken()).isEqualTo(languages); + } + + /** + * Test des getters et setters pour la date de début. + */ + @Test + @DisplayName("Getters et setters pour la date de début") + void testStartDateGetterSetter() { + // Given + LocalDate startDate = LocalDate.now(); + + // When + coach.setStartDate(startDate); + + // Then + assertThat(coach.getStartDate()).isEqualTo(startDate); + } + + /** + * Test des getters et setters pour la date de fin. + */ + @Test + @DisplayName("Getters et setters pour la date de fin") + void testEndDateGetterSetter() { + // Given + LocalDate endDate = LocalDate.now().plusYears(1); + + // When + coach.setEndDate(endDate); + + // Then + assertThat(coach.getEndDate()).isEqualTo(endDate); + } + + /** + * Test des getters et setters pour les notes. + */ + @Test + @DisplayName("Getters et setters pour les notes") + void testNotesGetterSetter() { + // Given + String notes = "Excellent coach with high client satisfaction ratings."; + + // When + coach.setNotes(notes); + + // Then + assertThat(coach.getNotes()).isEqualTo(notes); + } + + /** + * Test du statut par défaut. + */ + @Test + @DisplayName("Statut par défaut") + void testDefaultStatus() { + // Given + Coach newCoach = new Coach(); + + // Then + assertThat(newCoach.getStatus()).isEqualTo(CoachStatus.ACTIVE); + } + + /** + * Test de la disponibilité par défaut. + */ + @Test + @DisplayName("Disponibilité par défaut") + void testDefaultAvailability() { + // Given + Coach newCoach = new Coach(); + + // Then + assertThat(newCoach.isAvailableForBooking()).isTrue(); + } + + /** + * Test de tous les statuts CoachStatus. + */ + @Test + @DisplayName("Tous les statuts CoachStatus") + void testAllCoachStatuses() { + // Test ACTIVE + coach.setStatus(CoachStatus.ACTIVE); + assertThat(coach.getStatus()).isEqualTo(CoachStatus.ACTIVE); + + // Test INACTIVE + coach.setStatus(CoachStatus.INACTIVE); + assertThat(coach.getStatus()).isEqualTo(CoachStatus.INACTIVE); + + // Test ON_LEAVE + coach.setStatus(CoachStatus.ON_LEAVE); + assertThat(coach.getStatus()).isEqualTo(CoachStatus.ON_LEAVE); + + // Test SUSPENDED + coach.setStatus(CoachStatus.SUSPENDED); + assertThat(coach.getStatus()).isEqualTo(CoachStatus.SUSPENDED); + } + + /** + * Test de coach en congé. + */ + @Test + @DisplayName("Coach en congé") + void testCoachOnLeave() { + // Given + coach.setStatus(CoachStatus.ACTIVE); + coach.setAvailableForBooking(true); + + // When - Mise en congé + coach.setStatus(CoachStatus.ON_LEAVE); + coach.setAvailableForBooking(false); + + // Then + assertThat(coach.getStatus()).isEqualTo(CoachStatus.ON_LEAVE); + assertThat(coach.isAvailableForBooking()).isFalse(); + } + + /** + * Test des heures de travail valides. + */ + @Test + @DisplayName("Heures de travail valides") + void testValidWorkingHours() { + // Given + LocalTime start = LocalTime.of(9, 0); + LocalTime end = LocalTime.of(17, 0); + + // When + coach.setWorkingHoursStart(start); + coach.setWorkingHoursEnd(end); + + // Then + assertThat(coach.getWorkingHoursStart()).isBefore(coach.getWorkingHoursEnd()); + } + + /** + * Test avec des valeurs null optionnelles. + */ + @Test + @DisplayName("Valeurs null optionnelles") + void testOptionalNullValues() { + // Given + Coach minimalCoach = new Coach(); + minimalCoach.setUser(user); + minimalCoach.setSpecialization("General Coaching"); + + // Then - Les champs optionnels peuvent être null + assertThat(minimalCoach.getYearsOfExperience()).isNull(); + assertThat(minimalCoach.getCertifications()).isNull(); + assertThat(minimalCoach.getBio()).isNull(); + assertThat(minimalCoach.getHourlyRate()).isNull(); + assertThat(minimalCoach.getWorkingHoursStart()).isNull(); + assertThat(minimalCoach.getWorkingHoursEnd()).isNull(); + assertThat(minimalCoach.getTimeZone()).isNull(); + assertThat(minimalCoach.getLanguagesSpoken()).isNull(); + assertThat(minimalCoach.getStartDate()).isNull(); + assertThat(minimalCoach.getEndDate()).isNull(); + assertThat(minimalCoach.getNotes()).isNull(); + } + + /** + * Test de l'héritage de BaseEntity. + */ + @Test + @DisplayName("Héritage de BaseEntity") + void testBaseEntityInheritance() { + // Given + String createdBy = "admin@gbcm.com"; + + // When + coach.setCreatedBy(createdBy); + + // Then + assertThat(coach.getCreatedBy()).isEqualTo(createdBy); + assertThat(coach.isActive()).isTrue(); // Méthode héritée de BaseEntity + assertThat(coach.isDeleted()).isFalse(); // Méthode héritée de BaseEntity + } + + /** + * Test de soft delete sur un coach. + */ + @Test + @DisplayName("Soft delete sur un coach") + void testCoachSoftDelete() { + // Given + String deletedBy = "admin@gbcm.com"; + + // When + coach.softDelete(deletedBy); + + // Then + assertThat(coach.isDeleted()).isTrue(); + assertThat(coach.getDeletedBy()).isEqualTo(deletedBy); + assertThat(coach.getDeletedAt()).isNotNull(); + assertThat(coach.isActive()).isFalse(); + } + + /** + * Test de coach avec tarif élevé. + */ + @Test + @DisplayName("Coach avec tarif élevé") + void testHighRateCoach() { + // Given + BigDecimal highRate = new BigDecimal("500.00"); + + // When + coach.setHourlyRate(highRate); + + // Then + assertThat(coach.getHourlyRate()).isEqualTo(highRate); + assertThat(coach.getHourlyRate().compareTo(new BigDecimal("100.00"))).isGreaterThan(0); + } + + /** + * Test de coach multilingue. + */ + @Test + @DisplayName("Coach multilingue") + void testMultilingualCoach() { + // Given + String languages = "French, English, German, Spanish, Italian"; + + // When + coach.setLanguagesSpoken(languages); + + // Then + assertThat(coach.getLanguagesSpoken()).isEqualTo(languages); + assertThat(coach.getLanguagesSpoken()).contains("French"); + assertThat(coach.getLanguagesSpoken()).contains("English"); + assertThat(coach.getLanguagesSpoken()).contains("German"); + } + + /** + * Test de coach avec longue biographie. + */ + @Test + @DisplayName("Coach avec longue biographie") + void testCoachWithLongBio() { + // Given + String longBio = "Dr. Jane Smith is a highly experienced executive coach with over 15 years of experience " + + "in leadership development, organizational transformation, and strategic planning. " + + "She holds a PhD in Organizational Psychology and is certified by the International " + + "Coach Federation (ICF) at the Master Certified Coach (MCC) level. " + + "Her expertise spans across various industries including technology, healthcare, " + + "finance, and manufacturing. She has successfully coached over 200 executives " + + "and has been instrumental in driving organizational change initiatives " + + "that have resulted in significant business improvements."; + + // When + coach.setBio(longBio); + + // Then + assertThat(coach.getBio()).isEqualTo(longBio); + assertThat(coach.getBio().length()).isGreaterThan(500); + } + + /** + * Test de coach avec horaires étendus. + */ + @Test + @DisplayName("Coach avec horaires étendus") + void testExtendedWorkingHours() { + // Given + LocalTime earlyStart = LocalTime.of(7, 0); + LocalTime lateEnd = LocalTime.of(21, 0); + + // When + coach.setWorkingHoursStart(earlyStart); + coach.setWorkingHoursEnd(lateEnd); + + // Then + assertThat(coach.getWorkingHoursStart()).isEqualTo(earlyStart); + assertThat(coach.getWorkingHoursEnd()).isEqualTo(lateEnd); + + // Vérifier que la durée de travail est de 14 heures + long workingHours = java.time.Duration.between(earlyStart, lateEnd).toHours(); + assertThat(workingHours).isEqualTo(14); + } +} diff --git a/src/test/java/com/gbcm/server/impl/service/notification/EmailServiceSimpleTest.java b/src/test/java/com/gbcm/server/impl/service/notification/EmailServiceSimpleTest.java new file mode 100644 index 0000000..342a94c --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/service/notification/EmailServiceSimpleTest.java @@ -0,0 +1,397 @@ +package com.gbcm.server.impl.service.notification; + +import com.gbcm.server.api.enums.UserRole; +import com.gbcm.server.api.enums.UserStatus; +import com.gbcm.server.impl.entity.User; +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 EmailServiceSimple. + * Teste toutes les fonctionnalités d'envoi d'emails en mode simulation. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@QuarkusTest +@DisplayName("Tests EmailServiceSimple") +class EmailServiceSimpleTest { + + @Inject + EmailServiceSimple emailService; + + private User testUser; + + @BeforeEach + void setUp() { + testUser = new User(); + testUser.setId(1L); + testUser.setFirstName("John"); + testUser.setLastName("Doe"); + testUser.setEmail("john.doe@gbcm.com"); + testUser.setRole(UserRole.CLIENT); + testUser.setStatus(UserStatus.ACTIVE); + testUser.setActive(true); + } + + /** + * Test d'injection du service. + */ + @Test + @DisplayName("Injection du service") + void testServiceInjection() { + assertThat(emailService).isNotNull(); + } + + /** + * Test d'envoi d'email de bienvenue avec utilisateur valide. + */ + @Test + @DisplayName("Envoi d'email de bienvenue avec utilisateur valide") + void testSendWelcomeEmail_ValidUser() { + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, null)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi d'email de bienvenue avec mot de passe temporaire. + */ + @Test + @DisplayName("Envoi d'email de bienvenue avec mot de passe temporaire") + void testSendWelcomeEmail_WithTemporaryPassword() { + // Given + String temporaryPassword = "TempPass123!"; + + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, temporaryPassword)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi d'email de bienvenue avec utilisateur null. + */ + @Test + @DisplayName("Envoi d'email de bienvenue avec utilisateur null") + void testSendWelcomeEmail_NullUser() { + // When & Then + assertThatThrownBy(() -> emailService.sendWelcomeEmail(null, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Utilisateur et email requis"); + } + + /** + * Test d'envoi d'email de bienvenue avec email null. + */ + @Test + @DisplayName("Envoi d'email de bienvenue avec email null") + void testSendWelcomeEmail_NullEmail() { + // Given + testUser.setEmail(null); + + // When & Then + assertThatThrownBy(() -> emailService.sendWelcomeEmail(testUser, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Utilisateur et email requis"); + } + + /** + * Test d'envoi d'email de réinitialisation de mot de passe. + */ + @Test + @DisplayName("Envoi d'email de réinitialisation de mot de passe") + void testSendPasswordResetEmail_ValidUser() { + // Given + String resetToken = "reset-token-123"; + + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendPasswordResetEmail(testUser, resetToken)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi d'email de réinitialisation avec utilisateur null. + */ + @Test + @DisplayName("Envoi d'email de réinitialisation avec utilisateur null") + void testSendPasswordResetEmail_NullUser() { + // Given + String resetToken = "reset-token-123"; + + // When & Then + assertThatThrownBy(() -> emailService.sendPasswordResetEmail(null, resetToken)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Utilisateur et token requis"); + } + + /** + * Test d'envoi d'email de réinitialisation avec token null. + */ + @Test + @DisplayName("Envoi d'email de réinitialisation avec token null") + void testSendPasswordResetEmail_NullToken() { + // When & Then + assertThatThrownBy(() -> emailService.sendPasswordResetEmail(testUser, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Utilisateur et token requis"); + } + + /** + * Test d'envoi d'email de réinitialisation avec email null. + */ + @Test + @DisplayName("Envoi d'email de réinitialisation avec email null") + void testSendPasswordResetEmail_NullEmail() { + // Given + testUser.setEmail(null); + String resetToken = "reset-token-123"; + + // When & Then + assertThatThrownBy(() -> emailService.sendPasswordResetEmail(testUser, resetToken)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Utilisateur et token requis"); + } + + /** + * Test d'envoi d'email de notification de changement de mot de passe. + */ + @Test + @DisplayName("Envoi d'email de notification de changement de mot de passe") + void testSendPasswordChangeNotification_ValidUser() { + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendPasswordChangeNotification(testUser)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi d'email de notification avec utilisateur null. + */ + @Test + @DisplayName("Envoi d'email de notification avec utilisateur null") + void testSendPasswordChangeNotification_NullUser() { + // When & Then + assertThatThrownBy(() -> emailService.sendPasswordChangeNotification(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Utilisateur requis"); + } + + /** + * Test d'envoi d'email de notification avec email null. + */ + @Test + @DisplayName("Envoi d'email de notification avec email null") + void testSendPasswordChangeNotification_NullEmail() { + // Given + testUser.setEmail(null); + + // When & Then + assertThatThrownBy(() -> emailService.sendPasswordChangeNotification(testUser)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Utilisateur requis"); + } + + /** + * Test d'envoi d'email générique. + */ + @Test + @DisplayName("Envoi d'email générique") + void testSendEmail_ValidParameters() { + // Given + String to = "test@gbcm.com"; + String subject = "Test Subject"; + String htmlContent = "

Test Content

"; + + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendEmail(to, subject, htmlContent)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi d'email générique avec destinataire null. + */ + @Test + @DisplayName("Envoi d'email générique avec destinataire null") + void testSendEmail_NullTo() { + // Given + String subject = "Test Subject"; + String htmlContent = "

Test Content

"; + + // When & Then + assertThatThrownBy(() -> emailService.sendEmail(null, subject, htmlContent)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Destinataire, sujet et contenu requis"); + } + + /** + * Test d'envoi d'email générique avec sujet null. + */ + @Test + @DisplayName("Envoi d'email générique avec sujet null") + void testSendEmail_NullSubject() { + // Given + String to = "test@gbcm.com"; + String htmlContent = "

Test Content

"; + + // When & Then + assertThatThrownBy(() -> emailService.sendEmail(to, null, htmlContent)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Destinataire, sujet et contenu requis"); + } + + /** + * Test d'envoi d'email générique avec contenu null. + */ + @Test + @DisplayName("Envoi d'email générique avec contenu null") + void testSendEmail_NullContent() { + // Given + String to = "test@gbcm.com"; + String subject = "Test Subject"; + + // When & Then + assertThatThrownBy(() -> emailService.sendEmail(to, subject, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Destinataire, sujet et contenu requis"); + } + + /** + * Test d'envoi d'email avec contenu HTML complexe. + */ + @Test + @DisplayName("Envoi d'email avec contenu HTML complexe") + void testSendEmail_ComplexHtmlContent() { + // Given + String to = "test@gbcm.com"; + String subject = "Test Subject"; + String htmlContent = """ + + +

Welcome to GBCM

+

This is a complex HTML email with:

+
    +
  • Lists
  • +
  • Links: GBCM
  • +
  • Images: Logo
  • +
+

Best regards,
The GBCM Team

+ + + """; + + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendEmail(to, subject, htmlContent)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi multiple d'emails. + */ + @Test + @DisplayName("Envoi multiple d'emails") + void testSendMultipleEmails() { + // Given + String[] recipients = {"user1@gbcm.com", "user2@gbcm.com", "user3@gbcm.com"}; + String subject = "Bulk Email Test"; + String htmlContent = "

Bulk Email Content

"; + + // When & Then - Ne doit pas lever d'exception + for (String recipient : recipients) { + assertThatCode(() -> emailService.sendEmail(recipient, subject, htmlContent)) + .doesNotThrowAnyException(); + } + } + + /** + * Test d'envoi d'email de bienvenue pour différents rôles. + */ + @Test + @DisplayName("Envoi d'email de bienvenue pour différents rôles") + void testSendWelcomeEmail_DifferentRoles() { + // Test pour ADMIN + testUser.setRole(UserRole.ADMIN); + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, null)) + .doesNotThrowAnyException(); + + // Test pour MANAGER + testUser.setRole(UserRole.MANAGER); + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, null)) + .doesNotThrowAnyException(); + + // Test pour COACH + testUser.setRole(UserRole.COACH); + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, null)) + .doesNotThrowAnyException(); + + // Test pour CLIENT + testUser.setRole(UserRole.CLIENT); + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, null)) + .doesNotThrowAnyException(); + + // Test pour PROSPECT + testUser.setRole(UserRole.PROSPECT); + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, null)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi d'email avec caractères spéciaux. + */ + @Test + @DisplayName("Envoi d'email avec caractères spéciaux") + void testSendEmail_SpecialCharacters() { + // Given + testUser.setFirstName("François"); + testUser.setLastName("Müller"); + testUser.setEmail("francois.muller@gbcm.com"); + + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendWelcomeEmail(testUser, null)) + .doesNotThrowAnyException(); + } + + /** + * Test d'envoi d'email avec sujet contenant des caractères spéciaux. + */ + @Test + @DisplayName("Envoi d'email avec sujet contenant des caractères spéciaux") + void testSendEmail_SpecialCharactersInSubject() { + // Given + String to = "test@gbcm.com"; + String subject = "Bienvenue chez GBCM - Votre compte est créé ! 🎉"; + String htmlContent = "

Bienvenue !

"; + + // When & Then - Ne doit pas lever d'exception + assertThatCode(() -> emailService.sendEmail(to, subject, htmlContent)) + .doesNotThrowAnyException(); + } + + /** + * Test de performance d'envoi d'emails. + */ + @Test + @DisplayName("Test de performance d'envoi d'emails") + void testSendEmail_Performance() { + // Given + long startTime = System.currentTimeMillis(); + + // When - Envoi de 50 emails + for (int i = 0; i < 50; i++) { + emailService.sendEmail( + "user" + i + "@gbcm.com", + "Test Subject " + i, + "

Test Content " + i + "

" + ); + } + long endTime = System.currentTimeMillis(); + + // Then + long duration = endTime - startTime; + assertThat(duration).isLessThan(2000); // Moins de 2 secondes pour 50 emails simulés + } +} diff --git a/src/test/java/com/gbcm/server/impl/service/security/PasswordServiceTest.java b/src/test/java/com/gbcm/server/impl/service/security/PasswordServiceTest.java new file mode 100644 index 0000000..a5a45cb --- /dev/null +++ b/src/test/java/com/gbcm/server/impl/service/security/PasswordServiceTest.java @@ -0,0 +1,455 @@ +package com.gbcm.server.impl.service.security; + +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +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 PasswordService. + * Teste toutes les fonctionnalités de gestion des mots de passe. + * + * @author GBCM Development Team + * @version 1.0 + * @since 1.0 + */ +@QuarkusTest +@DisplayName("Tests PasswordService") +class PasswordServiceTest { + + @Inject + PasswordService passwordService; + + /** + * Test d'injection du service. + */ + @Test + @DisplayName("Injection du service") + void testServiceInjection() { + assertThat(passwordService).isNotNull(); + } + + /** + * Test de hachage d'un mot de passe valide. + */ + @Test + @DisplayName("Hachage d'un mot de passe valide") + void testHashPassword_ValidPassword() { + // Given + String plainPassword = "TestPassword123!"; + + // When + String hashedPassword = passwordService.hashPassword(plainPassword); + + // Then + assertThat(hashedPassword).isNotNull(); + assertThat(hashedPassword).isNotEmpty(); + assertThat(hashedPassword).startsWith("$2a$"); + assertThat(hashedPassword).isNotEqualTo(plainPassword); + } + + /** + * Test de hachage avec mot de passe null. + */ + @Test + @DisplayName("Hachage avec mot de passe null") + void testHashPassword_NullPassword() { + // When & Then + assertThatThrownBy(() -> passwordService.hashPassword(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Le mot de passe ne peut pas être null ou vide"); + } + + /** + * Test de hachage avec mot de passe vide. + */ + @Test + @DisplayName("Hachage avec mot de passe vide") + void testHashPassword_EmptyPassword() { + // When & Then + assertThatThrownBy(() -> passwordService.hashPassword("")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Le mot de passe ne peut pas être null ou vide"); + } + + /** + * Test de vérification d'un mot de passe correct. + */ + @Test + @DisplayName("Vérification d'un mot de passe correct") + void testVerifyPassword_CorrectPassword() { + // Given + String plainPassword = "TestPassword123!"; + String hashedPassword = passwordService.hashPassword(plainPassword); + + // When + boolean result = passwordService.verifyPassword(plainPassword, hashedPassword); + + // Then + assertThat(result).isTrue(); + } + + /** + * Test de vérification d'un mot de passe incorrect. + */ + @Test + @DisplayName("Vérification d'un mot de passe incorrect") + void testVerifyPassword_IncorrectPassword() { + // Given + String plainPassword = "TestPassword123!"; + String wrongPassword = "WrongPassword456!"; + String hashedPassword = passwordService.hashPassword(plainPassword); + + // When + boolean result = passwordService.verifyPassword(wrongPassword, hashedPassword); + + // Then + assertThat(result).isFalse(); + } + + /** + * Test de vérification avec mot de passe null. + */ + @Test + @DisplayName("Vérification avec mot de passe null") + void testVerifyPassword_NullPlainPassword() { + // Given + String hashedPassword = passwordService.hashPassword("TestPassword123!"); + + // When & Then + assertThatThrownBy(() -> passwordService.verifyPassword(null, hashedPassword)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Le mot de passe et le hash ne peuvent pas être null ou vides"); + } + + /** + * Test de vérification avec hash null. + */ + @Test + @DisplayName("Vérification avec hash null") + void testVerifyPassword_NullHashedPassword() { + // When & Then + assertThatThrownBy(() -> passwordService.verifyPassword("TestPassword123!", null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Le mot de passe et le hash ne peuvent pas être null ou vides"); + } + + /** + * Test de validation d'un mot de passe valide. + */ + @Test + @DisplayName("Validation d'un mot de passe valide") + void testValidatePassword_ValidPassword() { + // Given + String validPassword = "TestPassword123!"; + + // When + boolean result = passwordService.validatePassword(validPassword); + + // Then + assertThat(result).isTrue(); + } + + /** + * Test de validation d'un mot de passe trop court. + */ + @Test + @DisplayName("Validation d'un mot de passe trop court") + void testValidatePassword_TooShort() { + // Given + String shortPassword = "Test1!"; + + // When + boolean result = passwordService.validatePassword(shortPassword); + + // Then + assertThat(result).isFalse(); + } + + /** + * Test de validation d'un mot de passe sans majuscule. + */ + @Test + @DisplayName("Validation d'un mot de passe sans majuscule") + void testValidatePassword_NoUppercase() { + // Given + String noUppercasePassword = "testpassword123!"; + + // When + boolean result = passwordService.validatePassword(noUppercasePassword); + + // Then + assertThat(result).isFalse(); + } + + /** + * Test de validation d'un mot de passe sans minuscule. + */ + @Test + @DisplayName("Validation d'un mot de passe sans minuscule") + void testValidatePassword_NoLowercase() { + // Given + String noLowercasePassword = "TESTPASSWORD123!"; + + // When + boolean result = passwordService.validatePassword(noLowercasePassword); + + // Then + assertThat(result).isFalse(); + } + + /** + * Test de validation d'un mot de passe sans chiffre. + */ + @Test + @DisplayName("Validation d'un mot de passe sans chiffre") + void testValidatePassword_NoDigit() { + // Given + String noDigitPassword = "TestPassword!"; + + // When + boolean result = passwordService.validatePassword(noDigitPassword); + + // Then + assertThat(result).isFalse(); + } + + /** + * Test de validation d'un mot de passe sans caractère spécial. + */ + @Test + @DisplayName("Validation d'un mot de passe sans caractère spécial") + void testValidatePassword_NoSpecialChar() { + // Given + String noSpecialCharPassword = "TestPassword123"; + + // When + boolean result = passwordService.validatePassword(noSpecialCharPassword); + + // Then + assertThat(result).isFalse(); + } + + /** + * Test de validation avec mot de passe null. + */ + @Test + @DisplayName("Validation avec mot de passe null") + void testValidatePassword_NullPassword() { + // When + boolean result = passwordService.validatePassword(null); + + // Then + assertThat(result).isFalse(); + } + + /** + * Test de génération d'un mot de passe aléatoire avec longueur spécifiée. + */ + @Test + @DisplayName("Génération d'un mot de passe aléatoire avec longueur spécifiée") + void testGenerateRandomPassword_WithLength() { + // Given + int length = 16; + + // When + String password = passwordService.generateRandomPassword(length); + + // Then + assertThat(password).isNotNull(); + assertThat(password).hasSize(length); + assertThat(passwordService.validatePassword(password)).isTrue(); + } + + /** + * Test de génération d'un mot de passe aléatoire avec longueur par défaut. + */ + @Test + @DisplayName("Génération d'un mot de passe aléatoire avec longueur par défaut") + void testGenerateRandomPassword_DefaultLength() { + // When + String password = passwordService.generateRandomPassword(); + + // Then + assertThat(password).isNotNull(); + assertThat(password).hasSize(12); + assertThat(passwordService.validatePassword(password)).isTrue(); + } + + /** + * Test de génération avec longueur minimale. + */ + @Test + @DisplayName("Génération avec longueur minimale") + void testGenerateRandomPassword_MinimumLength() { + // Given + int minLength = 8; + + // When + String password = passwordService.generateRandomPassword(minLength); + + // Then + assertThat(password).isNotNull(); + assertThat(password).hasSize(minLength); + assertThat(passwordService.validatePassword(password)).isTrue(); + } + + /** + * Test de génération avec longueur trop petite. + */ + @Test + @DisplayName("Génération avec longueur trop petite") + void testGenerateRandomPassword_TooShort() { + // When & Then + assertThatThrownBy(() -> passwordService.generateRandomPassword(4)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("La longueur du mot de passe doit être d'au moins 8 caractères"); + } + + /** + * Test de vérification du renouvellement - mot de passe expiré. + */ + @Test + @DisplayName("Vérification du renouvellement - mot de passe expiré") + void testShouldRenewPassword_Expired() { + // Given + LocalDateTime lastChange = LocalDateTime.now().minusDays(100); + int maxAgeInDays = 90; + + // When + boolean shouldRenew = passwordService.shouldRenewPassword(lastChange, maxAgeInDays); + + // Then + assertThat(shouldRenew).isTrue(); + } + + /** + * Test de vérification du renouvellement - mot de passe valide. + */ + @Test + @DisplayName("Vérification du renouvellement - mot de passe valide") + void testShouldRenewPassword_Valid() { + // Given + LocalDateTime lastChange = LocalDateTime.now().minusDays(30); + int maxAgeInDays = 90; + + // When + boolean shouldRenew = passwordService.shouldRenewPassword(lastChange, maxAgeInDays); + + // Then + assertThat(shouldRenew).isFalse(); + } + + /** + * Test de vérification du renouvellement - date null. + */ + @Test + @DisplayName("Vérification du renouvellement - date null") + void testShouldRenewPassword_NullDate() { + // Given + int maxAgeInDays = 90; + + // When + boolean shouldRenew = passwordService.shouldRenewPassword(null, maxAgeInDays); + + // Then + assertThat(shouldRenew).isTrue(); + } + + /** + * Test de calcul de la force d'un mot de passe fort. + */ + @Test + @DisplayName("Calcul de la force d'un mot de passe fort") + void testCalculatePasswordStrength_StrongPassword() { + // Given + String strongPassword = "MyVeryStrongP@ssw0rd123!"; + + // When + int strength = passwordService.calculatePasswordStrength(strongPassword); + + // Then + assertThat(strength).isGreaterThan(80); + } + + /** + * Test de calcul de la force d'un mot de passe faible. + */ + @Test + @DisplayName("Calcul de la force d'un mot de passe faible") + void testCalculatePasswordStrength_WeakPassword() { + // Given + String weakPassword = "password"; + + // When + int strength = passwordService.calculatePasswordStrength(weakPassword); + + // Then + assertThat(strength).isLessThan(50); + } + + /** + * Test de calcul de la force avec mot de passe null. + */ + @Test + @DisplayName("Calcul de la force avec mot de passe null") + void testCalculatePasswordStrength_NullPassword() { + // When + int strength = passwordService.calculatePasswordStrength(null); + + // Then + assertThat(strength).isEqualTo(0); + } + + /** + * Test de calcul de la force avec mot de passe vide. + */ + @Test + @DisplayName("Calcul de la force avec mot de passe vide") + void testCalculatePasswordStrength_EmptyPassword() { + // When + int strength = passwordService.calculatePasswordStrength(""); + + // Then + assertThat(strength).isEqualTo(0); + } + + /** + * Test de génération de mots de passe uniques. + */ + @Test + @DisplayName("Génération de mots de passe uniques") + void testGenerateRandomPassword_Uniqueness() { + // When + String password1 = passwordService.generateRandomPassword(); + String password2 = passwordService.generateRandomPassword(); + + // Then + assertThat(password1).isNotEqualTo(password2); + } + + /** + * Test de performance de hachage. + */ + @Test + @DisplayName("Test de performance de hachage") + void testHashPassword_Performance() { + // Given + String password = "TestPassword123!"; + long startTime = System.currentTimeMillis(); + + // When + for (int i = 0; i < 10; i++) { + passwordService.hashPassword(password + i); + } + long endTime = System.currentTimeMillis(); + + // Then + long duration = endTime - startTime; + assertThat(duration).isLessThan(5000); // Moins de 5 secondes pour 10 hachages + } +}