feat(mobile): Implement Keycloak WebView authentication with HTTP callback
- Replace flutter_appauth with custom WebView implementation to resolve deep link issues - Add KeycloakWebViewAuthService with integrated WebView for seamless authentication - Configure Android manifest for HTTP cleartext traffic support - Add network security config for development environment (192.168.1.11) - Update Keycloak client to use HTTP callback endpoint (http://192.168.1.11:8080/auth/callback) - Remove obsolete keycloak_auth_service.dart and temporary scripts - Clean up dependencies and regenerate injection configuration - Tested successfully on multiple Android devices (Xiaomi 2201116TG, SM A725F) BREAKING CHANGE: Authentication flow now uses WebView instead of external browser - Users will see Keycloak login page within the app instead of browser redirect - Resolves ERR_CLEARTEXT_NOT_PERMITTED and deep link state management issues - Maintains full OIDC compliance with PKCE flow and secure token storage Technical improvements: - WebView with custom navigation delegate for callback handling - Automatic token extraction and user info parsing from JWT - Proper error handling and user feedback - Consistent authentication state management across app lifecycle
This commit is contained in:
@@ -0,0 +1,413 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.entity.Evenement;
|
||||
import dev.lions.unionflow.server.entity.Evenement.StatutEvenement;
|
||||
import dev.lions.unionflow.server.entity.Evenement.TypeEvenement;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.security.TestSecurity;
|
||||
import io.restassured.http.ContentType;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
* Tests d'intégration pour EvenementResource
|
||||
*
|
||||
* Tests complets de l'API REST des événements avec authentification
|
||||
* et validation des permissions. Optimisé pour l'intégration mobile.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@QuarkusTest
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@DisplayName("Tests d'intégration - API Événements")
|
||||
class EvenementResourceTest {
|
||||
|
||||
private static Long evenementTestId;
|
||||
private static Long organisationTestId;
|
||||
private static Long membreTestId;
|
||||
|
||||
@BeforeAll
|
||||
@Transactional
|
||||
static void setupTestData() {
|
||||
// Créer une organisation de test
|
||||
Organisation organisation = Organisation.builder()
|
||||
.nom("Union Test API")
|
||||
.typeOrganisation("ASSOCIATION")
|
||||
.statut("ACTIVE")
|
||||
.email("test-api@union.com")
|
||||
.telephone("0123456789")
|
||||
.adresse("123 Rue de Test")
|
||||
.codePostal("75001")
|
||||
.ville("Paris")
|
||||
.pays("France")
|
||||
.actif(true)
|
||||
.creePar("test@unionflow.dev")
|
||||
.dateCreation(LocalDateTime.now())
|
||||
.build();
|
||||
organisation.persist();
|
||||
organisationTestId = organisation.id;
|
||||
|
||||
// Créer un membre de test
|
||||
Membre membre = Membre.builder()
|
||||
.numeroMembre("UF2025-API01")
|
||||
.prenom("Marie")
|
||||
.nom("Martin")
|
||||
.email("marie.martin@test.com")
|
||||
.telephone("0987654321")
|
||||
.dateNaissance(LocalDate.of(1990, 5, 15))
|
||||
.dateAdhesion(LocalDate.now())
|
||||
.actif(true)
|
||||
.organisation(organisation)
|
||||
.build();
|
||||
membre.persist();
|
||||
membreTestId = membre.id;
|
||||
|
||||
// Créer un événement de test
|
||||
Evenement evenement = Evenement.builder()
|
||||
.titre("Conférence API Test")
|
||||
.description("Conférence de test pour l'API")
|
||||
.dateDebut(LocalDateTime.now().plusDays(15))
|
||||
.dateFin(LocalDateTime.now().plusDays(15).plusHours(2))
|
||||
.lieu("Centre de conférence Test")
|
||||
.typeEvenement(TypeEvenement.CONFERENCE)
|
||||
.statut(StatutEvenement.PLANIFIE)
|
||||
.capaciteMax(50)
|
||||
.prix(BigDecimal.valueOf(15.00))
|
||||
.inscriptionRequise(true)
|
||||
.visiblePublic(true)
|
||||
.actif(true)
|
||||
.organisation(organisation)
|
||||
.organisateur(membre)
|
||||
.creePar("test@unionflow.dev")
|
||||
.dateCreation(LocalDateTime.now())
|
||||
.build();
|
||||
evenement.persist();
|
||||
evenementTestId = evenement.id;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@DisplayName("GET /api/evenements - Lister événements (authentifié)")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testListerEvenements_Authentifie() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/evenements")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("size()", greaterThanOrEqualTo(1))
|
||||
.body("[0].titre", notNullValue())
|
||||
.body("[0].dateDebut", notNullValue())
|
||||
.body("[0].statut", notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
@DisplayName("GET /api/evenements - Non authentifié")
|
||||
void testListerEvenements_NonAuthentifie() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/evenements")
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
@DisplayName("GET /api/evenements/{id} - Récupérer événement")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testObtenirEvenement() {
|
||||
given()
|
||||
.pathParam("id", evenementTestId)
|
||||
.when()
|
||||
.get("/api/evenements/{id}")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("id", equalTo(evenementTestId.intValue()))
|
||||
.body("titre", equalTo("Conférence API Test"))
|
||||
.body("description", equalTo("Conférence de test pour l'API"))
|
||||
.body("typeEvenement", equalTo("CONFERENCE"))
|
||||
.body("statut", equalTo("PLANIFIE"))
|
||||
.body("capaciteMax", equalTo(50))
|
||||
.body("prix", equalTo(15.0f))
|
||||
.body("inscriptionRequise", equalTo(true))
|
||||
.body("visiblePublic", equalTo(true))
|
||||
.body("actif", equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
@DisplayName("GET /api/evenements/{id} - Événement non trouvé")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testObtenirEvenement_NonTrouve() {
|
||||
given()
|
||||
.pathParam("id", 99999)
|
||||
.when()
|
||||
.get("/api/evenements/{id}")
|
||||
.then()
|
||||
.statusCode(404)
|
||||
.body("error", equalTo("Événement non trouvé"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
@DisplayName("POST /api/evenements - Créer événement (organisateur)")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"ORGANISATEUR_EVENEMENT"})
|
||||
void testCreerEvenement_Organisateur() {
|
||||
String nouvelEvenement = String.format("""
|
||||
{
|
||||
"titre": "Nouvel Événement Test",
|
||||
"description": "Description du nouvel événement",
|
||||
"dateDebut": "%s",
|
||||
"dateFin": "%s",
|
||||
"lieu": "Lieu de test",
|
||||
"typeEvenement": "FORMATION",
|
||||
"capaciteMax": 30,
|
||||
"prix": 20.00,
|
||||
"inscriptionRequise": true,
|
||||
"visiblePublic": true,
|
||||
"organisation": {"id": %d},
|
||||
"organisateur": {"id": %d}
|
||||
}
|
||||
""",
|
||||
LocalDateTime.now().plusDays(20).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
|
||||
LocalDateTime.now().plusDays(20).plusHours(3).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
|
||||
organisationTestId,
|
||||
membreTestId
|
||||
);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(nouvelEvenement)
|
||||
.when()
|
||||
.post("/api/evenements")
|
||||
.then()
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("titre", equalTo("Nouvel Événement Test"))
|
||||
.body("typeEvenement", equalTo("FORMATION"))
|
||||
.body("capaciteMax", equalTo(30))
|
||||
.body("prix", equalTo(20.0f))
|
||||
.body("actif", equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
@DisplayName("POST /api/evenements - Permissions insuffisantes")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testCreerEvenement_PermissionsInsuffisantes() {
|
||||
String nouvelEvenement = """
|
||||
{
|
||||
"titre": "Événement Non Autorisé",
|
||||
"description": "Test permissions",
|
||||
"dateDebut": "2025-02-15T10:00:00",
|
||||
"dateFin": "2025-02-15T12:00:00",
|
||||
"lieu": "Lieu test",
|
||||
"typeEvenement": "FORMATION"
|
||||
}
|
||||
""";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(nouvelEvenement)
|
||||
.when()
|
||||
.post("/api/evenements")
|
||||
.then()
|
||||
.statusCode(403);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
@DisplayName("PUT /api/evenements/{id} - Mettre à jour événement")
|
||||
@TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
|
||||
void testMettreAJourEvenement_Admin() {
|
||||
String evenementModifie = String.format("""
|
||||
{
|
||||
"titre": "Conférence API Test - Modifiée",
|
||||
"description": "Description mise à jour",
|
||||
"dateDebut": "%s",
|
||||
"dateFin": "%s",
|
||||
"lieu": "Nouveau lieu",
|
||||
"typeEvenement": "CONFERENCE",
|
||||
"capaciteMax": 75,
|
||||
"prix": 25.00,
|
||||
"inscriptionRequise": true,
|
||||
"visiblePublic": true
|
||||
}
|
||||
""",
|
||||
LocalDateTime.now().plusDays(16).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
|
||||
LocalDateTime.now().plusDays(16).plusHours(3).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||
);
|
||||
|
||||
given()
|
||||
.pathParam("id", evenementTestId)
|
||||
.contentType(ContentType.JSON)
|
||||
.body(evenementModifie)
|
||||
.when()
|
||||
.put("/api/evenements/{id}")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("titre", equalTo("Conférence API Test - Modifiée"))
|
||||
.body("description", equalTo("Description mise à jour"))
|
||||
.body("lieu", equalTo("Nouveau lieu"))
|
||||
.body("capaciteMax", equalTo(75))
|
||||
.body("prix", equalTo(25.0f));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
@DisplayName("GET /api/evenements/a-venir - Événements à venir")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testEvenementsAVenir() {
|
||||
given()
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 10)
|
||||
.when()
|
||||
.get("/api/evenements/a-venir")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(9)
|
||||
@DisplayName("GET /api/evenements/publics - Événements publics (non authentifié)")
|
||||
void testEvenementsPublics_NonAuthentifie() {
|
||||
given()
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 20)
|
||||
.when()
|
||||
.get("/api/evenements/publics")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(10)
|
||||
@DisplayName("GET /api/evenements/recherche - Recherche d'événements")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testRechercherEvenements() {
|
||||
given()
|
||||
.queryParam("q", "Conférence")
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 20)
|
||||
.when()
|
||||
.get("/api/evenements/recherche")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(11)
|
||||
@DisplayName("GET /api/evenements/recherche - Terme de recherche manquant")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testRechercherEvenements_TermeManquant() {
|
||||
given()
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 20)
|
||||
.when()
|
||||
.get("/api/evenements/recherche")
|
||||
.then()
|
||||
.statusCode(400)
|
||||
.body("error", equalTo("Le terme de recherche est obligatoire"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(12)
|
||||
@DisplayName("GET /api/evenements/type/{type} - Événements par type")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testEvenementsParType() {
|
||||
given()
|
||||
.pathParam("type", "CONFERENCE")
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 20)
|
||||
.when()
|
||||
.get("/api/evenements/type/{type}")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(13)
|
||||
@DisplayName("PATCH /api/evenements/{id}/statut - Changer statut")
|
||||
@TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
|
||||
void testChangerStatut() {
|
||||
given()
|
||||
.pathParam("id", evenementTestId)
|
||||
.queryParam("statut", "CONFIRME")
|
||||
.when()
|
||||
.patch("/api/evenements/{id}/statut")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("statut", equalTo("CONFIRME"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(14)
|
||||
@DisplayName("GET /api/evenements/statistiques - Statistiques")
|
||||
@TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
|
||||
void testObtenirStatistiques() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/evenements/statistiques")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("total", notNullValue())
|
||||
.body("actifs", notNullValue())
|
||||
.body("timestamp", notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(15)
|
||||
@DisplayName("DELETE /api/evenements/{id} - Supprimer événement")
|
||||
@TestSecurity(user = "admin@unionflow.dev", roles = {"ADMIN"})
|
||||
void testSupprimerEvenement() {
|
||||
given()
|
||||
.pathParam("id", evenementTestId)
|
||||
.when()
|
||||
.delete("/api/evenements/{id}")
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(16)
|
||||
@DisplayName("Pagination - Paramètres valides")
|
||||
@TestSecurity(user = "marie.martin@test.com", roles = {"MEMBRE"})
|
||||
void testPagination() {
|
||||
given()
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 5)
|
||||
.queryParam("sort", "titre")
|
||||
.queryParam("direction", "asc")
|
||||
.when()
|
||||
.get("/api/evenements")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.organisation.OrganisationDTO;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.security.TestSecurity;
|
||||
import io.restassured.http.ContentType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
|
||||
/**
|
||||
* Tests d'intégration pour OrganisationResource
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@QuarkusTest
|
||||
class OrganisationResourceTest {
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testCreerOrganisation_Success() {
|
||||
OrganisationDTO organisation = createTestOrganisationDTO();
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(organisation)
|
||||
.when()
|
||||
.post("/api/organisations")
|
||||
.then()
|
||||
.statusCode(201)
|
||||
.body("nom", equalTo("Lions Club Test API"))
|
||||
.body("email", equalTo("testapi@lionsclub.org"))
|
||||
.body("actif", equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testCreerOrganisation_EmailInvalide() {
|
||||
OrganisationDTO organisation = createTestOrganisationDTO();
|
||||
organisation.setEmail("email-invalide");
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(organisation)
|
||||
.when()
|
||||
.post("/api/organisations")
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testCreerOrganisation_NomVide() {
|
||||
OrganisationDTO organisation = createTestOrganisationDTO();
|
||||
organisation.setNom("");
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(organisation)
|
||||
.when()
|
||||
.post("/api/organisations")
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreerOrganisation_NonAuthentifie() {
|
||||
OrganisationDTO organisation = createTestOrganisationDTO();
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(organisation)
|
||||
.when()
|
||||
.post("/api/organisations")
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testListerOrganisations_Success() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/organisations")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testListerOrganisations_AvecPagination() {
|
||||
given()
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 10)
|
||||
.when()
|
||||
.get("/api/organisations")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testListerOrganisations_AvecRecherche() {
|
||||
given()
|
||||
.queryParam("recherche", "Lions")
|
||||
.when()
|
||||
.get("/api/organisations")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListerOrganisations_NonAuthentifie() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/organisations")
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testObtenirOrganisation_NonTrouvee() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/organisations/99999")
|
||||
.then()
|
||||
.statusCode(404)
|
||||
.body("error", equalTo("Organisation non trouvée"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testMettreAJourOrganisation_NonTrouvee() {
|
||||
OrganisationDTO organisation = createTestOrganisationDTO();
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(organisation)
|
||||
.when()
|
||||
.put("/api/organisations/99999")
|
||||
.then()
|
||||
.statusCode(404)
|
||||
.body("error", containsString("Organisation non trouvée"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testSupprimerOrganisation_NonTrouvee() {
|
||||
given()
|
||||
.when()
|
||||
.delete("/api/organisations/99999")
|
||||
.then()
|
||||
.statusCode(404)
|
||||
.body("error", containsString("Organisation non trouvée"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testRechercheAvancee_Success() {
|
||||
given()
|
||||
.queryParam("nom", "Lions")
|
||||
.queryParam("ville", "Abidjan")
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 10)
|
||||
.when()
|
||||
.get("/api/organisations/recherche")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testRechercheAvancee_SansCriteres() {
|
||||
given()
|
||||
.queryParam("page", 0)
|
||||
.queryParam("size", 10)
|
||||
.when()
|
||||
.get("/api/organisations/recherche")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("size()", greaterThanOrEqualTo(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testActiverOrganisation_NonTrouvee() {
|
||||
given()
|
||||
.when()
|
||||
.post("/api/organisations/99999/activer")
|
||||
.then()
|
||||
.statusCode(404)
|
||||
.body("error", containsString("Organisation non trouvée"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testSuspendreOrganisation_NonTrouvee() {
|
||||
given()
|
||||
.when()
|
||||
.post("/api/organisations/99999/suspendre")
|
||||
.then()
|
||||
.statusCode(404)
|
||||
.body("error", containsString("Organisation non trouvée"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testObtenirStatistiques_Success() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/organisations/statistiques")
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("totalOrganisations", notNullValue())
|
||||
.body("organisationsActives", notNullValue())
|
||||
.body("organisationsInactives", notNullValue())
|
||||
.body("nouvellesOrganisations30Jours", notNullValue())
|
||||
.body("tauxActivite", notNullValue())
|
||||
.body("timestamp", notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testObtenirStatistiques_NonAuthentifie() {
|
||||
given()
|
||||
.when()
|
||||
.get("/api/organisations/statistiques")
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test de workflow complet : création, lecture, mise à jour, suppression
|
||||
*/
|
||||
@Test
|
||||
@TestSecurity(user = "testUser", roles = {"ADMIN"})
|
||||
void testWorkflowComplet() {
|
||||
// 1. Créer une organisation
|
||||
OrganisationDTO organisation = createTestOrganisationDTO();
|
||||
organisation.setNom("Lions Club Workflow Test");
|
||||
organisation.setEmail("workflow@lionsclub.org");
|
||||
|
||||
String location = given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(organisation)
|
||||
.when()
|
||||
.post("/api/organisations")
|
||||
.then()
|
||||
.statusCode(201)
|
||||
.extract()
|
||||
.header("Location");
|
||||
|
||||
// Extraire l'ID de l'organisation créée
|
||||
String organisationId = location.substring(location.lastIndexOf("/") + 1);
|
||||
|
||||
// 2. Lire l'organisation créée
|
||||
given()
|
||||
.when()
|
||||
.get("/api/organisations/" + organisationId)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("nom", equalTo("Lions Club Workflow Test"))
|
||||
.body("email", equalTo("workflow@lionsclub.org"));
|
||||
|
||||
// 3. Mettre à jour l'organisation
|
||||
organisation.setDescription("Description mise à jour");
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(organisation)
|
||||
.when()
|
||||
.put("/api/organisations/" + organisationId)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.body("description", equalTo("Description mise à jour"));
|
||||
|
||||
// 4. Suspendre l'organisation
|
||||
given()
|
||||
.when()
|
||||
.post("/api/organisations/" + organisationId + "/suspendre")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
// 5. Activer l'organisation
|
||||
given()
|
||||
.when()
|
||||
.post("/api/organisations/" + organisationId + "/activer")
|
||||
.then()
|
||||
.statusCode(200);
|
||||
|
||||
// 6. Supprimer l'organisation (soft delete)
|
||||
given()
|
||||
.when()
|
||||
.delete("/api/organisations/" + organisationId)
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un DTO d'organisation pour les tests
|
||||
*/
|
||||
private OrganisationDTO createTestOrganisationDTO() {
|
||||
OrganisationDTO dto = new OrganisationDTO();
|
||||
dto.setId(UUID.randomUUID());
|
||||
dto.setNom("Lions Club Test API");
|
||||
dto.setNomCourt("LC Test API");
|
||||
dto.setEmail("testapi@lionsclub.org");
|
||||
dto.setDescription("Organisation de test pour l'API");
|
||||
dto.setTelephone("+225 01 02 03 04 05");
|
||||
dto.setAdresse("123 Rue de Test API");
|
||||
dto.setVille("Abidjan");
|
||||
dto.setCodePostal("00225");
|
||||
dto.setRegion("Lagunes");
|
||||
dto.setPays("Côte d'Ivoire");
|
||||
dto.setSiteWeb("https://testapi.lionsclub.org");
|
||||
dto.setObjectifs("Servir la communauté");
|
||||
dto.setActivitesPrincipales("Actions sociales et humanitaires");
|
||||
dto.setNombreMembres(0);
|
||||
dto.setDateCreation(LocalDateTime.now());
|
||||
dto.setActif(true);
|
||||
dto.setVersion(0L);
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import dev.lions.unionflow.server.entity.Evenement;
|
||||
import dev.lions.unionflow.server.entity.Evenement.StatutEvenement;
|
||||
import dev.lions.unionflow.server.entity.Evenement.TypeEvenement;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.repository.EvenementRepository;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.mockito.InjectMock;
|
||||
import jakarta.inject.Inject;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour EvenementService
|
||||
*
|
||||
* Tests complets du service de gestion des événements avec
|
||||
* validation des règles métier et intégration Keycloak.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@QuarkusTest
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
@DisplayName("Tests unitaires - Service Événements")
|
||||
class EvenementServiceTest {
|
||||
|
||||
@Inject
|
||||
EvenementService evenementService;
|
||||
|
||||
@InjectMock
|
||||
EvenementRepository evenementRepository;
|
||||
|
||||
@InjectMock
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@InjectMock
|
||||
OrganisationRepository organisationRepository;
|
||||
|
||||
@InjectMock
|
||||
KeycloakService keycloakService;
|
||||
|
||||
private Evenement evenementTest;
|
||||
private Organisation organisationTest;
|
||||
private Membre membreTest;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// Données de test
|
||||
organisationTest = Organisation.builder()
|
||||
.nom("Union Test")
|
||||
.typeOrganisation("ASSOCIATION")
|
||||
.statut("ACTIVE")
|
||||
.email("test@union.com")
|
||||
.actif(true)
|
||||
.build();
|
||||
organisationTest.id = 1L;
|
||||
|
||||
membreTest = Membre.builder()
|
||||
.numeroMembre("UF2025-TEST01")
|
||||
.prenom("Jean")
|
||||
.nom("Dupont")
|
||||
.email("jean.dupont@test.com")
|
||||
.actif(true)
|
||||
.build();
|
||||
membreTest.id = 1L;
|
||||
|
||||
evenementTest = Evenement.builder()
|
||||
.titre("Assemblée Générale 2025")
|
||||
.description("Assemblée générale annuelle de l'union")
|
||||
.dateDebut(LocalDateTime.now().plusDays(30))
|
||||
.dateFin(LocalDateTime.now().plusDays(30).plusHours(3))
|
||||
.lieu("Salle de conférence")
|
||||
.typeEvenement(TypeEvenement.ASSEMBLEE_GENERALE)
|
||||
.statut(StatutEvenement.PLANIFIE)
|
||||
.capaciteMax(100)
|
||||
.prix(BigDecimal.valueOf(25.00))
|
||||
.inscriptionRequise(true)
|
||||
.visiblePublic(true)
|
||||
.actif(true)
|
||||
.organisation(organisationTest)
|
||||
.organisateur(membreTest)
|
||||
.build();
|
||||
evenementTest.id = 1L;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
@DisplayName("Création d'événement - Succès")
|
||||
void testCreerEvenement_Succes() {
|
||||
// Given
|
||||
when(keycloakService.getCurrentUserEmail()).thenReturn("jean.dupont@test.com");
|
||||
when(evenementRepository.findByTitre(anyString())).thenReturn(Optional.empty());
|
||||
doNothing().when(evenementRepository).persist(any(Evenement.class));
|
||||
|
||||
// When
|
||||
Evenement resultat = evenementService.creerEvenement(evenementTest);
|
||||
|
||||
// Then
|
||||
assertNotNull(resultat);
|
||||
assertEquals("Assemblée Générale 2025", resultat.getTitre());
|
||||
assertEquals(StatutEvenement.PLANIFIE, resultat.getStatut());
|
||||
assertTrue(resultat.getActif());
|
||||
assertEquals("jean.dupont@test.com", resultat.getCreePar());
|
||||
|
||||
verify(evenementRepository).persist(any(Evenement.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
@DisplayName("Création d'événement - Titre obligatoire")
|
||||
void testCreerEvenement_TitreObligatoire() {
|
||||
// Given
|
||||
evenementTest.setTitre(null);
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> evenementService.creerEvenement(evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("Le titre de l'événement est obligatoire", exception.getMessage());
|
||||
verify(evenementRepository, never()).persist(any(Evenement.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
@DisplayName("Création d'événement - Date de début obligatoire")
|
||||
void testCreerEvenement_DateDebutObligatoire() {
|
||||
// Given
|
||||
evenementTest.setDateDebut(null);
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> evenementService.creerEvenement(evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("La date de début est obligatoire", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
@DisplayName("Création d'événement - Date de début dans le passé")
|
||||
void testCreerEvenement_DateDebutPassee() {
|
||||
// Given
|
||||
evenementTest.setDateDebut(LocalDateTime.now().minusDays(1));
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> evenementService.creerEvenement(evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("La date de début ne peut pas être dans le passé", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
@DisplayName("Création d'événement - Date de fin antérieure à date de début")
|
||||
void testCreerEvenement_DateFinInvalide() {
|
||||
// Given
|
||||
evenementTest.setDateFin(evenementTest.getDateDebut().minusHours(1));
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> evenementService.creerEvenement(evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("La date de fin ne peut pas être antérieure à la date de début", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(6)
|
||||
@DisplayName("Mise à jour d'événement - Succès")
|
||||
void testMettreAJourEvenement_Succes() {
|
||||
// Given
|
||||
when(evenementRepository.findByIdOptional(1L)).thenReturn(Optional.of(evenementTest));
|
||||
when(keycloakService.hasRole("ADMIN")).thenReturn(true);
|
||||
when(keycloakService.getCurrentUserEmail()).thenReturn("admin@test.com");
|
||||
doNothing().when(evenementRepository).persist(any(Evenement.class));
|
||||
|
||||
Evenement evenementMisAJour = Evenement.builder()
|
||||
.titre("Assemblée Générale 2025 - Modifiée")
|
||||
.description("Description mise à jour")
|
||||
.dateDebut(LocalDateTime.now().plusDays(35))
|
||||
.dateFin(LocalDateTime.now().plusDays(35).plusHours(4))
|
||||
.lieu("Nouvelle salle")
|
||||
.typeEvenement(TypeEvenement.ASSEMBLEE_GENERALE)
|
||||
.capaciteMax(150)
|
||||
.prix(BigDecimal.valueOf(30.00))
|
||||
.inscriptionRequise(true)
|
||||
.visiblePublic(true)
|
||||
.build();
|
||||
|
||||
// When
|
||||
Evenement resultat = evenementService.mettreAJourEvenement(1L, evenementMisAJour);
|
||||
|
||||
// Then
|
||||
assertNotNull(resultat);
|
||||
assertEquals("Assemblée Générale 2025 - Modifiée", resultat.getTitre());
|
||||
assertEquals("Description mise à jour", resultat.getDescription());
|
||||
assertEquals("Nouvelle salle", resultat.getLieu());
|
||||
assertEquals(150, resultat.getCapaciteMax());
|
||||
assertEquals(BigDecimal.valueOf(30.00), resultat.getPrix());
|
||||
assertEquals("admin@test.com", resultat.getModifiePar());
|
||||
|
||||
verify(evenementRepository).persist(any(Evenement.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(7)
|
||||
@DisplayName("Mise à jour d'événement - Événement non trouvé")
|
||||
void testMettreAJourEvenement_NonTrouve() {
|
||||
// Given
|
||||
when(evenementRepository.findByIdOptional(999L)).thenReturn(Optional.empty());
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> evenementService.mettreAJourEvenement(999L, evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("Événement non trouvé avec l'ID: 999", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(8)
|
||||
@DisplayName("Suppression d'événement - Succès")
|
||||
void testSupprimerEvenement_Succes() {
|
||||
// Given
|
||||
when(evenementRepository.findByIdOptional(1L)).thenReturn(Optional.of(evenementTest));
|
||||
when(keycloakService.hasRole("ADMIN")).thenReturn(true);
|
||||
when(keycloakService.getCurrentUserEmail()).thenReturn("admin@test.com");
|
||||
when(evenementTest.getNombreInscrits()).thenReturn(0);
|
||||
doNothing().when(evenementRepository).persist(any(Evenement.class));
|
||||
|
||||
// When
|
||||
assertDoesNotThrow(() -> evenementService.supprimerEvenement(1L));
|
||||
|
||||
// Then
|
||||
assertFalse(evenementTest.getActif());
|
||||
assertEquals("admin@test.com", evenementTest.getModifiePar());
|
||||
verify(evenementRepository).persist(any(Evenement.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(9)
|
||||
@DisplayName("Recherche d'événements - Succès")
|
||||
void testRechercherEvenements_Succes() {
|
||||
// Given
|
||||
List<Evenement> evenementsAttendus = List.of(evenementTest);
|
||||
when(evenementRepository.findByTitreOrDescription(anyString(), any(Page.class), any(Sort.class)))
|
||||
.thenReturn(evenementsAttendus);
|
||||
|
||||
// When
|
||||
List<Evenement> resultat = evenementService.rechercherEvenements(
|
||||
"Assemblée", Page.of(0, 10), Sort.by("dateDebut"));
|
||||
|
||||
// Then
|
||||
assertNotNull(resultat);
|
||||
assertEquals(1, resultat.size());
|
||||
assertEquals("Assemblée Générale 2025", resultat.get(0).getTitre());
|
||||
|
||||
verify(evenementRepository).findByTitreOrDescription(eq("Assemblée"), any(Page.class), any(Sort.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(10)
|
||||
@DisplayName("Changement de statut - Succès")
|
||||
void testChangerStatut_Succes() {
|
||||
// Given
|
||||
when(evenementRepository.findByIdOptional(1L)).thenReturn(Optional.of(evenementTest));
|
||||
when(keycloakService.hasRole("ADMIN")).thenReturn(true);
|
||||
when(keycloakService.getCurrentUserEmail()).thenReturn("admin@test.com");
|
||||
doNothing().when(evenementRepository).persist(any(Evenement.class));
|
||||
|
||||
// When
|
||||
Evenement resultat = evenementService.changerStatut(1L, StatutEvenement.CONFIRME);
|
||||
|
||||
// Then
|
||||
assertNotNull(resultat);
|
||||
assertEquals(StatutEvenement.CONFIRME, resultat.getStatut());
|
||||
assertEquals("admin@test.com", resultat.getModifiePar());
|
||||
|
||||
verify(evenementRepository).persist(any(Evenement.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(11)
|
||||
@DisplayName("Statistiques des événements")
|
||||
void testObtenirStatistiques() {
|
||||
// Given
|
||||
Map<String, Long> statsBase = Map.of(
|
||||
"total", 100L,
|
||||
"actifs", 80L,
|
||||
"aVenir", 30L,
|
||||
"enCours", 5L,
|
||||
"passes", 45L,
|
||||
"publics", 70L,
|
||||
"avecInscription", 25L
|
||||
);
|
||||
when(evenementRepository.getStatistiques()).thenReturn(statsBase);
|
||||
|
||||
// When
|
||||
Map<String, Object> resultat = evenementService.obtenirStatistiques();
|
||||
|
||||
// Then
|
||||
assertNotNull(resultat);
|
||||
assertEquals(100L, resultat.get("total"));
|
||||
assertEquals(80L, resultat.get("actifs"));
|
||||
assertEquals(30L, resultat.get("aVenir"));
|
||||
assertEquals(80.0, resultat.get("tauxActivite"));
|
||||
assertEquals(37.5, resultat.get("tauxEvenementsAVenir"));
|
||||
assertEquals(6.25, resultat.get("tauxEvenementsEnCours"));
|
||||
assertNotNull(resultat.get("timestamp"));
|
||||
|
||||
verify(evenementRepository).getStatistiques();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(12)
|
||||
@DisplayName("Lister événements actifs avec pagination")
|
||||
void testListerEvenementsActifs() {
|
||||
// Given
|
||||
List<Evenement> evenementsAttendus = List.of(evenementTest);
|
||||
when(evenementRepository.findAllActifs(any(Page.class), any(Sort.class)))
|
||||
.thenReturn(evenementsAttendus);
|
||||
|
||||
// When
|
||||
List<Evenement> resultat = evenementService.listerEvenementsActifs(
|
||||
Page.of(0, 20), Sort.by("dateDebut"));
|
||||
|
||||
// Then
|
||||
assertNotNull(resultat);
|
||||
assertEquals(1, resultat.size());
|
||||
assertEquals("Assemblée Générale 2025", resultat.get(0).getTitre());
|
||||
|
||||
verify(evenementRepository).findAllActifs(any(Page.class), any(Sort.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(13)
|
||||
@DisplayName("Validation des règles métier - Prix négatif")
|
||||
void testValidation_PrixNegatif() {
|
||||
// Given
|
||||
evenementTest.setPrix(BigDecimal.valueOf(-10.00));
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> evenementService.creerEvenement(evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("Le prix ne peut pas être négatif", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(14)
|
||||
@DisplayName("Validation des règles métier - Capacité négative")
|
||||
void testValidation_CapaciteNegative() {
|
||||
// Given
|
||||
evenementTest.setCapaciteMax(-5);
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> evenementService.creerEvenement(evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("La capacité maximale ne peut pas être négative", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(15)
|
||||
@DisplayName("Permissions - Utilisateur non autorisé")
|
||||
void testPermissions_UtilisateurNonAutorise() {
|
||||
// Given
|
||||
when(evenementRepository.findByIdOptional(1L)).thenReturn(Optional.of(evenementTest));
|
||||
when(keycloakService.hasRole(anyString())).thenReturn(false);
|
||||
when(keycloakService.getCurrentUserEmail()).thenReturn("autre@test.com");
|
||||
|
||||
// When & Then
|
||||
SecurityException exception = assertThrows(
|
||||
SecurityException.class,
|
||||
() -> evenementService.mettreAJourEvenement(1L, evenementTest)
|
||||
);
|
||||
|
||||
assertEquals("Vous n'avez pas les permissions pour modifier cet événement", exception.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.mockito.InjectMock;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour OrganisationService
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@QuarkusTest
|
||||
class OrganisationServiceTest {
|
||||
|
||||
@Inject
|
||||
OrganisationService organisationService;
|
||||
|
||||
@InjectMock
|
||||
OrganisationRepository organisationRepository;
|
||||
|
||||
private Organisation organisationTest;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
organisationTest = Organisation.builder()
|
||||
.nom("Lions Club Test")
|
||||
.nomCourt("LC Test")
|
||||
.email("test@lionsclub.org")
|
||||
.typeOrganisation("LIONS_CLUB")
|
||||
.statut("ACTIVE")
|
||||
.description("Organisation de test")
|
||||
.telephone("+225 01 02 03 04 05")
|
||||
.adresse("123 Rue de Test")
|
||||
.ville("Abidjan")
|
||||
.region("Lagunes")
|
||||
.pays("Côte d'Ivoire")
|
||||
.nombreMembres(25)
|
||||
.actif(true)
|
||||
.dateCreation(LocalDateTime.now())
|
||||
.version(0L)
|
||||
.build();
|
||||
organisationTest.id = 1L;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreerOrganisation_Success() {
|
||||
// Given
|
||||
Organisation organisationToCreate = Organisation.builder()
|
||||
.nom("Lions Club Test New")
|
||||
.email("testnew@lionsclub.org")
|
||||
.typeOrganisation("LIONS_CLUB")
|
||||
.build();
|
||||
|
||||
when(organisationRepository.findByEmail("testnew@lionsclub.org")).thenReturn(Optional.empty());
|
||||
when(organisationRepository.findByNom("Lions Club Test New")).thenReturn(Optional.empty());
|
||||
|
||||
// When
|
||||
Organisation result = organisationService.creerOrganisation(organisationToCreate);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals("Lions Club Test New", result.getNom());
|
||||
assertEquals("ACTIVE", result.getStatut());
|
||||
verify(organisationRepository).findByEmail("testnew@lionsclub.org");
|
||||
verify(organisationRepository).findByNom("Lions Club Test New");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreerOrganisation_EmailDejaExistant() {
|
||||
// Given
|
||||
when(organisationRepository.findByEmail(anyString())).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> organisationService.creerOrganisation(organisationTest));
|
||||
|
||||
assertEquals("Une organisation avec cet email existe déjà", exception.getMessage());
|
||||
verify(organisationRepository).findByEmail("test@lionsclub.org");
|
||||
verify(organisationRepository, never()).findByNom(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreerOrganisation_NomDejaExistant() {
|
||||
// Given
|
||||
when(organisationRepository.findByEmail(anyString())).thenReturn(Optional.empty());
|
||||
when(organisationRepository.findByNom(anyString())).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When & Then
|
||||
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
|
||||
() -> organisationService.creerOrganisation(organisationTest));
|
||||
|
||||
assertEquals("Une organisation avec ce nom existe déjà", exception.getMessage());
|
||||
verify(organisationRepository).findByEmail("test@lionsclub.org");
|
||||
verify(organisationRepository).findByNom("Lions Club Test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMettreAJourOrganisation_Success() {
|
||||
// Given
|
||||
Organisation organisationMiseAJour = Organisation.builder()
|
||||
.nom("Lions Club Test Modifié")
|
||||
.email("test@lionsclub.org")
|
||||
.description("Description modifiée")
|
||||
.telephone("+225 01 02 03 04 06")
|
||||
.build();
|
||||
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.of(organisationTest));
|
||||
when(organisationRepository.findByNom("Lions Club Test Modifié")).thenReturn(Optional.empty());
|
||||
|
||||
// When
|
||||
Organisation result = organisationService.mettreAJourOrganisation(1L, organisationMiseAJour, "testUser");
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals("Lions Club Test Modifié", result.getNom());
|
||||
assertEquals("Description modifiée", result.getDescription());
|
||||
assertEquals("+225 01 02 03 04 06", result.getTelephone());
|
||||
assertEquals("testUser", result.getModifiePar());
|
||||
assertNotNull(result.getDateModification());
|
||||
assertEquals(1L, result.getVersion());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMettreAJourOrganisation_OrganisationNonTrouvee() {
|
||||
// Given
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.empty());
|
||||
|
||||
// When & Then
|
||||
NotFoundException exception = assertThrows(NotFoundException.class,
|
||||
() -> organisationService.mettreAJourOrganisation(1L, organisationTest, "testUser"));
|
||||
|
||||
assertEquals("Organisation non trouvée avec l'ID: 1", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupprimerOrganisation_Success() {
|
||||
// Given
|
||||
organisationTest.setNombreMembres(0);
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When
|
||||
organisationService.supprimerOrganisation(1L, "testUser");
|
||||
|
||||
// Then
|
||||
assertFalse(organisationTest.getActif());
|
||||
assertEquals("DISSOUTE", organisationTest.getStatut());
|
||||
assertEquals("testUser", organisationTest.getModifiePar());
|
||||
assertNotNull(organisationTest.getDateModification());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSupprimerOrganisation_AvecMembresActifs() {
|
||||
// Given
|
||||
organisationTest.setNombreMembres(5);
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When & Then
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class,
|
||||
() -> organisationService.supprimerOrganisation(1L, "testUser"));
|
||||
|
||||
assertEquals("Impossible de supprimer une organisation avec des membres actifs", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTrouverParId_Success() {
|
||||
// Given
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When
|
||||
Optional<Organisation> result = organisationService.trouverParId(1L);
|
||||
|
||||
// Then
|
||||
assertTrue(result.isPresent());
|
||||
assertEquals("Lions Club Test", result.get().getNom());
|
||||
verify(organisationRepository).findByIdOptional(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTrouverParId_NonTrouve() {
|
||||
// Given
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.empty());
|
||||
|
||||
// When
|
||||
Optional<Organisation> result = organisationService.trouverParId(1L);
|
||||
|
||||
// Then
|
||||
assertFalse(result.isPresent());
|
||||
verify(organisationRepository).findByIdOptional(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTrouverParEmail_Success() {
|
||||
// Given
|
||||
when(organisationRepository.findByEmail("test@lionsclub.org")).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When
|
||||
Optional<Organisation> result = organisationService.trouverParEmail("test@lionsclub.org");
|
||||
|
||||
// Then
|
||||
assertTrue(result.isPresent());
|
||||
assertEquals("Lions Club Test", result.get().getNom());
|
||||
verify(organisationRepository).findByEmail("test@lionsclub.org");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testListerOrganisationsActives() {
|
||||
// Given
|
||||
List<Organisation> organisations = Arrays.asList(organisationTest);
|
||||
when(organisationRepository.findAllActives()).thenReturn(organisations);
|
||||
|
||||
// When
|
||||
List<Organisation> result = organisationService.listerOrganisationsActives();
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.size());
|
||||
assertEquals("Lions Club Test", result.get(0).getNom());
|
||||
verify(organisationRepository).findAllActives();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testActiverOrganisation_Success() {
|
||||
// Given
|
||||
organisationTest.setStatut("SUSPENDUE");
|
||||
organisationTest.setActif(false);
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When
|
||||
Organisation result = organisationService.activerOrganisation(1L, "testUser");
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals("ACTIVE", result.getStatut());
|
||||
assertTrue(result.getActif());
|
||||
assertEquals("testUser", result.getModifiePar());
|
||||
assertNotNull(result.getDateModification());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuspendreOrganisation_Success() {
|
||||
// Given
|
||||
when(organisationRepository.findByIdOptional(1L)).thenReturn(Optional.of(organisationTest));
|
||||
|
||||
// When
|
||||
Organisation result = organisationService.suspendreOrganisation(1L, "testUser");
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals("SUSPENDUE", result.getStatut());
|
||||
assertFalse(result.getAccepteNouveauxMembres());
|
||||
assertEquals("testUser", result.getModifiePar());
|
||||
assertNotNull(result.getDateModification());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testObtenirStatistiques() {
|
||||
// Given
|
||||
when(organisationRepository.count()).thenReturn(100L);
|
||||
when(organisationRepository.countActives()).thenReturn(85L);
|
||||
when(organisationRepository.countNouvellesOrganisations(any(LocalDate.class))).thenReturn(5L);
|
||||
|
||||
// When
|
||||
Map<String, Object> result = organisationService.obtenirStatistiques();
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals(100L, result.get("totalOrganisations"));
|
||||
assertEquals(85L, result.get("organisationsActives"));
|
||||
assertEquals(15L, result.get("organisationsInactives"));
|
||||
assertEquals(5L, result.get("nouvellesOrganisations30Jours"));
|
||||
assertEquals(85.0, result.get("tauxActivite"));
|
||||
assertNotNull(result.get("timestamp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConvertToDTO() {
|
||||
// When
|
||||
var dto = organisationService.convertToDTO(organisationTest);
|
||||
|
||||
// Then
|
||||
assertNotNull(dto);
|
||||
assertEquals("Lions Club Test", dto.getNom());
|
||||
assertEquals("LC Test", dto.getNomCourt());
|
||||
assertEquals("test@lionsclub.org", dto.getEmail());
|
||||
assertEquals("Organisation de test", dto.getDescription());
|
||||
assertEquals("+225 01 02 03 04 05", dto.getTelephone());
|
||||
assertEquals("Abidjan", dto.getVille());
|
||||
assertEquals(25, dto.getNombreMembres());
|
||||
assertTrue(dto.getActif());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConvertToDTO_Null() {
|
||||
// When
|
||||
var dto = organisationService.convertToDTO(null);
|
||||
|
||||
// Then
|
||||
assertNull(dto);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConvertFromDTO() {
|
||||
// Given
|
||||
var dto = organisationService.convertToDTO(organisationTest);
|
||||
|
||||
// When
|
||||
Organisation result = organisationService.convertFromDTO(dto);
|
||||
|
||||
// Then
|
||||
assertNotNull(result);
|
||||
assertEquals("Lions Club Test", result.getNom());
|
||||
assertEquals("LC Test", result.getNomCourt());
|
||||
assertEquals("test@lionsclub.org", result.getEmail());
|
||||
assertEquals("Organisation de test", result.getDescription());
|
||||
assertEquals("+225 01 02 03 04 05", result.getTelephone());
|
||||
assertEquals("Abidjan", result.getVille());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConvertFromDTO_Null() {
|
||||
// When
|
||||
Organisation result = organisationService.convertFromDTO(null);
|
||||
|
||||
// Then
|
||||
assertNull(result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user