package dev.lions.unionflow.server.resource; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import dev.lions.unionflow.server.api.dto.paiement.response.PaiementGatewayResponse; import dev.lions.unionflow.server.api.dto.paiement.response.PaiementResponse; import dev.lions.unionflow.server.api.dto.paiement.response.PaiementSummaryResponse; import dev.lions.unionflow.server.service.PaiementService; import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; import io.restassured.http.ContentType; import jakarta.ws.rs.NotFoundException; import java.util.Collections; import java.util.List; import java.util.UUID; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; /** * Tests d'intégration REST pour PaiementResource. * * @author UnionFlow Team * @version 2.0 * @since 2026-03-21 */ @QuarkusTest @TestMethodOrder(MethodOrderer.OrderAnnotation.class) class PaiementResourceTest { private static final String BASE_PATH = "/api/paiements"; private static final String PAIEMENT_ID = "00000000-0000-0000-0000-000000000010"; private static final String MEMBRE_ID = "00000000-0000-0000-0000-000000000020"; @InjectMock PaiementService paiementService; // ------------------------------------------------------------------------- // GET /api/paiements/{id} // ------------------------------------------------------------------------- @Test @Order(1) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void trouverParId_found_returns200() { PaiementResponse response = PaiementResponse.builder() .numeroReference("PAY-001") .build(); when(paiementService.trouverParId(any(UUID.class))).thenReturn(response); given() .pathParam("id", PAIEMENT_ID) .when() .get(BASE_PATH + "/{id}") .then() .statusCode(200) .contentType(ContentType.JSON) .body("$", notNullValue()); } @Test @Order(2) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void trouverParId_notFound_returns404() { when(paiementService.trouverParId(any(UUID.class))) .thenThrow(new NotFoundException("Paiement non trouvé")); given() .pathParam("id", UUID.randomUUID()) .when() .get(BASE_PATH + "/{id}") .then() .statusCode(404); } // ------------------------------------------------------------------------- // GET /api/paiements/reference/{numeroReference} // ------------------------------------------------------------------------- @Test @Order(3) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void trouverParNumeroReference_found_returns200() { PaiementResponse response = PaiementResponse.builder() .numeroReference("PAY-REF-001") .build(); when(paiementService.trouverParNumeroReference(anyString())).thenReturn(response); given() .pathParam("numeroReference", "PAY-REF-001") .when() .get(BASE_PATH + "/reference/{numeroReference}") .then() .statusCode(200) .contentType(ContentType.JSON); } @Test @Order(4) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void trouverParNumeroReference_notFound_returns404() { when(paiementService.trouverParNumeroReference(anyString())) .thenThrow(new NotFoundException("Référence non trouvée")); given() .pathParam("numeroReference", "PAY-INEXISTANT-99999") .when() .get(BASE_PATH + "/reference/{numeroReference}") .then() .statusCode(404); } // ------------------------------------------------------------------------- // GET /api/paiements/membre/{membreId} // ------------------------------------------------------------------------- @Test @Order(5) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void listerParMembre_returns200() { when(paiementService.listerParMembre(any(UUID.class))) .thenReturn(Collections.emptyList()); given() .pathParam("membreId", MEMBRE_ID) .when() .get(BASE_PATH + "/membre/{membreId}") .then() .statusCode(200) .body("$", notNullValue()); } // ------------------------------------------------------------------------- // GET /api/paiements/mes-paiements/historique // ------------------------------------------------------------------------- @Test @Order(6) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void getMonHistoriquePaiements_returns200() { List history = Collections.emptyList(); when(paiementService.getMonHistoriquePaiements(anyInt())).thenReturn(history); given() .queryParam("limit", 5) .when() .get(BASE_PATH + "/mes-paiements/historique") .then() .statusCode(200) .body("$", notNullValue()); } @Test @Order(7) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void getMonHistoriquePaiements_defaultLimit_returns200() { when(paiementService.getMonHistoriquePaiements(anyInt())).thenReturn(Collections.emptyList()); given() .when() .get(BASE_PATH + "/mes-paiements/historique") .then() .statusCode(200); } // ------------------------------------------------------------------------- // POST /api/paiements // ------------------------------------------------------------------------- @Test @Order(8) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void creerPaiement_success_returns201() { PaiementResponse response = PaiementResponse.builder() .numeroReference("PAY-NEW-001") .build(); when(paiementService.creerPaiement(any())).thenReturn(response); String body = String.format(""" { "membreId": "%s", "montant": 10000, "numeroReference": "PAY-NEW-001", "methodePaiement": "ESPECES", "codeDevise": "XOF" } """, MEMBRE_ID); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH) .then() .statusCode(anyOf(equalTo(201), equalTo(400))); } @Test @Order(9) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void creerPaiement_serverError_returns500() { when(paiementService.creerPaiement(any())) .thenThrow(new RuntimeException("db error")); String body = String.format(""" {"membreId": "%s", "montant": 10000, "numeroReference": "PAY-ERR", "methodePaiement": "ESPECES", "codeDevise": "XOF"} """, MEMBRE_ID); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH) .then() .statusCode(anyOf(equalTo(500), equalTo(400))); } // ------------------------------------------------------------------------- // POST /api/paiements/{id}/valider // ------------------------------------------------------------------------- @Test @Order(10) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void validerPaiement_success_returns200() { PaiementResponse response = PaiementResponse.builder() .numeroReference("PAY-001") .build(); when(paiementService.validerPaiement(any(UUID.class))).thenReturn(response); given() .pathParam("id", PAIEMENT_ID) .contentType(ContentType.JSON) .when() .post(BASE_PATH + "/{id}/valider") .then() .statusCode(200) .contentType(ContentType.JSON); } @Test @Order(11) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void validerPaiement_notFound_returns404() { when(paiementService.validerPaiement(any(UUID.class))) .thenThrow(new NotFoundException("Paiement non trouvé")); given() .pathParam("id", UUID.randomUUID()) .contentType(ContentType.JSON) .when() .post(BASE_PATH + "/{id}/valider") .then() .statusCode(404); } // ------------------------------------------------------------------------- // POST /api/paiements/{id}/annuler // ------------------------------------------------------------------------- @Test @Order(12) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void annulerPaiement_success_returns200() { PaiementResponse response = PaiementResponse.builder() .numeroReference("PAY-001") .build(); when(paiementService.annulerPaiement(any(UUID.class))).thenReturn(response); given() .pathParam("id", PAIEMENT_ID) .contentType(ContentType.JSON) .when() .post(BASE_PATH + "/{id}/annuler") .then() .statusCode(200) .contentType(ContentType.JSON); } @Test @Order(13) @TestSecurity(user = "admin@unionflow.com", roles = {"ADMIN"}) void annulerPaiement_notFound_returns404() { when(paiementService.annulerPaiement(any(UUID.class))) .thenThrow(new NotFoundException("Paiement non trouvé")); given() .pathParam("id", UUID.randomUUID()) .contentType(ContentType.JSON) .when() .post(BASE_PATH + "/{id}/annuler") .then() .statusCode(404); } // ------------------------------------------------------------------------- // POST /api/paiements/initier-paiement-en-ligne // ------------------------------------------------------------------------- @Test @Order(14) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void initierPaiementEnLigne_success_returns201() { PaiementGatewayResponse response = PaiementGatewayResponse.builder() .transactionId(UUID.randomUUID()) .redirectUrl("https://wave.example.com/pay/TXN-001") .build(); when(paiementService.initierPaiementEnLigne(any())).thenReturn(response); String body = String.format(""" { "cotisationId": "%s", "methodePaiement": "WAVE", "numeroTelephone": "771234567" } """, UUID.randomUUID()); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH + "/initier-paiement-en-ligne") .then() .statusCode(anyOf(equalTo(201), equalTo(400))); } @Test @Order(15) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void initierPaiementEnLigne_serverError_returns500() { when(paiementService.initierPaiementEnLigne(any())) .thenThrow(new RuntimeException("gateway error")); String body = String.format(""" {"cotisationId": "%s", "methodePaiement": "WAVE", "numeroTelephone": "771234567"} """, UUID.randomUUID()); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH + "/initier-paiement-en-ligne") .then() .statusCode(anyOf(equalTo(500), equalTo(400))); } // ------------------------------------------------------------------------- // POST /api/paiements/initier-depot-epargne-en-ligne // ------------------------------------------------------------------------- @Test @Order(16) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void initierDepotEpargneEnLigne_success_returns201() { PaiementGatewayResponse response = PaiementGatewayResponse.builder() .transactionId(UUID.randomUUID()) .redirectUrl("https://wave.example.com/pay/DEPOT-001") .build(); when(paiementService.initierDepotEpargneEnLigne(any())).thenReturn(response); String body = String.format(""" { "compteId": "%s", "montant": 10000, "numeroTelephone": "771234567" } """, UUID.randomUUID()); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH + "/initier-depot-epargne-en-ligne") .then() .statusCode(anyOf(equalTo(201), equalTo(400))); } @Test @Order(17) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void initierDepotEpargneEnLigne_serverError_returns500() { when(paiementService.initierDepotEpargneEnLigne(any())) .thenThrow(new RuntimeException("epargne error")); String body = String.format(""" {"compteId": "%s", "montant": 10000, "numeroTelephone": "771234567"} """, UUID.randomUUID()); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH + "/initier-depot-epargne-en-ligne") .then() .statusCode(anyOf(equalTo(500), equalTo(400))); } // ------------------------------------------------------------------------- // POST /api/paiements/declarer-paiement-manuel // ------------------------------------------------------------------------- @Test @Order(18) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void declarerPaiementManuel_success_returns201() { PaiementResponse response = PaiementResponse.builder() .numeroReference("MANUEL-001") .build(); when(paiementService.declarerPaiementManuel(any())).thenReturn(response); String body = String.format(""" { "cotisationId": "%s", "methodePaiement": "ESPECES", "montant": 5000, "dateDeclaration": "2026-03-21", "commentaire": "Paiement remis en main propre" } """, UUID.randomUUID()); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH + "/declarer-paiement-manuel") .then() .statusCode(201) .contentType(ContentType.JSON); } @Test @Order(19) @TestSecurity(user = "membre@unionflow.com", roles = {"MEMBRE"}) void declarerPaiementManuel_serverError_returns500() { when(paiementService.declarerPaiementManuel(any())) .thenThrow(new RuntimeException("declaration error")); String body = String.format(""" {"cotisationId": "%s", "methodePaiement": "ESPECES", "montant": 5000} """, UUID.randomUUID()); given() .contentType(ContentType.JSON) .body(body) .when() .post(BASE_PATH + "/declarer-paiement-manuel") .then() .statusCode(500); } }