Refactoring - Version stable

This commit is contained in:
dahoud
2026-03-28 14:21:30 +00:00
parent 00b981c510
commit a740c172ef
4402 changed files with 88517 additions and 1555 deletions

View File

@@ -118,6 +118,94 @@ class AdresseTest {
assertThat(a.toString()).isNotNull().isNotEmpty();
}
// ── Branch coverage: getAdresseComplete ────────────────────────────────
@Test
@DisplayName("getAdresseComplete: only complementAdresse (sb empty at append)")
void getAdresseComplete_onlyComplementAdresse() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setComplementAdresse("Bât B");
assertThat(a.getAdresseComplete()).isEqualTo("Bât B");
}
@Test
@DisplayName("getAdresseComplete: only codePostal (sb empty at append)")
void getAdresseComplete_onlyCodePostal() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setCodePostal("75002");
assertThat(a.getAdresseComplete()).isEqualTo("75002");
}
@Test
@DisplayName("getAdresseComplete: only region (sb empty at append)")
void getAdresseComplete_onlyRegion() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setRegion("Bretagne");
assertThat(a.getAdresseComplete()).isEqualTo("Bretagne");
}
@Test
@DisplayName("getAdresseComplete: only pays (sb empty at append)")
void getAdresseComplete_onlyPays() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setPays("France");
assertThat(a.getAdresseComplete()).isEqualTo("France");
}
@Test
@DisplayName("getAdresseComplete: adresse + codePostal (skip complementAdresse), triggers sb>0 for codePostal")
void getAdresseComplete_adresseAndCodePostal() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setAdresse("2 rue X");
a.setCodePostal("13000");
assertThat(a.getAdresseComplete()).isEqualTo("2 rue X, 13000");
}
@Test
@DisplayName("getAdresseComplete: codePostal + ville (sb>0 for ville space separator)")
void getAdresseComplete_codePostalAndVille() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setCodePostal("69001");
a.setVille("Lyon");
assertThat(a.getAdresseComplete()).isEqualTo("69001 Lyon");
}
@Test
@DisplayName("getAdresseComplete: adresse with empty string fields ignored")
void getAdresseComplete_emptyStringFieldsIgnored() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setAdresse("3 rue Y");
a.setComplementAdresse("");
a.setCodePostal("");
a.setVille("");
a.setRegion("");
a.setPays("");
assertThat(a.getAdresseComplete()).isEqualTo("3 rue Y");
}
// ── Branch coverage manquante ──────────────────────────────────────────
/**
* L107 branch manquante : adresse != null mais adresse.isEmpty() → false (deuxième branche du &&)
* → `if (adresse != null && !adresse.isEmpty())` → false (adresse est vide "")
*/
@Test
@DisplayName("getAdresseComplete: adresse = empty string (non null) → ignorée (branche isEmpty)")
void getAdresseComplete_adresseEmptyString_ignored() {
Adresse a = new Adresse();
a.setTypeAdresse("SIEGE");
a.setAdresse(""); // non null mais vide → condition !adresse.isEmpty() est false → ignorée
a.setVille("Dakar");
assertThat(a.getAdresseComplete()).isEqualTo("Dakar");
}
@Test
@DisplayName("relations: organisation, membre, evenement")
void relations() {

View File

@@ -0,0 +1,169 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("ApproverAction")
class ApproverActionTest {
private static TransactionApproval newApproval() {
TransactionApproval ta = new TransactionApproval();
ta.setId(UUID.randomUUID());
return ta;
}
// -------------------------------------------------------------------------
// approve
// -------------------------------------------------------------------------
@Test
@DisplayName("approve: positionne la décision à APPROVED")
void approve_setsDecisionApproved() {
ApproverAction a = new ApproverAction();
a.setDecision("PENDING");
a.approve("Tout est correct");
assertThat(a.getDecision()).isEqualTo("APPROVED");
}
@Test
@DisplayName("approve: positionne le commentaire")
void approve_setsComment() {
ApproverAction a = new ApproverAction();
a.approve("Approuvé sans réserve");
assertThat(a.getComment()).isEqualTo("Approuvé sans réserve");
}
@Test
@DisplayName("approve: positionne decidedAt à une date non nulle")
void approve_setsDecidedAt() {
LocalDateTime before = LocalDateTime.now().minusSeconds(1);
ApproverAction a = new ApproverAction();
a.approve("OK");
assertThat(a.getDecidedAt()).isNotNull().isAfterOrEqualTo(before);
}
// -------------------------------------------------------------------------
// reject
// -------------------------------------------------------------------------
@Test
@DisplayName("reject: positionne la décision à REJECTED")
void reject_setsDecisionRejected() {
ApproverAction a = new ApproverAction();
a.setDecision("PENDING");
a.reject("Montant trop élevé");
assertThat(a.getDecision()).isEqualTo("REJECTED");
}
@Test
@DisplayName("reject: positionne la raison dans le commentaire")
void reject_setsReason() {
ApproverAction a = new ApproverAction();
a.reject("Justificatif manquant");
assertThat(a.getComment()).isEqualTo("Justificatif manquant");
}
@Test
@DisplayName("reject: positionne decidedAt à une date non nulle")
void reject_setsDecidedAt() {
LocalDateTime before = LocalDateTime.now().minusSeconds(1);
ApproverAction a = new ApproverAction();
a.reject("Raison");
assertThat(a.getDecidedAt()).isNotNull().isAfterOrEqualTo(before);
}
// -------------------------------------------------------------------------
// onCreate (réflexion)
// -------------------------------------------------------------------------
@Test
@DisplayName("onCreate: initialise decision à PENDING si null")
void onCreate_initializesDecisionIfNull() throws Exception {
ApproverAction a = new ApproverAction();
a.setDecision(null);
Method onCreate = ApproverAction.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(a);
assertThat(a.getDecision()).isEqualTo("PENDING");
}
@Test
@DisplayName("onCreate: ne remplace pas decision si déjà renseigné")
void onCreate_doesNotOverrideDecision() throws Exception {
ApproverAction a = new ApproverAction();
a.setDecision("APPROVED");
Method onCreate = ApproverAction.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(a);
assertThat(a.getDecision()).isEqualTo("APPROVED");
}
// -------------------------------------------------------------------------
// builder
// -------------------------------------------------------------------------
@Test
@DisplayName("builder: positionne tous les champs correctement")
void builder_setsAllFields() {
TransactionApproval approval = newApproval();
UUID approverId = UUID.randomUUID();
LocalDateTime decidedAt = LocalDateTime.of(2026, 3, 20, 14, 30);
ApproverAction a = ApproverAction.builder()
.approval(approval)
.approverId(approverId)
.approverName("Mamadou Diallo")
.approverRole("TRESORIER")
.decision("APPROVED")
.comment("Dépense justifiée")
.decidedAt(decidedAt)
.build();
assertThat(a.getApproval()).isSameAs(approval);
assertThat(a.getApproverId()).isEqualTo(approverId);
assertThat(a.getApproverName()).isEqualTo("Mamadou Diallo");
assertThat(a.getApproverRole()).isEqualTo("TRESORIER");
assertThat(a.getDecision()).isEqualTo("APPROVED");
assertThat(a.getComment()).isEqualTo("Dépense justifiée");
assertThat(a.getDecidedAt()).isEqualTo(decidedAt);
}
// -------------------------------------------------------------------------
// getters / setters
// -------------------------------------------------------------------------
@Test
@DisplayName("getters/setters: tous les champs accessibles en lecture/écriture")
void gettersSetters_workCorrectly() {
TransactionApproval approval = newApproval();
UUID approverId = UUID.randomUUID();
LocalDateTime decidedAt = LocalDateTime.now();
ApproverAction a = new ApproverAction();
a.setApproval(approval);
a.setApproverId(approverId);
a.setApproverName("Aïssata Koné");
a.setApproverRole("VICE_PRESIDENT");
a.setDecision("REJECTED");
a.setComment("Documents insuffisants");
a.setDecidedAt(decidedAt);
assertThat(a.getApproval()).isSameAs(approval);
assertThat(a.getApproverId()).isEqualTo(approverId);
assertThat(a.getApproverName()).isEqualTo("Aïssata Koné");
assertThat(a.getApproverRole()).isEqualTo("VICE_PRESIDENT");
assertThat(a.getDecision()).isEqualTo("REJECTED");
assertThat(a.getComment()).isEqualTo("Documents insuffisants");
assertThat(a.getDecidedAt()).isEqualTo(decidedAt);
}
}

View File

@@ -106,6 +106,20 @@ class AyantDroitTest {
assertThat(a.isCouvertAujourdhui()).isFalse();
}
@Test
@DisplayName("isCouvertAujourdhui: false si actif=false même avec dates valides")
void isCouvertAujourdhui_false_whenActifFalse() {
AyantDroit a = new AyantDroit();
a.setMembreOrganisation(newMembreOrganisation());
a.setPrenom("X");
a.setNom("Y");
a.setLienParente(LienParente.ENFANT);
a.setDateDebutCouverture(LocalDate.now().minusDays(1));
a.setDateFinCouverture(null);
a.setActif(false);
assertThat(a.isCouvertAujourdhui()).isFalse();
}
@Test
@DisplayName("isCouvertAujourdhui: true si actif et dates couvrent aujourd'hui")
void isCouvertAujourdhui_true() {
@@ -151,4 +165,62 @@ class AyantDroitTest {
a.setLienParente(LienParente.ENFANT);
assertThat(a.toString()).isNotNull().isNotEmpty();
}
// ── Branch coverage manquantes ─────────────────────────────────────────
@Test
@DisplayName("isCouvertAujourdhui: dateDebutCouverture null → condition L85 null short-circuit → continue (branche dateDebutCouverture==null)")
void isCouvertAujourdhui_debutNull_branchNullShortCircuit() {
AyantDroit a = new AyantDroit();
a.setMembreOrganisation(newMembreOrganisation());
a.setPrenom("X");
a.setNom("Y");
a.setLienParente(LienParente.ENFANT);
a.setDateDebutCouverture(null); // null → dateDebutCouverture != null = false → skip at L85
a.setDateFinCouverture(null); // null → dateFinCouverture != null = false → skip at L87
a.setActif(true);
// Pas de retour false aux conditions → return Boolean.TRUE.equals(true) = true
assertThat(a.isCouvertAujourdhui()).isTrue();
}
/**
* L85 branch manquante : dateDebutCouverture != null mais today >= dateDebutCouverture
* (today is NOT before → condition false)
* → couvre la branche `dateDebutCouverture != null && today.isBefore(...) → false`
*/
@Test
@DisplayName("isCouvertAujourdhui: dateDebutCouverture non null mais pas avant → continue (branche false)")
void isCouvertAujourdhui_debutNonNull_nonBefore_continueToActif() {
AyantDroit a = new AyantDroit();
a.setMembreOrganisation(newMembreOrganisation());
a.setPrenom("X");
a.setNom("Y");
a.setLienParente(LienParente.ENFANT);
// dateDebutCouverture est dans le passé → today.isBefore(debutCouverture) est false
a.setDateDebutCouverture(LocalDate.now().minusDays(5));
a.setDateFinCouverture(null);
a.setActif(true);
// Ne retourne pas false à la première condition → continue et retourne true (actif=true)
assertThat(a.isCouvertAujourdhui()).isTrue();
}
/**
* L87 branch manquante : dateFinCouverture != null mais today <= dateFinCouverture
* (today is NOT after → condition false)
* → couvre la branche `dateFinCouverture != null && today.isAfter(...) → false`
*/
@Test
@DisplayName("isCouvertAujourdhui: dateFinCouverture non null mais pas encore dépassée → continue (branche false)")
void isCouvertAujourdhui_finNonNull_nonAfter_continueToActif() {
AyantDroit a = new AyantDroit();
a.setMembreOrganisation(newMembreOrganisation());
a.setPrenom("X");
a.setNom("Y");
a.setLienParente(LienParente.ENFANT);
a.setDateDebutCouverture(LocalDate.now().minusDays(5));
// dateFinCouverture dans le futur → today.isAfter(finCouverture) est false → continue
a.setDateFinCouverture(LocalDate.now().plusDays(5));
a.setActif(true);
assertThat(a.isCouvertAujourdhui()).isTrue();
}
}

View File

@@ -0,0 +1,144 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("BudgetLine")
class BudgetLineTest {
private static Budget newBudget() {
Budget b = new Budget();
b.setId(UUID.randomUUID());
return b;
}
// -------------------------------------------------------------------------
// getRealizationRate
// -------------------------------------------------------------------------
@Test
@DisplayName("getRealizationRate: amountPlanned == 0 renvoie 0.0")
void getRealizationRate_zeroPlan_returns0() {
BudgetLine line = new BudgetLine();
line.setAmountPlanned(BigDecimal.ZERO);
line.setAmountRealized(new BigDecimal("100.00"));
assertThat(line.getRealizationRate()).isEqualTo(0.0);
}
@Test
@DisplayName("getRealizationRate: 75 réalisé / 100 prévu = 75%")
void getRealizationRate_withValues_returnsRatio() {
BudgetLine line = new BudgetLine();
line.setAmountPlanned(new BigDecimal("100.00"));
line.setAmountRealized(new BigDecimal("75.00"));
assertThat(line.getRealizationRate()).isEqualTo(75.0);
}
// -------------------------------------------------------------------------
// getVariance
// -------------------------------------------------------------------------
@Test
@DisplayName("getVariance: positif quand réalisé > prévu")
void getVariance_positive_whenOver() {
BudgetLine line = new BudgetLine();
line.setAmountPlanned(new BigDecimal("100.00"));
line.setAmountRealized(new BigDecimal("120.00"));
assertThat(line.getVariance()).isEqualByComparingTo("20.00");
}
@Test
@DisplayName("getVariance: négatif quand réalisé < prévu")
void getVariance_negative_whenUnder() {
BudgetLine line = new BudgetLine();
line.setAmountPlanned(new BigDecimal("100.00"));
line.setAmountRealized(new BigDecimal("80.00"));
assertThat(line.getVariance()).isEqualByComparingTo("-20.00");
}
// -------------------------------------------------------------------------
// isOverBudget
// -------------------------------------------------------------------------
@Test
@DisplayName("isOverBudget: réalisé > prévu renvoie true")
void isOverBudget_whenOver_returnsTrue() {
BudgetLine line = new BudgetLine();
line.setAmountPlanned(new BigDecimal("200.00"));
line.setAmountRealized(new BigDecimal("201.00"));
assertThat(line.isOverBudget()).isTrue();
}
@Test
@DisplayName("isOverBudget: réalisé <= prévu renvoie false")
void isOverBudget_whenNotOver_returnsFalse() {
BudgetLine line = new BudgetLine();
line.setAmountPlanned(new BigDecimal("200.00"));
line.setAmountRealized(new BigDecimal("200.00"));
assertThat(line.isOverBudget()).isFalse();
}
// -------------------------------------------------------------------------
// onCreate (réflexion)
// -------------------------------------------------------------------------
@Test
@DisplayName("onCreate: initialise amountRealized à ZERO si null")
void onCreate_initializesAmountRealizedIfNull() throws Exception {
BudgetLine line = new BudgetLine();
line.setAmountRealized(null);
Method onCreate = BudgetLine.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(line);
assertThat(line.getAmountRealized()).isEqualByComparingTo(BigDecimal.ZERO);
}
@Test
@DisplayName("onCreate: ne remplace pas amountRealized si déjà renseigné")
void onCreate_doesNotOverrideAmountRealized() throws Exception {
BudgetLine line = new BudgetLine();
line.setAmountRealized(new BigDecimal("350.00"));
Method onCreate = BudgetLine.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(line);
assertThat(line.getAmountRealized()).isEqualByComparingTo("350.00");
}
// -------------------------------------------------------------------------
// builder
// -------------------------------------------------------------------------
@Test
@DisplayName("builder: positionne tous les champs correctement")
void builder_setsAllFields() {
Budget budget = newBudget();
BudgetLine line = BudgetLine.builder()
.budget(budget)
.category("CONTRIBUTIONS")
.name("Cotisations membres")
.description("Cotisations mensuelles")
.amountPlanned(new BigDecimal("50000.00"))
.amountRealized(new BigDecimal("42000.00"))
.notes("Léger retard de collecte")
.build();
assertThat(line.getBudget()).isSameAs(budget);
assertThat(line.getCategory()).isEqualTo("CONTRIBUTIONS");
assertThat(line.getName()).isEqualTo("Cotisations membres");
assertThat(line.getDescription()).isEqualTo("Cotisations mensuelles");
assertThat(line.getAmountPlanned()).isEqualByComparingTo("50000.00");
assertThat(line.getAmountRealized()).isEqualByComparingTo("42000.00");
assertThat(line.getNotes()).isEqualTo("Léger retard de collecte");
}
}

View File

@@ -0,0 +1,319 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("Budget")
class BudgetTest {
private static Organisation newOrganisation() {
Organisation o = new Organisation();
o.setId(UUID.randomUUID());
return o;
}
private static BudgetLine newLine(BigDecimal planned, BigDecimal realized) {
BudgetLine line = new BudgetLine();
line.setAmountPlanned(planned);
line.setAmountRealized(realized);
return line;
}
// -------------------------------------------------------------------------
// getRealizationRate
// -------------------------------------------------------------------------
@Test
@DisplayName("getRealizationRate: totalPlanned == 0 renvoie 0.0")
void getRealizationRate_zeroPlan_returns0() {
Budget b = new Budget();
b.setTotalPlanned(BigDecimal.ZERO);
b.setTotalRealized(new BigDecimal("500.00"));
assertThat(b.getRealizationRate()).isEqualTo(0.0);
}
@Test
@DisplayName("getRealizationRate: 500 réalisé / 1000 prévu = 50%")
void getRealizationRate_withPlan_returnsRatio() {
Budget b = new Budget();
b.setTotalPlanned(new BigDecimal("1000.00"));
b.setTotalRealized(new BigDecimal("500.00"));
assertThat(b.getRealizationRate()).isEqualTo(50.0);
}
// -------------------------------------------------------------------------
// getVariance
// -------------------------------------------------------------------------
@Test
@DisplayName("getVariance: renvoie réalisé - prévu")
void getVariance_returnsRealized_minus_planned() {
Budget b = new Budget();
b.setTotalPlanned(new BigDecimal("1000.00"));
b.setTotalRealized(new BigDecimal("1200.00"));
assertThat(b.getVariance()).isEqualByComparingTo("200.00");
}
// -------------------------------------------------------------------------
// isOverBudget
// -------------------------------------------------------------------------
@Test
@DisplayName("isOverBudget: réalisé > prévu renvoie true")
void isOverBudget_whenOver_returnsTrue() {
Budget b = new Budget();
b.setTotalPlanned(new BigDecimal("1000.00"));
b.setTotalRealized(new BigDecimal("1001.00"));
assertThat(b.isOverBudget()).isTrue();
}
@Test
@DisplayName("isOverBudget: réalisé == prévu renvoie false")
void isOverBudget_whenEqual_returnsFalse() {
Budget b = new Budget();
b.setTotalPlanned(new BigDecimal("1000.00"));
b.setTotalRealized(new BigDecimal("1000.00"));
assertThat(b.isOverBudget()).isFalse();
}
// -------------------------------------------------------------------------
// isActive
// -------------------------------------------------------------------------
@Test
@DisplayName("isActive: statut ACTIVE renvoie true")
void isActive_active_returnsTrue() {
Budget b = new Budget();
b.setStatus("ACTIVE");
assertThat(b.isActive()).isTrue();
}
@Test
@DisplayName("isActive: statut DRAFT renvoie false")
void isActive_draft_returnsFalse() {
Budget b = new Budget();
b.setStatus("DRAFT");
assertThat(b.isActive()).isFalse();
}
// -------------------------------------------------------------------------
// isCurrentPeriod
// -------------------------------------------------------------------------
@Test
@DisplayName("isCurrentPeriod: aujourd'hui dans la période renvoie true")
void isCurrentPeriod_duringPeriod_returnsTrue() {
Budget b = new Budget();
b.setStartDate(LocalDate.now().minusDays(5));
b.setEndDate(LocalDate.now().plusDays(5));
assertThat(b.isCurrentPeriod()).isTrue();
}
@Test
@DisplayName("isCurrentPeriod: période terminée renvoie false")
void isCurrentPeriod_afterPeriod_returnsFalse() {
Budget b = new Budget();
b.setStartDate(LocalDate.now().minusDays(10));
b.setEndDate(LocalDate.now().minusDays(1));
assertThat(b.isCurrentPeriod()).isFalse();
}
@Test
@DisplayName("isCurrentPeriod: période pas encore commencée renvoie false")
void isCurrentPeriod_beforePeriod_returnsFalse() {
Budget b = new Budget();
b.setStartDate(LocalDate.now().plusDays(1));
b.setEndDate(LocalDate.now().plusDays(10));
assertThat(b.isCurrentPeriod()).isFalse();
}
// -------------------------------------------------------------------------
// addLine / removeLine / recalculateTotals
// -------------------------------------------------------------------------
@Test
@DisplayName("addLine: ajoute la ligne, lie le budget parent et recalcule les totaux")
void addLine_addsLineAndRecalculates() {
Budget b = Budget.builder()
.name("Budget Test")
.organisation(newOrganisation())
.period("ANNUAL")
.year(2026)
.status("DRAFT")
.createdById(UUID.randomUUID())
.createdAtBudget(LocalDateTime.now())
.startDate(LocalDate.of(2026, 1, 1))
.endDate(LocalDate.of(2026, 12, 31))
.build();
BudgetLine line = newLine(new BigDecimal("500.00"), new BigDecimal("200.00"));
b.addLine(line);
assertThat(b.getLines()).hasSize(1);
assertThat(line.getBudget()).isSameAs(b);
assertThat(b.getTotalPlanned()).isEqualByComparingTo("500.00");
assertThat(b.getTotalRealized()).isEqualByComparingTo("200.00");
}
@Test
@DisplayName("removeLine: retire la ligne, délie le budget parent et recalcule les totaux")
void removeLine_removesAndRecalculates() {
Budget b = Budget.builder()
.name("Budget Test")
.organisation(newOrganisation())
.period("ANNUAL")
.year(2026)
.status("DRAFT")
.createdById(UUID.randomUUID())
.createdAtBudget(LocalDateTime.now())
.startDate(LocalDate.of(2026, 1, 1))
.endDate(LocalDate.of(2026, 12, 31))
.build();
BudgetLine line1 = newLine(new BigDecimal("300.00"), new BigDecimal("100.00"));
BudgetLine line2 = newLine(new BigDecimal("200.00"), new BigDecimal("50.00"));
b.addLine(line1);
b.addLine(line2);
assertThat(b.getLines()).hasSize(2);
b.removeLine(line1);
assertThat(b.getLines()).hasSize(1);
assertThat(line1.getBudget()).isNull();
assertThat(b.getTotalPlanned()).isEqualByComparingTo("200.00");
assertThat(b.getTotalRealized()).isEqualByComparingTo("50.00");
}
@Test
@DisplayName("recalculateTotals: somme correcte avec plusieurs lignes")
void recalculateTotals_multipleLines_sumsCorrectly() {
Budget b = Budget.builder()
.name("Budget Multi")
.organisation(newOrganisation())
.period("ANNUAL")
.year(2026)
.status("DRAFT")
.createdById(UUID.randomUUID())
.createdAtBudget(LocalDateTime.now())
.startDate(LocalDate.of(2026, 1, 1))
.endDate(LocalDate.of(2026, 12, 31))
.build();
b.addLine(newLine(new BigDecimal("100.00"), new BigDecimal("80.00")));
b.addLine(newLine(new BigDecimal("200.00"), new BigDecimal("150.00")));
b.addLine(newLine(new BigDecimal("300.00"), new BigDecimal("320.00")));
assertThat(b.getTotalPlanned()).isEqualByComparingTo("600.00");
assertThat(b.getTotalRealized()).isEqualByComparingTo("550.00");
}
// -------------------------------------------------------------------------
// onCreate (réflexion)
// -------------------------------------------------------------------------
@Test
@DisplayName("onCreate: initialise les champs null (createdAtBudget, currency, status, totalPlanned, totalRealized)")
void onCreate_initializesNullFields() throws Exception {
Budget b = new Budget();
b.setCreatedAtBudget(null);
b.setCurrency(null);
b.setStatus(null);
b.setTotalPlanned(null);
b.setTotalRealized(null);
Method onCreate = Budget.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(b);
assertThat(b.getCreatedAtBudget()).isNotNull();
assertThat(b.getCurrency()).isEqualTo("XOF");
assertThat(b.getStatus()).isEqualTo("DRAFT");
assertThat(b.getTotalPlanned()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(b.getTotalRealized()).isEqualByComparingTo(BigDecimal.ZERO);
}
@Test
@DisplayName("onCreate: ne remplace pas les champs déjà renseignés")
void onCreate_doesNotOverrideExistingFields() throws Exception {
LocalDateTime existingDate = LocalDateTime.of(2025, 6, 15, 10, 0);
Budget b = new Budget();
b.setCreatedAtBudget(existingDate);
b.setCurrency("EUR");
b.setStatus("ACTIVE");
b.setTotalPlanned(new BigDecimal("5000.00"));
b.setTotalRealized(new BigDecimal("2500.00"));
Method onCreate = Budget.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(b);
assertThat(b.getCreatedAtBudget()).isEqualTo(existingDate);
assertThat(b.getCurrency()).isEqualTo("EUR");
assertThat(b.getStatus()).isEqualTo("ACTIVE");
assertThat(b.getTotalPlanned()).isEqualByComparingTo("5000.00");
assertThat(b.getTotalRealized()).isEqualByComparingTo("2500.00");
}
// -------------------------------------------------------------------------
// builder
// -------------------------------------------------------------------------
@Test
@DisplayName("builder: crée un objet complet avec tous les champs")
void builder_createsComplete() {
UUID createdById = UUID.randomUUID();
UUID approvedById = UUID.randomUUID();
Organisation org = newOrganisation();
LocalDateTime now = LocalDateTime.now();
LocalDateTime approvedAt = now.plusDays(1);
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate end = LocalDate.of(2026, 12, 31);
Budget b = Budget.builder()
.name("Budget Annuel 2026")
.description("Description du budget")
.organisation(org)
.period("ANNUAL")
.year(2026)
.month(null)
.status("ACTIVE")
.totalPlanned(new BigDecimal("1000000.00"))
.totalRealized(new BigDecimal("500000.00"))
.currency("XOF")
.createdById(createdById)
.createdAtBudget(now)
.approvedAt(approvedAt)
.approvedById(approvedById)
.startDate(start)
.endDate(end)
.metadata("{\"note\":\"test\"}")
.build();
assertThat(b.getName()).isEqualTo("Budget Annuel 2026");
assertThat(b.getDescription()).isEqualTo("Description du budget");
assertThat(b.getOrganisation()).isSameAs(org);
assertThat(b.getPeriod()).isEqualTo("ANNUAL");
assertThat(b.getYear()).isEqualTo(2026);
assertThat(b.getMonth()).isNull();
assertThat(b.getStatus()).isEqualTo("ACTIVE");
assertThat(b.getTotalPlanned()).isEqualByComparingTo("1000000.00");
assertThat(b.getTotalRealized()).isEqualByComparingTo("500000.00");
assertThat(b.getCurrency()).isEqualTo("XOF");
assertThat(b.getCreatedById()).isEqualTo(createdById);
assertThat(b.getCreatedAtBudget()).isEqualTo(now);
assertThat(b.getApprovedAt()).isEqualTo(approvedAt);
assertThat(b.getApprovedById()).isEqualTo(approvedById);
assertThat(b.getStartDate()).isEqualTo(start);
assertThat(b.getEndDate()).isEqualTo(end);
assertThat(b.getMetadata()).isEqualTo("{\"note\":\"test\"}");
assertThat(b.getLines()).isNotNull().isEmpty();
}
}

View File

@@ -89,4 +89,38 @@ class CompteWaveTest {
c.setNumeroTelephone("+22507000005");
assertThat(c.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("onCreate: environnement null → SANDBOX")
void onCreate_environnementNull_defaultsSandbox() {
CompteWave c = new CompteWave();
c.setStatutCompte(null);
c.setEnvironnement(null);
c.onCreate();
assertThat(c.getEnvironnement()).isEqualTo("SANDBOX");
assertThat(c.getStatutCompte()).isEqualTo(StatutCompteWave.NON_VERIFIE);
}
@Test
@DisplayName("onCreate: environnement vide → SANDBOX")
void onCreate_environnementEmpty_defaultsSandbox() {
CompteWave c = new CompteWave();
c.setEnvironnement("");
c.onCreate();
assertThat(c.getEnvironnement()).isEqualTo("SANDBOX");
}
@Test
@DisplayName("onCreate: statutCompte et environnement déjà définis → conservés (branches false)")
void onCreate_allDejaDefinis_conservesValues() {
// Valeurs déjà définies → conservées sans modification
CompteWave c = new CompteWave();
c.setStatutCompte(StatutCompteWave.VERIFIE);
c.setEnvironnement("PRODUCTION");
c.onCreate();
assertThat(c.getStatutCompte()).isEqualTo(StatutCompteWave.VERIFIE);
assertThat(c.getEnvironnement()).isEqualTo("PRODUCTION");
}
}

View File

@@ -60,6 +60,43 @@ class ConfigurationWaveTest {
assertThat(c.getEnvironnement()).isEqualTo("COMMON");
}
@Test
@DisplayName("onCreate initialise typeValeur et environnement si vide")
void onCreate_initialiseChamps_empty() throws Exception {
ConfigurationWave c = new ConfigurationWave();
c.setCle("k2");
c.setTypeValeur("");
c.setEnvironnement("");
Method onCreate = ConfigurationWave.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(c);
assertThat(c.getTypeValeur()).isEqualTo("STRING");
assertThat(c.getEnvironnement()).isEqualTo("COMMON");
}
@Test
@DisplayName("onCreate: typeValeur et environnement déjà renseignés → non écrasés")
void onCreate_existingValues_notOverwritten() throws Exception {
ConfigurationWave c = new ConfigurationWave();
c.setCle("k3");
c.setTypeValeur("NUMBER");
c.setEnvironnement("PRODUCTION");
Method onCreate = ConfigurationWave.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(c);
assertThat(c.getTypeValeur()).isEqualTo("NUMBER");
assertThat(c.getEnvironnement()).isEqualTo("PRODUCTION");
}
@Test
@DisplayName("isEncryptee: false si typeValeur null")
void isEncryptee_nullTypeValeur_returnsFalse() {
ConfigurationWave c = new ConfigurationWave();
c.setCle("x");
c.setTypeValeur(null);
assertThat(c.isEncryptee()).isFalse();
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {

View File

@@ -0,0 +1,95 @@
package dev.lions.unionflow.server.entity;
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
@DisplayName("Conversation")
class ConversationTest {
@Test
@DisplayName("getters/setters de base")
void gettersSetters() {
Conversation c = new Conversation();
c.setName("Groupe Test");
c.setDescription("Description groupe");
c.setType(ConversationType.GROUP);
c.setIsMuted(false);
c.setIsPinned(true);
c.setIsArchived(false);
assertThat(c.getName()).isEqualTo("Groupe Test");
assertThat(c.getDescription()).isEqualTo("Description groupe");
assertThat(c.getType()).isEqualTo(ConversationType.GROUP);
assertThat(c.getIsMuted()).isFalse();
assertThat(c.getIsPinned()).isTrue();
assertThat(c.getIsArchived()).isFalse();
}
@Test
@DisplayName("onUpdate (PreUpdate) - met à jour updatedAt via réflexion")
void onUpdate_setsUpdatedAt() throws Exception {
Conversation c = new Conversation();
assertThat(c.getUpdatedAt()).isNull();
Method onUpdate = Conversation.class.getDeclaredMethod("onUpdate");
onUpdate.setAccessible(true);
LocalDateTime before = LocalDateTime.now().minusSeconds(1);
onUpdate.invoke(c);
LocalDateTime after = LocalDateTime.now().plusSeconds(1);
assertThat(c.getUpdatedAt()).isNotNull();
assertThat(c.getUpdatedAt()).isAfter(before);
assertThat(c.getUpdatedAt()).isBefore(after);
}
@Test
@DisplayName("onUpdate appelé deux fois met à jour updatedAt à chaque fois")
void onUpdate_calledTwice_updatesEachTime() throws Exception {
Conversation c = new Conversation();
Method onUpdate = Conversation.class.getDeclaredMethod("onUpdate");
onUpdate.setAccessible(true);
onUpdate.invoke(c);
LocalDateTime first = c.getUpdatedAt();
// petit délai pour différencier les timestamps
Thread.sleep(5);
onUpdate.invoke(c);
LocalDateTime second = c.getUpdatedAt();
assertThat(second).isAfterOrEqualTo(first);
}
@Test
@DisplayName("participants initialisé à liste vide")
void participants_initializedEmpty() {
Conversation c = new Conversation();
assertThat(c.getParticipants()).isNotNull().isEmpty();
}
@Test
@DisplayName("messages initialisé à liste vide")
void messages_initializedEmpty() {
Conversation c = new Conversation();
assertThat(c.getMessages()).isNotNull().isEmpty();
}
@Test
@DisplayName("isMuted et isPinned et isArchived défaut false")
void defaultFlags_areFalse() {
Conversation c = new Conversation();
assertThat(c.getIsMuted()).isFalse();
assertThat(c.getIsPinned()).isFalse();
assertThat(c.getIsArchived()).isFalse();
}
}

View File

@@ -142,4 +142,79 @@ class CotisationTest {
c.setAnnee(2025);
assertThat(c.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("getMontantRestant: ZERO si montantDu null")
void getMontantRestant_montantDuNull_returnsZero() {
Cotisation c = new Cotisation();
c.setMontantPaye(new BigDecimal("50.00"));
// montantDu = null
assertThat(c.getMontantRestant()).isEqualByComparingTo(BigDecimal.ZERO);
}
@Test
@DisplayName("isEnRetard: false si dateEcheance null")
void isEnRetard_echeanceNull_returnsFalse() {
Cotisation c = new Cotisation();
c.setMontantDu(new BigDecimal("100.00"));
c.setMontantPaye(BigDecimal.ZERO);
// dateEcheance = null
assertThat(c.isEnRetard()).isFalse();
}
@Test
@DisplayName("onCreate: numeroReference déjà défini → conservé (branche false)")
void onCreate_numeroReferenceDejaDefini_conserve() {
// numeroReference déjà défini → non écrasé par onCreate
Cotisation c = new Cotisation();
c.setNumeroReference("COT-EXISTANT-001");
c.setCodeDevise("XOF");
c.setStatut("EN_ATTENTE");
c.setMontantPaye(BigDecimal.ZERO);
c.setNombreRappels(0);
c.setRecurrente(false);
c.onCreate();
// numeroReference doit rester inchangé
assertThat(c.getNumeroReference()).isEqualTo("COT-EXISTANT-001");
}
@Test
@DisplayName("onCreate: numeroReference vide (empty string) → généré (branche isEmpty)")
void onCreate_emptyNumeroReference_generated() throws Exception {
Cotisation c = new Cotisation();
c.setNumeroReference(""); // non null mais vide → isEmpty() est true → doit générer
c.setCodeDevise("XOF");
c.setStatut("EN_ATTENTE");
c.setMontantPaye(BigDecimal.ZERO);
c.setNombreRappels(0);
c.setRecurrente(false);
java.lang.reflect.Method onCreate = Cotisation.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(c);
assertThat(c.getNumeroReference()).isNotEmpty().startsWith("COT-");
}
@Test
@DisplayName("onCreate: initialise les défauts si null")
void onCreate_setsDefaults() {
Cotisation c = new Cotisation();
// Force null pour couvrir toutes les branches de initialisation
c.setNumeroReference(null);
c.setCodeDevise(null);
c.setStatut(null);
c.setMontantPaye(null);
c.setNombreRappels(null);
c.setRecurrente(null);
c.onCreate();
assertThat(c.getNumeroReference()).isNotNull().startsWith("COT-");
assertThat(c.getCodeDevise()).isEqualTo("XOF");
assertThat(c.getStatut()).isEqualTo("EN_ATTENTE");
assertThat(c.getMontantPaye()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(c.getNombreRappels()).isEqualTo(0);
assertThat(c.getRecurrente()).isFalse();
}
}

View File

@@ -0,0 +1,62 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests de couverture complémentaires pour DemandeAdhesion.
* Couvre les branches manquantes de isPayeeIntegralement().
*/
@DisplayName("DemandeAdhesion - couverture complémentaire")
class DemandeAdhesionCoverageTest {
@Test
@DisplayName("isPayeeIntegralement retourne false quand montantPaye est null")
void isPayeeIntegralement_nullMontantPaye_returnsFalse() {
DemandeAdhesion d = new DemandeAdhesion();
d.setFraisAdhesion(new BigDecimal("5000.00"));
d.setMontantPaye(null);
assertThat(d.isPayeeIntegralement()).isFalse();
}
@Test
@DisplayName("isPayeeIntegralement retourne true quand montantPaye > fraisAdhesion (overpayment)")
void isPayeeIntegralement_overpayment_returnsTrue() {
DemandeAdhesion d = new DemandeAdhesion();
d.setFraisAdhesion(new BigDecimal("5000.00"));
d.setMontantPaye(new BigDecimal("6000.00"));
assertThat(d.isPayeeIntegralement()).isTrue();
}
@Test
@DisplayName("isPayeeIntegralement retourne true quand fraisAdhesion est zero et montantPaye est zero")
void isPayeeIntegralement_zeroFraisZeroMontant_returnsTrue() {
DemandeAdhesion d = new DemandeAdhesion();
d.setFraisAdhesion(BigDecimal.ZERO);
d.setMontantPaye(BigDecimal.ZERO);
assertThat(d.isPayeeIntegralement()).isTrue();
}
@Test
@DisplayName("isPayeeIntegralement retourne false quand les deux champs sont null")
void isPayeeIntegralement_bothNull_returnsFalse() {
DemandeAdhesion d = new DemandeAdhesion();
d.setFraisAdhesion(null);
d.setMontantPaye(null);
assertThat(d.isPayeeIntegralement()).isFalse();
}
@Test
@DisplayName("isEnAttente retourne false pour statut ANNULEE")
void isEnAttente_annulee_returnsFalse() {
DemandeAdhesion d = new DemandeAdhesion();
d.setStatut("ANNULEE");
assertThat(d.isEnAttente()).isFalse();
assertThat(d.isApprouvee()).isFalse();
assertThat(d.isRejetee()).isFalse();
}
}

View File

@@ -91,4 +91,124 @@ class DemandeAdhesionTest {
d.setOrganisation(newOrganisation());
assertThat(d.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("isApprouvee retourne true pour statut APPROUVEE")
void isApprouvee_approuvee_returnsTrue() {
DemandeAdhesion d = new DemandeAdhesion();
d.setStatut("APPROUVEE");
assertThat(d.isApprouvee()).isTrue();
assertThat(d.isRejetee()).isFalse();
}
@Test
@DisplayName("isRejetee retourne true pour statut REJETEE")
void isRejetee_rejetee_returnsTrue() {
DemandeAdhesion d = new DemandeAdhesion();
d.setStatut("REJETEE");
assertThat(d.isRejetee()).isTrue();
assertThat(d.isApprouvee()).isFalse();
}
@Test
@DisplayName("genererNumeroReference retourne un code non vide")
void genererNumeroReference_returnsNonEmpty() {
String ref = DemandeAdhesion.genererNumeroReference();
assertThat(ref).isNotNull().isNotEmpty().startsWith("ADH-");
}
@Test
@DisplayName("onCreate: initialise tous les défauts si null")
void onCreate_setsDefaults() {
DemandeAdhesion d = new DemandeAdhesion();
// Les champs @Builder.Default sont déjà initialisés — les forcer à null pour tester les branches true
d.setDateDemande(null);
d.setStatut(null);
d.setCodeDevise(null);
d.setFraisAdhesion(null);
d.setMontantPaye(null);
// numeroReference est null par défaut (pas de @Builder.Default)
d.onCreate();
assertThat(d.getDateDemande()).isNotNull();
assertThat(d.getStatut()).isEqualTo("EN_ATTENTE");
assertThat(d.getCodeDevise()).isEqualTo("XOF");
assertThat(d.getFraisAdhesion()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(d.getMontantPaye()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(d.getNumeroReference()).isNotNull().startsWith("ADH-");
}
@Test
@DisplayName("onCreate: ne modifie pas les champs déjà initialisés")
void onCreate_preservesExistingValues() {
DemandeAdhesion d = new DemandeAdhesion();
LocalDateTime specificDate = LocalDateTime.of(2024, 1, 15, 10, 30);
d.setDateDemande(specificDate);
d.setStatut("APPROUVEE");
d.setCodeDevise("EUR");
d.setFraisAdhesion(BigDecimal.valueOf(100));
d.setMontantPaye(BigDecimal.valueOf(50));
d.setNumeroReference("ADH-CUSTOM-001");
d.onCreate();
assertThat(d.getDateDemande()).isEqualTo(specificDate);
assertThat(d.getStatut()).isEqualTo("APPROUVEE");
assertThat(d.getCodeDevise()).isEqualTo("EUR");
assertThat(d.getFraisAdhesion()).isEqualByComparingTo(BigDecimal.valueOf(100));
assertThat(d.getMontantPaye()).isEqualByComparingTo(BigDecimal.valueOf(50));
assertThat(d.getNumeroReference()).isEqualTo("ADH-CUSTOM-001");
}
@Test
@DisplayName("onCreate: génère une référence si numeroReference est vide")
void onCreate_emptyReference_generatesNew() {
DemandeAdhesion d = new DemandeAdhesion();
d.setNumeroReference(""); // vide → condition isEmpty() == true
d.setStatut("EN_ATTENTE");
d.setCodeDevise("XOF");
d.setFraisAdhesion(BigDecimal.ZERO);
d.setMontantPaye(BigDecimal.ZERO);
d.setDateDemande(LocalDateTime.now());
d.onCreate();
assertThat(d.getNumeroReference()).isNotEmpty().startsWith("ADH-");
}
@Test
@DisplayName("isEnAttente retourne true pour statut EN_ATTENTE")
void isEnAttente_retournsTrue() {
DemandeAdhesion d = new DemandeAdhesion();
d.setStatut("EN_ATTENTE");
assertThat(d.isEnAttente()).isTrue();
assertThat(d.isApprouvee()).isFalse();
assertThat(d.isRejetee()).isFalse();
}
@Test
@DisplayName("isPayeeIntegralement retourne true quand montantPaye >= fraisAdhesion")
void isPayeeIntegralement_paymentComplete_returnsTrue() {
DemandeAdhesion d = new DemandeAdhesion();
d.setFraisAdhesion(new BigDecimal("5000.00"));
d.setMontantPaye(new BigDecimal("5000.00"));
assertThat(d.isPayeeIntegralement()).isTrue();
}
@Test
@DisplayName("isPayeeIntegralement retourne false quand montantPaye < fraisAdhesion")
void isPayeeIntegralement_paymentIncomplete_returnsFalse() {
DemandeAdhesion d = new DemandeAdhesion();
d.setFraisAdhesion(new BigDecimal("5000.00"));
d.setMontantPaye(new BigDecimal("2500.00"));
assertThat(d.isPayeeIntegralement()).isFalse();
}
@Test
@DisplayName("isPayeeIntegralement retourne false quand fraisAdhesion est null")
void isPayeeIntegralement_nullFrais_returnsFalse() {
DemandeAdhesion d = new DemandeAdhesion();
d.setFraisAdhesion(null);
d.setMontantPaye(BigDecimal.ZERO);
assertThat(d.isPayeeIntegralement()).isFalse();
}
}

View File

@@ -60,6 +60,14 @@ class DocumentTest {
assertThat(d.verifierIntegriteSha256("autre")).isFalse();
}
@Test
@DisplayName("verifierIntegriteSha256: false si hashSha256 null")
void verifierIntegriteSha256_hashNull_returnsFalse() {
Document d = new Document();
d.setHashSha256(null);
assertThat(d.verifierIntegriteSha256("def456")).isFalse();
}
@Test
@DisplayName("getTailleFormatee: B, KB, MB")
void getTailleFormatee() {

View File

@@ -4,10 +4,13 @@ import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("EcritureComptable")
@@ -122,4 +125,228 @@ class EcritureComptableTest {
e.setJournal(newJournal());
assertThat(e.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("onUpdate (PreUpdate) calcule les totaux via réflexion")
void onUpdate_calculesTotaux() throws Exception {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("X");
e.setDateEcriture(LocalDate.now());
e.setLibelle("L");
e.setJournal(newJournal());
e.setMontantDebit(BigDecimal.ZERO);
e.setMontantCredit(BigDecimal.ZERO);
LigneEcriture l1 = new LigneEcriture();
l1.setMontantDebit(new BigDecimal("200"));
l1.setMontantCredit(BigDecimal.ZERO);
LigneEcriture l2 = new LigneEcriture();
l2.setMontantDebit(BigDecimal.ZERO);
l2.setMontantCredit(new BigDecimal("200"));
e.getLignes().add(l1);
e.getLignes().add(l2);
Method onUpdate = EcritureComptable.class.getDeclaredMethod("onUpdate");
onUpdate.setAccessible(true);
onUpdate.invoke(e);
assertThat(e.getMontantDebit()).isEqualByComparingTo("200");
assertThat(e.getMontantCredit()).isEqualByComparingTo("200");
}
@Test
@DisplayName("onCreate: initialise les défauts si null")
void onCreate_setsDefaults() throws Exception {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece(null);
e.setDateEcriture(null);
e.setMontantDebit(null);
e.setMontantCredit(null);
e.setPointe(null);
Method onCreate = EcritureComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(e);
assertThat(e.getNumeroPiece()).isNotNull().startsWith("ECR-");
assertThat(e.getDateEcriture()).isEqualTo(LocalDate.now());
assertThat(e.getMontantDebit()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(e.getMontantCredit()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(e.getPointe()).isFalse();
}
@Test
@DisplayName("onCreate: numeroPiece vide → généré")
void onCreate_numeroPieceEmpty_generated() throws Exception {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("");
e.setDateEcriture(LocalDate.of(2025, 6, 1));
Method onCreate = EcritureComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(e);
assertThat(e.getNumeroPiece()).isNotEmpty().startsWith("ECR-20250601-");
}
@Test
@DisplayName("onCreate: avec lignes → calculerTotaux appelé")
void onCreate_withLignes_calculesTotaux() throws Exception {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("X");
e.setDateEcriture(LocalDate.now());
LigneEcriture l1 = new LigneEcriture();
l1.setMontantDebit(new BigDecimal("300"));
l1.setMontantCredit(BigDecimal.ZERO);
LigneEcriture l2 = new LigneEcriture();
l2.setMontantDebit(BigDecimal.ZERO);
l2.setMontantCredit(new BigDecimal("300"));
e.getLignes().add(l1);
e.getLignes().add(l2);
Method onCreate = EcritureComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(e);
assertThat(e.getMontantDebit()).isEqualByComparingTo("300");
assertThat(e.getMontantCredit()).isEqualByComparingTo("300");
}
@Test
@DisplayName("isEquilibree: false si montantCredit null (branche ||)")
void isEquilibree_montantCreditNull_returnsFalse() {
// montantDebit non null mais montantCredit null → retourne false
EcritureComptable e = new EcritureComptable();
e.setJournal(newJournal());
e.setNumeroPiece("X");
e.setDateEcriture(LocalDate.now());
e.setLibelle("L");
e.setMontantDebit(new BigDecimal("100.00"));
e.setMontantCredit(null);
assertThat(e.isEquilibree()).isFalse();
}
@Test
@DisplayName("calculerTotaux: lignes vides → totaux à ZERO")
void calculerTotaux_emptyLignes_setsZero() {
// Couvre la branche : `if (lignes == null || lignes.isEmpty()) { return ZERO; }`
EcritureComptable e = new EcritureComptable();
e.setJournal(newJournal());
e.setNumeroPiece("X");
e.setDateEcriture(LocalDate.now());
e.setLibelle("L");
e.setMontantDebit(new BigDecimal("500.00"));
e.setMontantCredit(new BigDecimal("500.00"));
// lignes est vide (défaut)
e.calculerTotaux();
assertThat(e.getMontantDebit()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(e.getMontantCredit()).isEqualByComparingTo(BigDecimal.ZERO);
}
@Test
@DisplayName("onCreate: lignes non vides présentes → calculerTotaux() appelé (branche true)")
void onCreate_lignesNonVides_calculeTotaux() throws Exception {
// Couvre la branche `if (lignes != null && !lignes.isEmpty())` → true dans onCreate
// Ce test est complémentaire à onCreate_withLignes_calculesTotaux pour s'assurer
// que le chemin est bien couvert.
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("PIECE-001");
e.setDateEcriture(LocalDate.now());
e.setLibelle("Test");
LigneEcriture l1 = new LigneEcriture();
l1.setMontantDebit(new BigDecimal("75"));
l1.setMontantCredit(BigDecimal.ZERO);
LigneEcriture l2 = new LigneEcriture();
l2.setMontantDebit(BigDecimal.ZERO);
l2.setMontantCredit(new BigDecimal("75"));
e.getLignes().add(l1);
e.getLignes().add(l2);
Method onCreate = EcritureComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(e);
assertThat(e.getMontantDebit()).isEqualByComparingTo("75");
assertThat(e.getMontantCredit()).isEqualByComparingTo("75");
// numeroPiece déjà défini → conservé (branche false du if numeroPiece)
assertThat(e.getNumeroPiece()).isEqualTo("PIECE-001");
}
@Test
@DisplayName("calculerTotaux: lignes == null → totaux à ZERO (branche lignes null)")
void calculerTotaux_nullLignes_setsZero() throws Exception {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("X");
e.setDateEcriture(LocalDate.now());
e.setLibelle("L");
e.setMontantDebit(new BigDecimal("999"));
e.setMontantCredit(new BigDecimal("999"));
// Forcer lignes à null via réflexion
java.lang.reflect.Field lignesField = EcritureComptable.class.getDeclaredField("lignes");
lignesField.setAccessible(true);
lignesField.set(e, null);
e.calculerTotaux();
assertThat(e.getMontantDebit()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(e.getMontantCredit()).isEqualByComparingTo(BigDecimal.ZERO);
}
@Test
@DisplayName("onCreate: lignes != null et isEmpty → calculerTotaux NON appelé (branche false)")
void onCreate_lignesEmptyList_calculerTotauxNotCalled() throws Exception {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("X");
e.setDateEcriture(LocalDate.now());
e.setLibelle("L");
e.setMontantDebit(new BigDecimal("10"));
e.setMontantCredit(new BigDecimal("10"));
// lignes est une ArrayList vide (valeur par défaut) → lignes != null && !lignes.isEmpty() → false
assertThat(e.getLignes()).isEmpty();
Method onCreate = EcritureComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(e);
// calculerTotaux n'a pas été appelé → montants restent (ou reset à 0 par init)
// but montantDebit/Credit were already set, and they won't be recalculated
// Just verify no exception and onCreate ran
assertThat(e.getNumeroPiece()).isEqualTo("X");
}
@Test
@DisplayName("onCreate: numeroPiece vide ('') → génère un nouveau numeroPiece (branche isEmpty() true)")
void onCreate_numeroPieceEmpty_generatesPiece() throws Exception {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece(""); // non-null mais vide → isEmpty() true → génère
e.setDateEcriture(LocalDate.now()); // non-null → ternaire true
e.setLibelle("L");
e.setMontantDebit(new BigDecimal("10"));
e.setMontantCredit(new BigDecimal("10"));
Method onCreate = EcritureComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(e);
// numeroPiece était vide → a été généré
assertThat(e.getNumeroPiece()).isNotEmpty();
assertThat(e.getNumeroPiece()).startsWith("ECR");
}
@Test
@DisplayName("calculerTotaux: filtre les montants null (branch false des lambdas filter)")
void calculerTotaux_withNullAmounts() {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("X");
e.setDateEcriture(LocalDate.now());
e.setLibelle("L");
// Ligne avec montants null → filtrée (couvre branch false: amount != null → false)
LigneEcriture l1 = new LigneEcriture();
l1.setMontantDebit(null);
l1.setMontantCredit(null);
LigneEcriture l2 = new LigneEcriture();
l2.setMontantDebit(new BigDecimal("50"));
l2.setMontantCredit(new BigDecimal("50"));
e.getLignes().add(l1);
e.getLignes().add(l2);
e.calculerTotaux();
assertThat(e.getMontantDebit()).isEqualByComparingTo("50");
assertThat(e.getMontantCredit()).isEqualByComparingTo("50");
}
}

View File

@@ -0,0 +1,622 @@
package dev.lions.unionflow.server.entity;
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import dev.lions.unionflow.server.api.enums.wave.StatutWebhook;
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests de couverture pour les branches manquées dans les entités.
* Cible les branches des @PrePersist/@PreUpdate et des méthodes métier
* non couvertes par les tests unitaires existants.
*/
@DisplayName("EntityCoverageTest — branches @PrePersist et méthodes métier")
class EntityCoverageTest {
// ─── Membre ──────────────────────────────────────────────────────────────
@Nested
@DisplayName("Membre — branches manquées")
class MembreCoverage {
@Test
@DisplayName("onCreate() avec statutCompte=null l'initialise à EN_ATTENTE_VALIDATION")
void onCreate_nullStatutCompte_setsDefault() {
// On ne peut pas appeler directement @PrePersist hors container JPA,
// mais on peut tester la branche via reflection ou en vérifiant la valeur Builder.Default.
// Alternative : vérifier que le Builder.Default couvre le null au niveau entité.
Membre m = new Membre();
m.setNumeroMembre("X");
m.setPrenom("A");
m.setNom("B");
m.setEmail("a@test.com");
m.setDateNaissance(LocalDate.now());
// La valeur par défaut via Builder.Default est "EN_ATTENTE_VALIDATION"
// mais l'instanciation via new Membre() ne déclenche pas le @Builder.Default.
// On couvre la branche en appelant le @PrePersist directement via under-test:
m.setStatutCompte(null);
// Invoke the protected onCreate via direct call (simulated)
m.onCreate();
assertThat(m.getStatutCompte()).isEqualTo("EN_ATTENTE_VALIDATION");
}
@Test
@DisplayName("getAge() avec dateNaissance=null retourne 0")
void getAge_nullDateNaissance_returnsZero() {
Membre m = new Membre();
m.setDateNaissance(null);
assertThat(m.getAge()).isEqualTo(0);
}
@Test
@DisplayName("isMajeur() avec dateNaissance=null retourne false")
void isMajeur_nullDateNaissance_returnsFalse() {
Membre m = new Membre();
m.setDateNaissance(null);
assertThat(m.isMajeur()).isFalse();
}
}
// ─── MembreOrganisation ───────────────────────────────────────────────────
@Nested
@DisplayName("MembreOrganisation — branches manquées")
class MembreOrganisationCoverage {
@Test
@DisplayName("onCreate() avec statutMembre=null l'initialise à EN_ATTENTE_VALIDATION")
void onCreate_nullStatutMembre_setsDefault() {
MembreOrganisation mo = new MembreOrganisation();
mo.setStatutMembre(null);
mo.onCreate();
assertThat(mo.getStatutMembre()).isEqualTo(StatutMembre.EN_ATTENTE_VALIDATION);
}
}
// ─── ModuleOrganisationActif ──────────────────────────────────────────────
@Nested
@DisplayName("ModuleOrganisationActif — isActif() non couvert")
class ModuleOrganisationActifCoverage {
@Test
@DisplayName("isActif() retourne true quand actif=true")
void isActif_true() {
ModuleOrganisationActif m = new ModuleOrganisationActif();
m.setActif(true);
m.setModuleCode("TEST");
assertThat(m.isActif()).isTrue();
}
@Test
@DisplayName("isActif() retourne false quand actif=false")
void isActif_false() {
ModuleOrganisationActif m = new ModuleOrganisationActif();
m.setActif(false);
m.setModuleCode("TEST");
assertThat(m.isActif()).isFalse();
}
@Test
@DisplayName("isActif() retourne false quand actif=null")
void isActif_null() {
ModuleOrganisationActif m = new ModuleOrganisationActif();
m.setActif(null);
assertThat(m.isActif()).isFalse();
}
}
// ─── InscriptionEvenement — preUpdate ─────────────────────────────────────
@Nested
@DisplayName("InscriptionEvenement — preUpdate() non couvert")
class InscriptionEvenementCoverage {
@Test
@DisplayName("preUpdate() appelle super.onUpdate() sans exception")
void preUpdate_doesNotThrow() {
InscriptionEvenement ie = new InscriptionEvenement();
ie.setStatut("CONFIRMEE");
ie.preUpdate();
// La date de modification est setDateModification — on vérifie juste que ça ne plante pas
assertThat(ie.getDateModification()).isNotNull();
}
}
// ─── Conversation — onUpdate ─────────────────────────────────────────────
@Nested
@DisplayName("Conversation — onUpdate() non couvert")
class ConversationCoverage {
@Test
@DisplayName("Conversation getters/setters de base")
void gettersSetters() {
Conversation c = new Conversation();
c.setName("Chat Test");
c.setDescription("Description");
c.setType(ConversationType.GROUP);
c.setIsMuted(false);
c.setIsPinned(true);
c.setIsArchived(false);
c.setUpdatedAt(LocalDateTime.now());
assertThat(c.getName()).isEqualTo("Chat Test");
assertThat(c.getType()).isEqualTo(ConversationType.GROUP);
assertThat(c.getIsPinned()).isTrue();
}
@Test
@DisplayName("onUpdate() met à jour updatedAt")
void onUpdate_setsUpdatedAt() {
Conversation c = new Conversation();
c.setName("Chat");
c.setType(ConversationType.INDIVIDUAL);
assertThat(c.getUpdatedAt()).isNull();
c.onUpdate();
assertThat(c.getUpdatedAt()).isNotNull();
}
}
// ─── EcritureComptable — onUpdate ────────────────────────────────────────
@Nested
@DisplayName("EcritureComptable — onUpdate() non couvert")
class EcritureComptableCoverage {
@Test
@DisplayName("onUpdate() appelle calculerTotaux")
void onUpdate_calculerTotaux() {
EcritureComptable e = new EcritureComptable();
e.setNumeroPiece("ECR-UPD-001");
e.setDateEcriture(java.time.LocalDate.now());
e.setLibelle("Test update");
e.setMontantDebit(java.math.BigDecimal.ZERO);
e.setMontantCredit(java.math.BigDecimal.ZERO);
// onUpdate calls calculerTotaux; with empty lignes, totals reset to ZERO
e.onUpdate();
assertThat(e.getMontantDebit()).isEqualByComparingTo(java.math.BigDecimal.ZERO);
assertThat(e.getMontantCredit()).isEqualByComparingTo(java.math.BigDecimal.ZERO);
}
}
// ─── ValidationEtapeDemande — onCreate ───────────────────────────────────
@Nested
@DisplayName("ValidationEtapeDemande — onCreate() non couvert")
class ValidationEtapeDemandeCoverage {
@Test
@DisplayName("onCreate() avec statut=null initialise à EN_ATTENTE")
void onCreate_nullStatut_setsDefault() {
dev.lions.unionflow.server.api.enums.solidarite.StatutValidationEtape statut =
dev.lions.unionflow.server.api.enums.solidarite.StatutValidationEtape.EN_ATTENTE;
ValidationEtapeDemande ved = new ValidationEtapeDemande();
ved.setStatut(null);
ved.onCreate();
assertThat(ved.getStatut()).isEqualTo(statut);
}
@Test
@DisplayName("onCreate() avec statut déjà défini le préserve")
void onCreate_existingStatut_preserves() {
dev.lions.unionflow.server.api.enums.solidarite.StatutValidationEtape statut =
dev.lions.unionflow.server.api.enums.solidarite.StatutValidationEtape.APPROUVEE;
ValidationEtapeDemande ved = new ValidationEtapeDemande();
ved.setStatut(statut);
ved.onCreate();
assertThat(ved.getStatut()).isEqualTo(statut);
}
}
// ─── SystemAlert — branches @PrePersist ──────────────────────────────────
@Nested
@DisplayName("SystemAlert — branches @PrePersist non couvertes")
class SystemAlertCoverage {
@Test
@DisplayName("onCreate() avec timestamp=null l'initialise")
void onCreate_nullTimestamp_setsNow() {
SystemAlert a = new SystemAlert();
a.setLevel("WARNING");
a.setTitle("Test");
a.setMessage("Msg");
a.setTimestamp(null);
a.setAcknowledged(null);
a.onCreate();
assertThat(a.getTimestamp()).isNotNull();
assertThat(a.getAcknowledged()).isFalse();
}
@Test
@DisplayName("onCreate() avec timestamp et acknowledged déjà définis ne les écrase pas")
void onCreate_withExistingValues_preserves() {
LocalDateTime ts = LocalDateTime.of(2026, 1, 1, 0, 0);
SystemAlert a = new SystemAlert();
a.setTimestamp(ts);
a.setAcknowledged(true);
a.onCreate();
assertThat(a.getTimestamp()).isEqualTo(ts);
assertThat(a.getAcknowledged()).isTrue();
}
}
// ─── SystemLog — branche @PrePersist ─────────────────────────────────────
@Nested
@DisplayName("SystemLog — branche @PrePersist non couverte")
class SystemLogCoverage {
@Test
@DisplayName("onCreate() avec timestamp=null l'initialise")
void onCreate_nullTimestamp_setsNow() {
SystemLog sl = new SystemLog();
sl.setLevel("ERROR");
sl.setSource("TEST");
sl.setMessage("Message test");
sl.setTimestamp(null);
sl.onCreate();
assertThat(sl.getTimestamp()).isNotNull();
}
@Test
@DisplayName("Getters/setters de SystemLog")
void gettersSetters() {
SystemLog sl = new SystemLog();
sl.setLevel("INFO");
sl.setSource("Database");
sl.setMessage("DB query");
sl.setDetails("stack trace here");
sl.setTimestamp(LocalDateTime.now());
sl.setUserId("user-123");
sl.setSessionId("sess-abc");
sl.setEndpoint("/api/test");
sl.setHttpStatusCode(200);
assertThat(sl.getLevel()).isEqualTo("INFO");
assertThat(sl.getSource()).isEqualTo("Database");
assertThat(sl.getEndpoint()).isEqualTo("/api/test");
assertThat(sl.getHttpStatusCode()).isEqualTo(200);
}
}
// ─── WebhookWave — branches @PrePersist ──────────────────────────────────
@Nested
@DisplayName("WebhookWave — branches @PrePersist non couvertes")
class WebhookWaveCoverage {
@Test
@DisplayName("onCreate() avec statutTraitement=null l'initialise à EN_ATTENTE")
void onCreate_nullStatut_setsDefault() {
WebhookWave w = new WebhookWave();
w.setWaveEventId("evt-" + UUID.randomUUID());
w.setStatutTraitement(null);
w.setNombreTentatives(null);
w.setDateReception(null);
w.onCreate();
assertThat(w.getStatutTraitement()).isEqualTo(StatutWebhook.EN_ATTENTE.name());
assertThat(w.getNombreTentatives()).isEqualTo(0);
assertThat(w.getDateReception()).isNotNull();
}
@Test
@DisplayName("onCreate() ne surécrit pas des valeurs déjà définies")
void onCreate_withExistingValues_preserves() {
WebhookWave w = new WebhookWave();
w.setWaveEventId("evt-" + UUID.randomUUID());
w.setStatutTraitement(StatutWebhook.TRAITE.name());
w.setNombreTentatives(3);
LocalDateTime reception = LocalDateTime.of(2026, 1, 1, 12, 0);
w.setDateReception(reception);
w.onCreate();
assertThat(w.getStatutTraitement()).isEqualTo(StatutWebhook.TRAITE.name());
assertThat(w.getNombreTentatives()).isEqualTo(3);
assertThat(w.getDateReception()).isEqualTo(reception);
}
}
// ─── Paiement — branches @PrePersist ─────────────────────────────────────
@Nested
@DisplayName("Paiement — branches @PrePersist non couvertes")
class PaiementCoverage {
private Membre newMembre() {
Membre m = new Membre();
m.setId(UUID.randomUUID());
m.setNumeroMembre("MX");
m.setPrenom("X");
m.setNom("Y");
m.setEmail("xy@test.com");
m.setDateNaissance(LocalDate.now());
return m;
}
@Test
@DisplayName("onCreate() avec numeroReference=null génère un numéro")
void onCreate_nullNumeroReference_generates() {
Paiement p = new Paiement();
p.setNumeroReference(null);
p.setStatutPaiement(null);
p.setMontant(BigDecimal.ONE);
p.setCodeDevise("XOF");
p.setMethodePaiement("WAVE");
p.setMembre(newMembre());
p.onCreate();
assertThat(p.getNumeroReference()).startsWith("PAY-");
assertThat(p.getStatutPaiement()).isEqualTo("EN_ATTENTE");
assertThat(p.getDatePaiement()).isNotNull();
}
@Test
@DisplayName("onCreate() avec numeroReference vide génère un numéro")
void onCreate_emptyNumeroReference_generates() {
Paiement p = new Paiement();
p.setNumeroReference("");
p.setStatutPaiement("VALIDE");
p.setMontant(BigDecimal.ONE);
p.setCodeDevise("XOF");
p.setMethodePaiement("WAVE");
p.setMembre(newMembre());
p.setDatePaiement(LocalDateTime.now()); // already set
p.onCreate();
assertThat(p.getNumeroReference()).startsWith("PAY-");
}
@Test
@DisplayName("peutEtreModifie() retourne false pour ANNULE")
void peutEtreModifie_annule_returnsFalse() {
Paiement p = new Paiement();
p.setNumeroReference("X");
p.setMontant(BigDecimal.ONE);
p.setCodeDevise("XOF");
p.setMethodePaiement("WAVE");
p.setMembre(newMembre());
p.setStatutPaiement("ANNULE");
assertThat(p.peutEtreModifie()).isFalse();
}
}
// ─── DemandeAide — branches @PrePersist ──────────────────────────────────
@Nested
@DisplayName("DemandeAide — branches @PrePersist non couvertes")
class DemandeAideCoverage {
@Test
@DisplayName("onCreate() avec dateDemande/statut/urgence=null initialise les defaults")
void onCreate_nullFields_setsDefaults() {
DemandeAide d = new DemandeAide();
d.setDateDemande(null);
d.setStatut(null);
d.setUrgence(null);
d.onCreate();
assertThat(d.getDateDemande()).isNotNull();
assertThat(d.getStatut()).isEqualTo(StatutAide.EN_ATTENTE);
assertThat(d.getUrgence()).isFalse();
}
}
// ─── LigneEcriture — branches @PrePersist + getMontant ───────────────────
@Nested
@DisplayName("LigneEcriture — branches non couvertes")
class LigneEcritureCoverage {
@Test
@DisplayName("onCreate() avec montantDebit/montantCredit=null les initialise à ZERO")
void onCreate_nullMontants_setsZero() {
LigneEcriture le = new LigneEcriture();
le.setMontantDebit(null);
le.setMontantCredit(null);
le.onCreate();
assertThat(le.getMontantDebit()).isEqualByComparingTo(BigDecimal.ZERO);
assertThat(le.getMontantCredit()).isEqualByComparingTo(BigDecimal.ZERO);
}
@Test
@DisplayName("getMontant() retourne ZERO quand les deux montants sont null/zero")
void getMontant_noDebitNoCredit_returnsZero() {
LigneEcriture le = new LigneEcriture();
le.setMontantDebit(null);
le.setMontantCredit(null);
assertThat(le.getMontant()).isEqualByComparingTo(BigDecimal.ZERO);
}
}
// ─── MembreRole — branches isActif ────────────────────────────────────────
@Nested
@DisplayName("MembreRole — branches isActif() et @PrePersist non couverts")
class MembreRoleCoverage {
private MembreRole newMembreRole() {
MembreRole mr = new MembreRole();
mr.setActif(true);
return mr;
}
@Test
@DisplayName("isActif() retourne false si actif=false")
void isActif_inactif_returnsFalse() {
MembreRole mr = newMembreRole();
mr.setActif(false);
assertThat(mr.isActif()).isFalse();
}
@Test
@DisplayName("isActif() retourne false si dateFin est dépassée")
void isActif_dateFinPassed_returnsFalse() {
MembreRole mr = newMembreRole();
mr.setDateDebut(LocalDate.now().minusDays(10));
mr.setDateFin(LocalDate.now().minusDays(1));
assertThat(mr.isActif()).isFalse();
}
@Test
@DisplayName("onCreate() avec dateDebut=null l'initialise à aujourd'hui")
void onCreate_nullDateDebut_setsToday() {
MembreRole mr = new MembreRole();
mr.setDateDebut(null);
mr.onCreate();
assertThat(mr.getDateDebut()).isEqualTo(LocalDate.now());
}
}
// ─── Permission — branche @PrePersist ────────────────────────────────────
@Nested
@DisplayName("Permission — branche @PrePersist non couverte")
class PermissionCoverage {
@Test
@DisplayName("onCreate() avec code=null et module/ressource/action définis génère le code")
void onCreate_nullCode_withModuleRessourceAction_generatesCode() {
Permission p = new Permission();
p.setCode(null);
p.setModule("FINANCE");
p.setRessource("COTISATION");
p.setAction("CREATE");
p.onCreate();
assertThat(p.getCode()).isEqualTo("FINANCE > COTISATION > CREATE");
}
@Test
@DisplayName("onCreate() avec code=null et module=null ne génère rien")
void onCreate_nullCode_nullModule_noGeneration() {
Permission p = new Permission();
p.setCode(null);
p.setModule(null);
p.setRessource(null);
p.setAction(null);
p.onCreate();
assertThat(p.getCode()).isNull();
}
}
// ─── Role — branches @PrePersist ─────────────────────────────────────────
@Nested
@DisplayName("Role — branches @PrePersist non couvertes")
class RoleCoverage {
@Test
@DisplayName("onCreate() avec typeRole/niveauHierarchique=null initialise les defaults")
void onCreate_nullFields_setsDefaults() {
Role r = new Role();
r.setTypeRole(null);
r.setNiveauHierarchique(null);
r.onCreate();
assertThat(r.getTypeRole()).isNotNull();
assertThat(r.getNiveauHierarchique()).isEqualTo(100);
}
}
// ─── Document — branches @PrePersist ─────────────────────────────────────
@Nested
@DisplayName("Document — branches @PrePersist non couvertes")
class DocumentCoverage {
@Test
@DisplayName("onCreate() avec nombreTelechargements/typeDocument=null initialise les defaults")
void onCreate_nullFields_setsDefaults() {
Document d = new Document();
d.setNombreTelechargements(null);
d.setTypeDocument(null);
d.onCreate();
assertThat(d.getNombreTelechargements()).isEqualTo(0);
assertThat(d.getTypeDocument()).isNotNull();
}
}
// ─── PieceJointe — branche @PrePersist ───────────────────────────────────
@Nested
@DisplayName("PieceJointe — branche @PrePersist non couverte")
class PieceJointeCoverage {
@Test
@DisplayName("onCreate() avec ordre=null l'initialise à 1")
void onCreate_nullOrdre_setsOne() {
PieceJointe pj = new PieceJointe();
pj.setOrdre(null);
pj.onCreate();
assertThat(pj.getOrdre()).isEqualTo(1);
}
}
// ─── Adresse — branche @PrePersist ───────────────────────────────────────
@Nested
@DisplayName("Adresse — branche @PrePersist non couverte")
class AdresseCoverage {
@Test
@DisplayName("onCreate() avec principale=null l'initialise à false")
void onCreate_nullPrincipale_setsFalse() {
Adresse a = new Adresse();
a.setPrincipale(null);
a.onCreate();
assertThat(a.getPrincipale()).isFalse();
}
}
// ─── JournalComptable — branche @PrePersist ──────────────────────────────
@Nested
@DisplayName("JournalComptable — branche @PrePersist non couverte")
class JournalComptableCoverage {
@Test
@DisplayName("onCreate() avec statut=null initialise à OUVERT")
void onCreate_nullStatut_setsOuvert() {
JournalComptable jc = new JournalComptable();
jc.setStatut(null);
jc.onCreate();
assertThat(jc.getStatut()).isEqualTo("OUVERT");
}
@Test
@DisplayName("onCreate() avec statut vide initialise à OUVERT")
void onCreate_emptyStatut_setsOuvert() {
JournalComptable jc = new JournalComptable();
jc.setStatut("");
jc.onCreate();
assertThat(jc.getStatut()).isEqualTo("OUVERT");
}
}
// ─── SuggestionVote — branches @PrePersist ───────────────────────────────
@Nested
@DisplayName("SuggestionVote — branches @PrePersist non couvertes")
class SuggestionVoteCoverage {
@Test
@DisplayName("onPrePersist() avec dateVote=null l'initialise")
void onPrePersist_nullDateVote_setsNow() {
SuggestionVote sv = new SuggestionVote();
sv.setSuggestionId(UUID.randomUUID());
sv.setUtilisateurId(UUID.randomUUID());
sv.setDateVote(null);
sv.setDateCreation(null);
sv.onPrePersist();
assertThat(sv.getDateVote()).isNotNull();
assertThat(sv.getDateCreation()).isNotNull();
}
}
}

View File

@@ -0,0 +1,211 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests de couverture complémentaires pour Evenement.
* Couvre les branches manquantes de isTermine(), isEnCours(), getTauxRemplissage().
*/
@DisplayName("Evenement - couverture complémentaire")
class EvenementCoverageTest {
private Evenement buildEvenement() {
Evenement e = new Evenement();
e.setTitre("Test Evenement");
e.setDateDebut(LocalDateTime.now().plusDays(1));
e.setStatut("PLANIFIE");
return e;
}
@Test
@DisplayName("isTermine: true si dateFin est dans le passé (statut non TERMINE)")
void isTermine_pastDateFin_returnsTrue() {
Evenement e = buildEvenement();
e.setDateDebut(LocalDateTime.now().minusDays(2));
e.setDateFin(LocalDateTime.now().minusHours(1)); // dateFin passée
e.setStatut("CONFIRME"); // pas TERMINE
assertThat(e.isTermine()).isTrue();
}
@Test
@DisplayName("isTermine: false si dateFin est dans le futur (statut non TERMINE)")
void isTermine_futureDateFin_returnsFalse() {
Evenement e = buildEvenement();
e.setDateFin(LocalDateTime.now().plusHours(2)); // dateFin future
e.setStatut("PLANIFIE");
assertThat(e.isTermine()).isFalse();
}
@Test
@DisplayName("isEnCours: false si dateDebut future")
void isEnCours_futureDateDebut_returnsFalse() {
Evenement e = buildEvenement();
e.setDateDebut(LocalDateTime.now().plusHours(1));
assertThat(e.isEnCours()).isFalse();
}
@Test
@DisplayName("isEnCours: true si dateDebut passée et dateFin null (événement sans fin définie)")
void isEnCours_noDateFin_returnsTrue() {
Evenement e = buildEvenement();
e.setDateDebut(LocalDateTime.now().minusHours(1));
e.setDateFin(null); // pas de dateFin → toujours en cours si après dateDebut
assertThat(e.isEnCours()).isTrue();
}
@Test
@DisplayName("isEnCours: false si dateFin passée")
void isEnCours_pastDateFin_returnsFalse() {
Evenement e = buildEvenement();
e.setDateDebut(LocalDateTime.now().minusHours(2));
e.setDateFin(LocalDateTime.now().minusHours(1)); // dateFin passée
assertThat(e.isEnCours()).isFalse();
}
@Test
@DisplayName("getTauxRemplissage: null si capaciteMax est 0")
void getTauxRemplissage_zeroCapaciteMax_returnsNull() {
Evenement e = buildEvenement();
e.setCapaciteMax(0);
assertThat(e.getTauxRemplissage()).isNull();
}
@Test
@DisplayName("getTauxRemplissage: retourne le pourcentage si inscriptions")
void getTauxRemplissage_withInscriptions_returnsPercentage() {
Evenement e = buildEvenement();
e.setCapaciteMax(4);
Membre m1 = new Membre();
m1.setId(java.util.UUID.randomUUID());
InscriptionEvenement i1 = new InscriptionEvenement();
i1.setMembre(m1);
i1.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
e.getInscriptions().add(i1);
Membre m2 = new Membre();
m2.setId(java.util.UUID.randomUUID());
InscriptionEvenement i2 = new InscriptionEvenement();
i2.setMembre(m2);
i2.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
e.getInscriptions().add(i2);
// 2 inscrits sur 4 = 50%
assertThat(e.getTauxRemplissage()).isEqualTo(50.0);
}
@Test
@DisplayName("getPlacesRestantes: 0 si nbInscrits >= capaciteMax")
void getPlacesRestantes_full_returnsZero() {
Evenement e = buildEvenement();
e.setCapaciteMax(1);
Membre m = new Membre();
m.setId(java.util.UUID.randomUUID());
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(m);
i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
e.getInscriptions().add(i);
// capaciteMax=1, inscrits=1 → placesRestantes=0
assertThat(e.getPlacesRestantes()).isEqualTo(0);
}
@Test
@DisplayName("isMemberInscrit: false si membre non inscrit mais d'autres sont inscrits")
void isMemberInscrit_differentMembre_returnsFalse() {
Evenement e = buildEvenement();
Membre m = new Membre();
m.setId(java.util.UUID.randomUUID());
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(m);
i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
e.getInscriptions().add(i);
// Vérifie un autre membre (non inscrit)
assertThat(e.isMemberInscrit(java.util.UUID.randomUUID())).isFalse();
}
@Test
@DisplayName("isMemberInscrit: false si inscription non CONFIRMEE")
void isMemberInscrit_nonConfirmee_returnsFalse() {
Evenement e = buildEvenement();
java.util.UUID membreId = java.util.UUID.randomUUID();
Membre m = new Membre();
m.setId(membreId);
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(m);
i.setStatut(InscriptionEvenement.StatutInscription.ANNULEE.name()); // pas CONFIRMEE
e.getInscriptions().add(i);
assertThat(e.isMemberInscrit(membreId)).isFalse();
}
// ── Branches manquantes ───────────────────────────────────────────────────
/**
* Branch manquante dans isComplet() : capaciteMax == null → false.
* Le code : `return capaciteMax != null && getNombreInscrits() >= capaciteMax`
* → quand capaciteMax est null, la condition court-circuite (false).
*/
@Test
@DisplayName("isComplet: false si capaciteMax null (capacité illimitée)")
void isComplet_capaciteMaxNull_returnsFalse() {
Evenement e = buildEvenement();
e.setCapaciteMax(null); // illimité → jamais complet
assertThat(e.isComplet()).isFalse();
}
/**
* Branch manquante dans getTauxRemplissage() : capaciteMax == null → null.
* Le code : `if (capaciteMax == null || capaciteMax == 0) { return null; }`
* → première condition (capaciteMax == null) doit retourner null.
*/
@Test
@DisplayName("getTauxRemplissage: null si capaciteMax est null")
void getTauxRemplissage_capaciteMaxNull_returnsNull() {
Evenement e = buildEvenement();
e.setCapaciteMax(null);
assertThat(e.getTauxRemplissage()).isNull();
}
/**
* Branch manquante dans isMemberInscrit() : inscriptions == null → false.
* Le code : `return inscriptions != null && inscriptions.stream()...`
* → quand inscriptions est null, retourne false.
*/
@Test
@DisplayName("isMemberInscrit: false si inscriptions est null")
void isMemberInscrit_inscriptionsNull_returnsFalse() {
Evenement e = buildEvenement();
e.setInscriptions(null);
assertThat(e.isMemberInscrit(java.util.UUID.randomUUID())).isFalse();
}
/**
* Branch manquante dans isOuvertAuxInscriptions() : quand capaciteMax != null
* mais getNombreInscrits() < capaciteMax → ne retourne pas false (capacité non atteinte),
* passe à la vérification du statut.
* Couvre le chemin où la branche capacité est false (non bloquante).
*/
@Test
@DisplayName("isOuvertAuxInscriptions: true si capaciteMax définie mais non atteinte")
void isOuvertAuxInscriptions_capaciteNonAtteinte_returnsTrue() {
Evenement e = buildEvenement();
e.setInscriptionRequise(true);
e.setActif(true);
e.setCapaciteMax(5); // capacité non atteinte (0 inscrits)
assertThat(e.isOuvertAuxInscriptions()).isTrue();
}
}

View File

@@ -240,4 +240,138 @@ class EvenementTest {
e.setDateDebut(LocalDateTime.now());
assertThat(e.isMemberInscrit(UUID.randomUUID())).isFalse();
}
@Test
@DisplayName("isOuvertAuxInscriptions: false si actif=false")
void isOuvertAuxInscriptions_false_actifFalse() {
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now().plusDays(1));
e.setInscriptionRequise(true);
e.setStatut("PLANIFIE");
e.setActif(false);
assertThat(e.isOuvertAuxInscriptions()).isFalse();
}
@Test
@DisplayName("isOuvertAuxInscriptions: false si date limite dépassée")
void isOuvertAuxInscriptions_false_dateLimitePassed() {
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now().plusDays(1));
e.setDateLimiteInscription(LocalDateTime.now().minusDays(1));
e.setInscriptionRequise(true);
e.setStatut("PLANIFIE");
e.setActif(true);
assertThat(e.isOuvertAuxInscriptions()).isFalse();
}
@Test
@DisplayName("isOuvertAuxInscriptions: false si événement déjà commencé")
void isOuvertAuxInscriptions_false_dateDebutPassed() {
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now().minusHours(1));
e.setInscriptionRequise(true);
e.setStatut("PLANIFIE");
e.setActif(true);
assertThat(e.isOuvertAuxInscriptions()).isFalse();
}
@Test
@DisplayName("isOuvertAuxInscriptions: false si capacité atteinte")
void isOuvertAuxInscriptions_false_capacitePleine() {
Membre m = new Membre();
m.setId(UUID.randomUUID());
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(m);
i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now().plusDays(1));
e.setInscriptionRequise(true);
e.setStatut("PLANIFIE");
e.setActif(true);
e.setCapaciteMax(1);
e.getInscriptions().add(i);
assertThat(e.isOuvertAuxInscriptions()).isFalse();
}
@Test
@DisplayName("isOuvertAuxInscriptions: true si statut CONFIRME")
void isOuvertAuxInscriptions_true_statutConfirme() {
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now().plusDays(1));
e.setInscriptionRequise(true);
e.setStatut("CONFIRME");
e.setActif(true);
assertThat(e.isOuvertAuxInscriptions()).isTrue();
}
@Test
@DisplayName("getNombreInscrits: 0 si inscriptions null")
void getNombreInscrits_inscriptionsNull_returnsZero() {
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now());
e.setInscriptions(null);
assertThat(e.getNombreInscrits()).isEqualTo(0);
}
@Test
@DisplayName("isTermine: true si dateFin passée et statut non TERMINE")
void isTermine_true_datefinPassed() {
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now().minusDays(3));
e.setDateFin(LocalDateTime.now().minusDays(1));
e.setStatut("PLANIFIE");
assertThat(e.isTermine()).isTrue();
}
@Test
@DisplayName("isOuvertAuxInscriptions: false si statut ni PLANIFIE ni CONFIRME")
void isOuvertAuxInscriptions_false_statutAutre() {
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now().plusDays(1));
e.setInscriptionRequise(true);
e.setStatut("EN_COURS");
e.setActif(true);
assertThat(e.isOuvertAuxInscriptions()).isFalse();
}
@Test
@DisplayName("isMemberInscrit: false si membre non confirmé")
void isMemberInscrit_false_notConfirmed() {
UUID membreId = UUID.randomUUID();
Membre m = new Membre();
m.setId(membreId);
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(m);
i.setStatut(InscriptionEvenement.StatutInscription.EN_ATTENTE.name());
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now());
e.getInscriptions().add(i);
assertThat(e.isMemberInscrit(membreId)).isFalse();
}
@Test
@DisplayName("isMemberInscrit: false si mauvais id membre")
void isMemberInscrit_false_wrongId() {
UUID membreId = UUID.randomUUID();
Membre m = new Membre();
m.setId(UUID.randomUUID()); // different id
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(m);
i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
Evenement e = new Evenement();
e.setTitre("Ev");
e.setDateDebut(LocalDateTime.now());
e.getInscriptions().add(i);
assertThat(e.isMemberInscrit(membreId)).isFalse();
}
}

View File

@@ -0,0 +1,167 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests unitaires pour l'entité FeedbackEvenement.
* Tests purs (sans Quarkus) car les méthodes sont de la logique en mémoire.
*/
@DisplayName("FeedbackEvenement")
class FeedbackEvenementTest {
private FeedbackEvenement buildFeedback() {
FeedbackEvenement fb = new FeedbackEvenement();
fb.setNote(4);
fb.setCommentaire("Très bon événement");
fb.setDateFeedback(LocalDateTime.now());
fb.setModerationStatut(FeedbackEvenement.ModerationStatut.PUBLIE.name());
return fb;
}
@Test
@DisplayName("isPublie retourne true quand statut est PUBLIE")
void isPublie_quandStatutPublie_retourneTrue() {
FeedbackEvenement fb = buildFeedback();
fb.setModerationStatut(FeedbackEvenement.ModerationStatut.PUBLIE.name());
assertThat(fb.isPublie()).isTrue();
}
@Test
@DisplayName("isPublie retourne false quand statut est EN_ATTENTE")
void isPublie_quandStatutEnAttente_retourneFalse() {
FeedbackEvenement fb = buildFeedback();
fb.setModerationStatut(FeedbackEvenement.ModerationStatut.EN_ATTENTE.name());
assertThat(fb.isPublie()).isFalse();
}
@Test
@DisplayName("isPublie retourne false quand statut est REJETE")
void isPublie_quandStatutRejete_retourneFalse() {
FeedbackEvenement fb = buildFeedback();
fb.setModerationStatut(FeedbackEvenement.ModerationStatut.REJETE.name());
assertThat(fb.isPublie()).isFalse();
}
@Test
@DisplayName("mettreEnAttente modifie le statut et la raison")
void mettreEnAttente_modifieStatutEtRaison() {
FeedbackEvenement fb = buildFeedback();
fb.mettreEnAttente("Contenu inapproprié");
assertThat(fb.getModerationStatut()).isEqualTo(FeedbackEvenement.ModerationStatut.EN_ATTENTE.name());
assertThat(fb.getRaisonModeration()).isEqualTo("Contenu inapproprié");
assertThat(fb.isPublie()).isFalse();
}
@Test
@DisplayName("publier restaure le statut PUBLIE et efface la raison")
void publier_restaureStatutPublieEtEffaceRaison() {
FeedbackEvenement fb = buildFeedback();
fb.mettreEnAttente("Raison de test");
fb.publier();
assertThat(fb.getModerationStatut()).isEqualTo(FeedbackEvenement.ModerationStatut.PUBLIE.name());
assertThat(fb.getRaisonModeration()).isNull();
assertThat(fb.isPublie()).isTrue();
}
@Test
@DisplayName("rejeter modifie le statut à REJETE et stocke la raison")
void rejeter_modifieStatutEtRaison() {
FeedbackEvenement fb = buildFeedback();
fb.rejeter("Commentaire offensant");
assertThat(fb.getModerationStatut()).isEqualTo(FeedbackEvenement.ModerationStatut.REJETE.name());
assertThat(fb.getRaisonModeration()).isEqualTo("Commentaire offensant");
assertThat(fb.isPublie()).isFalse();
}
@Test
@DisplayName("toString retourne une représentation textuelle avec membre et evenement null")
void toString_avecMembreEtEvenementNull_retourneChaineSansException() {
FeedbackEvenement fb = buildFeedback();
fb.setMembre(null);
fb.setEvenement(null);
fb.setNote(3);
String result = fb.toString();
assertThat(result).isNotNull();
assertThat(result).contains("FeedbackEvenement");
assertThat(result).contains("null"); // membre et evenement sont null
}
@Test
@DisplayName("toString retourne une représentation textuelle avec membre et evenement renseignés")
void toString_avecMembreEtEvenement_retourneRepresentationComplete() {
FeedbackEvenement fb = buildFeedback();
Membre membre = new Membre();
membre.setEmail("test@test.com");
fb.setMembre(membre);
Evenement evenement = new Evenement();
evenement.setTitre("AG 2026");
evenement.setDateDebut(LocalDateTime.now());
fb.setEvenement(evenement);
fb.setNote(5);
String result = fb.toString();
assertThat(result).isNotNull();
assertThat(result).contains("FeedbackEvenement");
assertThat(result).contains("test@test.com");
assertThat(result).contains("AG 2026");
}
@Test
@DisplayName("preUpdate appelle onUpdate sans exception")
void preUpdate_sansException() {
FeedbackEvenement fb = buildFeedback();
// dateModification initialement null (pas d'id/version puisque pas en base)
// preUpdate() appelle super.onUpdate() qui met dateModification à now()
// On vérifie que l'appel ne lance pas d'exception
fb.preUpdate();
assertThat(fb.getDateModification()).isNotNull();
}
@Test
@DisplayName("ModerationStatut enum contient les trois valeurs attendues")
void moderationStatutEnum_contientValeurs() {
FeedbackEvenement.ModerationStatut[] values = FeedbackEvenement.ModerationStatut.values();
assertThat(values).hasSize(3);
assertThat(values).contains(
FeedbackEvenement.ModerationStatut.PUBLIE,
FeedbackEvenement.ModerationStatut.EN_ATTENTE,
FeedbackEvenement.ModerationStatut.REJETE);
}
@Test
@DisplayName("getters et setters fonctionnent correctement")
void gettersSetters_fonctionnentCorrectement() {
FeedbackEvenement fb = new FeedbackEvenement();
LocalDateTime date = LocalDateTime.of(2026, 1, 15, 10, 0);
fb.setNote(3);
fb.setCommentaire("Bien mais peut mieux faire");
fb.setDateFeedback(date);
fb.setModerationStatut(FeedbackEvenement.ModerationStatut.EN_ATTENTE.name());
fb.setRaisonModeration("Vérification en cours");
assertThat(fb.getNote()).isEqualTo(3);
assertThat(fb.getCommentaire()).isEqualTo("Bien mais peut mieux faire");
assertThat(fb.getDateFeedback()).isEqualTo(date);
assertThat(fb.getModerationStatut()).isEqualTo("EN_ATTENTE");
assertThat(fb.getRaisonModeration()).isEqualTo("Vérification en cours");
}
}

View File

@@ -3,10 +3,12 @@ package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
@DisplayName("InscriptionEvenement")
class InscriptionEvenementTest {
@@ -118,4 +120,27 @@ class InscriptionEvenementTest {
i.setEvenement(newEvenement());
assertThat(i.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("preUpdate ne lève pas d'exception")
void preUpdate_doesNotThrow() throws Exception {
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(newMembre());
i.setEvenement(newEvenement());
i.setStatut(InscriptionEvenement.StatutInscription.CONFIRMEE.name());
Method preUpdate = InscriptionEvenement.class.getDeclaredMethod("preUpdate");
preUpdate.setAccessible(true);
assertThatCode(() -> preUpdate.invoke(i)).doesNotThrowAnyException();
}
@Test
@DisplayName("toString avec membre et evenement null")
void toString_nullMemberAndEvenement() {
InscriptionEvenement i = new InscriptionEvenement();
i.setMembre(null);
i.setEvenement(null);
i.setStatut("CONFIRMEE");
String s = i.toString();
assertThat(s).isNotNull().contains("null");
}
}

View File

@@ -0,0 +1,67 @@
package dev.lions.unionflow.server.entity;
import dev.lions.unionflow.server.api.enums.paiement.StatutIntentionPaiement;
import dev.lions.unionflow.server.api.enums.paiement.TypeObjetIntentionPaiement;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests pour {@link IntentionPaiement} — méthode onCreate().
*/
@DisplayName("IntentionPaiement — onCreate")
class IntentionPaiementBranchTest {
// ── onCreate() ────────────────────────────────────────────────────────────
@Test
@DisplayName("onCreate: dateExpiration déjà définie → conservée")
void onCreate_dateExpirationDejaDefinie_conservee() throws Exception {
IntentionPaiement ip = new IntentionPaiement();
ip.setMontantTotal(new BigDecimal("5000.00"));
ip.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
LocalDateTime expirationFixe = LocalDateTime.of(2025, 12, 31, 23, 59);
ip.setDateExpiration(expirationFixe);
Method onCreate = IntentionPaiement.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(ip);
// dateExpiration doit rester la valeur fixée, pas être remplacée
assertThat(ip.getDateExpiration()).isEqualTo(expirationFixe);
// les autres defaults sont bien positionnés
assertThat(ip.getStatut()).isEqualTo(StatutIntentionPaiement.INITIEE);
assertThat(ip.getCodeDevise()).isEqualTo("XOF");
}
@Test
@DisplayName("onCreate: dateExpiration null → positionnée à now+30min")
void onCreate_dateExpirationNull_setToNowPlusTrente() throws Exception {
IntentionPaiement ip = new IntentionPaiement();
ip.setMontantTotal(new BigDecimal("5000.00"));
ip.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
ip.setStatut(null);
ip.setCodeDevise(null);
ip.setDateExpiration(null);
LocalDateTime avant = LocalDateTime.now();
Method onCreate = IntentionPaiement.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(ip);
LocalDateTime apres = LocalDateTime.now();
assertThat(ip.getDateExpiration()).isNotNull();
assertThat(ip.getDateExpiration()).isAfterOrEqualTo(avant.plusMinutes(29));
assertThat(ip.getDateExpiration()).isBeforeOrEqualTo(apres.plusMinutes(31));
assertThat(ip.getStatut()).isEqualTo(StatutIntentionPaiement.INITIEE);
assertThat(ip.getCodeDevise()).isEqualTo("XOF");
}
}

View File

@@ -5,6 +5,7 @@ import dev.lions.unionflow.server.api.enums.paiement.TypeObjetIntentionPaiement;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
@@ -121,4 +122,35 @@ class IntentionPaiementTest {
i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
assertThat(i.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("isExpiree: false si dateExpiration null")
void isExpiree_nullDate_returnsFalse() {
IntentionPaiement i = new IntentionPaiement();
i.setUtilisateur(newMembre());
i.setMontantTotal(BigDecimal.ONE);
i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
i.setDateExpiration(null);
assertThat(i.isExpiree()).isFalse();
}
@Test
@DisplayName("onCreate: initialise les défauts si null")
void onCreate_setsDefaults() throws Exception {
IntentionPaiement i = new IntentionPaiement();
i.setUtilisateur(newMembre());
i.setMontantTotal(BigDecimal.ONE);
i.setTypeObjet(TypeObjetIntentionPaiement.COTISATION);
i.setStatut(null);
i.setCodeDevise(null);
i.setDateExpiration(null);
Method onCreate = IntentionPaiement.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(i);
assertThat(i.getStatut()).isEqualTo(StatutIntentionPaiement.INITIEE);
assertThat(i.getCodeDevise()).isEqualTo("XOF");
assertThat(i.getDateExpiration()).isNotNull().isAfter(LocalDateTime.now());
}
}

View File

@@ -0,0 +1,108 @@
package dev.lions.unionflow.server.entity;
import dev.lions.unionflow.server.api.enums.comptabilite.TypeJournalComptable;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests pour {@link JournalComptable} — méthodes estDansPeriode() et onCreate().
*/
@DisplayName("JournalComptable — estDansPeriode et onCreate")
class JournalComptableBranchTest {
private JournalComptable newJournal() {
JournalComptable j = new JournalComptable();
j.setCode("AC");
j.setLibelle("Achat");
j.setTypeJournal(TypeJournalComptable.ACHATS);
return j;
}
// ── estDansPeriode() ──────────────────────────────────────────────────────
@Test
@DisplayName("estDansPeriode: dateDebut non null mais dateFin null → true (période illimitée)")
void estDansPeriode_dateFinNull_returnsTrue() {
JournalComptable j = newJournal();
j.setDateDebut(LocalDate.of(2025, 1, 1));
j.setDateFin(null);
assertThat(j.estDansPeriode(LocalDate.of(2025, 6, 15))).isTrue();
}
@Test
@DisplayName("estDansPeriode: dateDebut null mais dateFin non null → true (période illimitée)")
void estDansPeriode_dateDebutNull_returnsTrue() {
JournalComptable j = newJournal();
j.setDateDebut(null);
j.setDateFin(LocalDate.of(2025, 12, 31));
assertThat(j.estDansPeriode(LocalDate.of(2025, 6, 15))).isTrue();
}
@Test
@DisplayName("estDansPeriode: date dans la période → true")
void estDansPeriode_dateInPeriode_returnsTrue() {
JournalComptable j = newJournal();
j.setDateDebut(LocalDate.of(2025, 1, 1));
j.setDateFin(LocalDate.of(2025, 12, 31));
assertThat(j.estDansPeriode(LocalDate.of(2025, 6, 15))).isTrue();
}
@Test
@DisplayName("estDansPeriode: date hors période → false")
void estDansPeriode_dateOutsidePeriode_returnsFalse() {
JournalComptable j = newJournal();
j.setDateDebut(LocalDate.of(2025, 1, 1));
j.setDateFin(LocalDate.of(2025, 6, 30));
assertThat(j.estDansPeriode(LocalDate.of(2025, 7, 1))).isFalse();
}
// ── onCreate() ────────────────────────────────────────────────────────────
@Test
@DisplayName("onCreate: statut null → OUVERT")
void onCreate_statutNull_setsOuvert() throws Exception {
JournalComptable j = newJournal();
j.setStatut(null);
Method onCreate = JournalComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(j);
assertThat(j.getStatut()).isEqualTo("OUVERT");
}
@Test
@DisplayName("onCreate: statut vide → OUVERT")
void onCreate_statutEmpty_setsOuvert() throws Exception {
JournalComptable j = newJournal();
j.setStatut("");
Method onCreate = JournalComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(j);
assertThat(j.getStatut()).isEqualTo("OUVERT");
}
@Test
@DisplayName("onCreate: statut déjà défini → conservé")
void onCreate_statutDejaDefini_conserve() throws Exception {
JournalComptable j = newJournal();
j.setStatut("FERME");
Method onCreate = JournalComptable.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(j);
assertThat(j.getStatut()).isEqualTo("FERME");
}
}

View File

@@ -73,6 +73,30 @@ class LigneEcritureTest {
assertThat(l.isValide()).isFalse();
}
@Test
@DisplayName("isValide: false si ni débit ni crédit (both null)")
void isValide_false_bothNull() {
LigneEcriture l = new LigneEcriture();
l.setEcriture(newEcriture());
l.setCompteComptable(newCompte());
l.setNumeroLigne(1);
l.setMontantDebit(null);
l.setMontantCredit(null);
assertThat(l.isValide()).isFalse();
}
@Test
@DisplayName("isValide: false si ni débit ni crédit (both zero)")
void isValide_false_bothZero() {
LigneEcriture l = new LigneEcriture();
l.setEcriture(newEcriture());
l.setCompteComptable(newCompte());
l.setNumeroLigne(1);
l.setMontantDebit(BigDecimal.ZERO);
l.setMontantCredit(BigDecimal.ZERO);
assertThat(l.isValide()).isFalse();
}
@Test
@DisplayName("getMontant: débit ou crédit")
void getMontant() {
@@ -88,6 +112,18 @@ class LigneEcritureTest {
assertThat(l.getMontant()).isEqualByComparingTo("200");
}
@Test
@DisplayName("getMontant: retourne ZERO si ni débit ni crédit (both null)")
void getMontant_zero_whenBothNull() {
LigneEcriture l = new LigneEcriture();
l.setEcriture(newEcriture());
l.setCompteComptable(newCompte());
l.setNumeroLigne(1);
l.setMontantDebit(null);
l.setMontantCredit(null);
assertThat(l.getMontant()).isEqualByComparingTo(BigDecimal.ZERO);
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {
@@ -117,4 +153,26 @@ class LigneEcritureTest {
l.setCompteComptable(newCompte());
assertThat(l.toString()).isNotNull().isNotEmpty();
}
// ── Branch coverage manquantes ─────────────────────────────────────────
/**
* L82 branch manquante : montantCredit != null mais == 0 (not > 0)
* → `montantCredit != null && montantCredit.compareTo(BigDecimal.ZERO) > 0` → false
* → retourne BigDecimal.ZERO (la dernière ligne)
*/
@Test
@DisplayName("getMontant: montantDebit null, montantCredit = ZERO → retourne ZERO (branche false compareTo)")
void getMontant_debitNull_creditZero_returnsZero() {
LigneEcriture l = new LigneEcriture();
l.setEcriture(newEcriture());
l.setCompteComptable(newCompte());
l.setNumeroLigne(1);
// montantDebit == null → premiere condition false
// montantCredit != null mais == 0 → deuxième condition: compareTo > 0 est FALSE
// → retourne BigDecimal.ZERO
l.setMontantDebit(null);
l.setMontantCredit(BigDecimal.ZERO);
assertThat(l.getMontant()).isEqualByComparingTo(BigDecimal.ZERO);
}
}

View File

@@ -57,6 +57,18 @@ class MembreOrganisationTest {
assertThat(mo.isActif()).isFalse();
}
@Test
@DisplayName("isActif: false si statutMembre ACTIF mais actif=false (branche &&)")
void isActif_statutActif_actifFalse_returnsFalse() {
// statutMembre ACTIF mais actif=false → isActif() retourne false
MembreOrganisation mo = new MembreOrganisation();
mo.setMembre(newMembre());
mo.setOrganisation(newOrganisation());
mo.setStatutMembre(StatutMembre.ACTIF);
mo.setActif(false);
assertThat(mo.isActif()).isFalse();
}
@Test
@DisplayName("peutDemanderAide")
void peutDemanderAide() {

View File

@@ -60,6 +60,29 @@ class MembreRoleTest {
assertThat(mr.isActif()).isFalse();
}
@Test
@DisplayName("isActif: false si actif=false")
void isActif_false_whenActifFalse() {
MembreRole mr = new MembreRole();
mr.setMembreOrganisation(newMembreOrganisation());
mr.setRole(newRole());
mr.setActif(false);
mr.setDateDebut(LocalDate.now().minusDays(1));
assertThat(mr.isActif()).isFalse();
}
@Test
@DisplayName("isActif: false si dateFin dans le passé")
void isActif_false_dateFinExpiree() {
MembreRole mr = new MembreRole();
mr.setMembreOrganisation(newMembreOrganisation());
mr.setRole(newRole());
mr.setActif(true);
mr.setDateDebut(LocalDate.now().minusDays(10));
mr.setDateFin(LocalDate.now().minusDays(1));
assertThat(mr.isActif()).isFalse();
}
@Test
@DisplayName("isActif: true si dans la période")
void isActif_true() {
@@ -98,4 +121,64 @@ class MembreRoleTest {
mr.setRole(newRole());
assertThat(mr.toString()).isNotNull().isNotEmpty();
}
// ── Branch coverage manquantes ─────────────────────────────────────────
@Test
@DisplayName("isActif: dateDebut null → condition L76 false (null short-circuit) → continue vers dateFin")
void isActif_dateDebutNull_branchNullShortCircuit() {
MembreRole mr = new MembreRole();
mr.setMembreOrganisation(newMembreOrganisation());
mr.setRole(newRole());
mr.setActif(true);
mr.setDateDebut(null); // null → dateDebut != null = false → skip if body at L76
mr.setDateFin(null); // null → dateFin != null = false → skip if body at L79
// → return true
assertThat(mr.isActif()).isTrue();
}
/**
* L72 branch manquante : getActif() == null → Boolean.TRUE.equals(null) = false
* → !false = true → returns false
*/
@Test
@DisplayName("isActif: actif null → Boolean.TRUE.equals(null) false → retourne false")
void isActif_actifNull_returnsFalse() {
MembreRole mr = new MembreRole();
mr.setMembreOrganisation(newMembreOrganisation());
mr.setRole(newRole());
// actif n'est pas défini (null)
mr.setDateDebut(LocalDate.now().minusDays(1));
assertThat(mr.isActif()).isFalse();
}
@Test
@DisplayName("isActif: dateDebut non null mais passée → ne retourne pas false (branche false isBefore)")
void isActif_dateDebut_notNull_notBefore_continues() {
MembreRole mr = new MembreRole();
mr.setMembreOrganisation(newMembreOrganisation());
mr.setRole(newRole());
mr.setActif(true);
// dateDebut dans le passé → isBefore(dateDebut) est false → on continue
mr.setDateDebut(LocalDate.now().minusDays(5));
mr.setDateFin(null);
assertThat(mr.isActif()).isTrue();
}
/**
* L79 branch manquante : dateFin != null mais today <= dateFin (NOT after)
* → `dateFin != null && aujourdhui.isAfter(dateFin)` → false → continue → return true
*/
@Test
@DisplayName("isActif: dateFin non null mais pas encore dépassée → ne retourne pas false (branche false isAfter)")
void isActif_dateFin_notNull_notAfter_returnsTrue() {
MembreRole mr = new MembreRole();
mr.setMembreOrganisation(newMembreOrganisation());
mr.setRole(newRole());
mr.setActif(true);
mr.setDateDebut(LocalDate.now().minusDays(5));
// dateFin dans le futur → isAfter(dateFin) est false → retourne true
mr.setDateFin(LocalDate.now().plusDays(5));
assertThat(mr.isActif()).isTrue();
}
}

View File

@@ -0,0 +1,37 @@
package dev.lions.unionflow.server.entity;
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("Message")
class MessageTest {
@Test
@DisplayName("markAsRead sets status to READ and sets readAt")
void markAsRead_setsStatusAndReadAt() {
Message m = new Message();
m.setStatus(MessageStatus.SENT);
assertThat(m.getReadAt()).isNull();
m.markAsRead();
assertThat(m.getStatus()).isEqualTo(MessageStatus.READ);
assertThat(m.getReadAt()).isNotNull();
}
@Test
@DisplayName("markAsEdited sets isEdited true and sets editedAt")
void markAsEdited_setsIsEditedAndEditedAt() {
Message m = new Message();
assertThat(m.getIsEdited()).isFalse();
assertThat(m.getEditedAt()).isNull();
m.markAsEdited();
assertThat(m.getIsEdited()).isTrue();
assertThat(m.getEditedAt()).isNotNull();
}
}

View File

@@ -3,6 +3,7 @@ package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;
@@ -95,4 +96,34 @@ class NotificationTest {
n.setTypeNotification("EMAIL");
assertThat(n.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("isEnvoyee: false si statut null")
void isEnvoyee_nullStatut_returnsFalse() {
Notification n = new Notification();
n.setTypeNotification("EMAIL");
n.setStatut(null);
assertThat(n.isEnvoyee()).isFalse();
assertThat(n.isLue()).isFalse();
}
@Test
@DisplayName("onCreate: initialise les défauts si null")
void onCreate_setsDefaults() throws Exception {
Notification n = new Notification();
n.setTypeNotification("EMAIL");
n.setPriorite(null);
n.setStatut(null);
n.setNombreTentatives(null);
n.setDateEnvoiPrevue(null);
Method onCreate = Notification.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(n);
assertThat(n.getPriorite()).isEqualTo("NORMALE");
assertThat(n.getStatut()).isEqualTo("EN_ATTENTE");
assertThat(n.getNombreTentatives()).isEqualTo(0);
assertThat(n.getDateEnvoiPrevue()).isNotNull();
}
}

View File

@@ -1,145 +1,805 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("Organisation")
@DisplayName("Organisation — couverture complète")
class OrganisationTest {
@Test
@DisplayName("getters/setters")
void gettersSetters() {
// ─── Utilitaire ───────────────────────────────────────────────────────────
private static Organisation baseOrganisation() {
Organisation o = new Organisation();
o.setNom("Club Lions Paris");
o.setNomCourt("CL Paris");
o.setTypeOrganisation("ASSOCIATION");
o.setStatut("ACTIVE");
o.setEmail("contact@club.fr");
o.setTelephone("+33100000000");
o.setDevise("XOF");
o.setNombreMembres(50);
o.setEstOrganisationRacine(true);
o.setAccepteNouveauxMembres(true);
assertThat(o.getNom()).isEqualTo("Club Lions Paris");
assertThat(o.getNomCourt()).isEqualTo("CL Paris");
assertThat(o.getStatut()).isEqualTo("ACTIVE");
assertThat(o.getEmail()).isEqualTo("contact@club.fr");
assertThat(o.getNombreMembres()).isEqualTo(50);
return o;
}
@Test
@DisplayName("getNomComplet: avec et sans nomCourt")
void getNomComplet() {
Organisation o = new Organisation();
o.setNom("Club A");
o.setNomCourt("CA");
o.setTypeOrganisation("X");
o.setStatut("ACTIVE");
o.setEmail("a@b.com");
assertThat(o.getNomComplet()).isEqualTo("Club A (CA)");
o.setNomCourt(null);
assertThat(o.getNomComplet()).isEqualTo("Club A");
// ─── Getters / Setters ────────────────────────────────────────────────────
@Nested
@DisplayName("Getters et setters")
class GettersSetters {
@Test
@DisplayName("Champs de base")
void champsDeBase() {
Organisation o = baseOrganisation();
o.setTelephone("+33100000000");
o.setDevise("XOF");
o.setNombreMembres(50);
o.setEstOrganisationRacine(true);
o.setAccepteNouveauxMembres(true);
assertThat(o.getNom()).isEqualTo("Club Lions Paris");
assertThat(o.getNomCourt()).isEqualTo("CL Paris");
assertThat(o.getStatut()).isEqualTo("ACTIVE");
assertThat(o.getEmail()).isEqualTo("contact@club.fr");
assertThat(o.getTelephone()).isEqualTo("+33100000000");
assertThat(o.getNombreMembres()).isEqualTo(50);
assertThat(o.getEstOrganisationRacine()).isTrue();
assertThat(o.getAccepteNouveauxMembres()).isTrue();
}
@Test
@DisplayName("description, numeroEnregistrement, dateFondation")
void descriptionEtEnregistrement() {
Organisation o = baseOrganisation();
LocalDate fondation = LocalDate.of(2010, 6, 15);
o.setDescription("Un club de bienfaisance");
o.setNumeroEnregistrement("REG-2010-00123");
o.setDateFondation(fondation);
assertThat(o.getDescription()).isEqualTo("Un club de bienfaisance");
assertThat(o.getNumeroEnregistrement()).isEqualTo("REG-2010-00123");
assertThat(o.getDateFondation()).isEqualTo(fondation);
}
@Test
@DisplayName("telephoneSecondaire et emailSecondaire")
void contactsSecondaires() {
Organisation o = baseOrganisation();
o.setTelephoneSecondaire("+33100000099");
o.setEmailSecondaire("info@club.fr");
assertThat(o.getTelephoneSecondaire()).isEqualTo("+33100000099");
assertThat(o.getEmailSecondaire()).isEqualTo("info@club.fr");
}
@Test
@DisplayName("adresse, ville, region, pays, codePostal")
void adresseComplete() {
Organisation o = baseOrganisation();
o.setAdresse("12 rue de la Paix");
o.setVille("Abidjan");
o.setRegion("Lagunes");
o.setPays("Côte d'Ivoire");
o.setCodePostal("01 BP 1234");
assertThat(o.getAdresse()).isEqualTo("12 rue de la Paix");
assertThat(o.getVille()).isEqualTo("Abidjan");
assertThat(o.getRegion()).isEqualTo("Lagunes");
assertThat(o.getPays()).isEqualTo("Côte d'Ivoire");
assertThat(o.getCodePostal()).isEqualTo("01 BP 1234");
}
@Test
@DisplayName("latitude et longitude")
void coordonneesGeographiques() {
Organisation o = baseOrganisation();
o.setLatitude(new BigDecimal("5.354680"));
o.setLongitude(new BigDecimal("-4.001430"));
assertThat(o.getLatitude()).isEqualByComparingTo("5.354680");
assertThat(o.getLongitude()).isEqualByComparingTo("-4.001430");
}
@Test
@DisplayName("siteWeb, logo, reseauxSociaux")
void webEtReseaux() {
Organisation o = baseOrganisation();
o.setSiteWeb("https://club-lions-paris.fr");
o.setLogo("https://cdn.example.com/logo.png");
o.setReseauxSociaux("{\"twitter\":\"@clublions\"}");
assertThat(o.getSiteWeb()).isEqualTo("https://club-lions-paris.fr");
assertThat(o.getLogo()).isEqualTo("https://cdn.example.com/logo.png");
assertThat(o.getReseauxSociaux()).isEqualTo("{\"twitter\":\"@clublions\"}");
}
@Test
@DisplayName("niveauHierarchique, cheminHierarchique, organisationParente")
void hierarchie() {
Organisation parent = baseOrganisation();
parent.setId(UUID.randomUUID());
Organisation enfant = baseOrganisation();
enfant.setNom("Sous-club Lions");
enfant.setEmail("sous@club.fr");
enfant.setOrganisationParente(parent);
enfant.setNiveauHierarchique(1);
enfant.setCheminHierarchique("/" + parent.getId());
enfant.setEstOrganisationRacine(false);
assertThat(enfant.getOrganisationParente()).isEqualTo(parent);
assertThat(enfant.getNiveauHierarchique()).isEqualTo(1);
assertThat(enfant.getCheminHierarchique()).startsWith("/");
assertThat(enfant.getEstOrganisationRacine()).isFalse();
}
@Test
@DisplayName("nombreAdministrateurs")
void nombreAdministrateurs() {
Organisation o = baseOrganisation();
o.setNombreAdministrateurs(5);
assertThat(o.getNombreAdministrateurs()).isEqualTo(5);
}
@Test
@DisplayName("budgetAnnuel, devise, cotisationObligatoire, montantCotisationAnnuelle")
void finances() {
Organisation o = baseOrganisation();
o.setBudgetAnnuel(new BigDecimal("5000000.00"));
o.setDevise("XOF");
o.setCotisationObligatoire(true);
o.setMontantCotisationAnnuelle(new BigDecimal("120000.00"));
assertThat(o.getBudgetAnnuel()).isEqualByComparingTo("5000000.00");
assertThat(o.getDevise()).isEqualTo("XOF");
assertThat(o.getCotisationObligatoire()).isTrue();
assertThat(o.getMontantCotisationAnnuelle()).isEqualByComparingTo("120000.00");
}
@Test
@DisplayName("objectifs, activitesPrincipales, certifications, partenaires, notes")
void informationsComplementaires() {
Organisation o = baseOrganisation();
o.setObjectifs("Aide humanitaire");
o.setActivitesPrincipales("Collecte de fonds, bénévolat");
o.setCertifications("ISO 9001");
o.setPartenaires("Croix-Rouge, ONU");
o.setNotes("Organisation fondée par des bénévoles");
assertThat(o.getObjectifs()).isEqualTo("Aide humanitaire");
assertThat(o.getActivitesPrincipales()).isEqualTo("Collecte de fonds, bénévolat");
assertThat(o.getCertifications()).isEqualTo("ISO 9001");
assertThat(o.getPartenaires()).isEqualTo("Croix-Rouge, ONU");
assertThat(o.getNotes()).isEqualTo("Organisation fondée par des bénévoles");
}
@Test
@DisplayName("organisationPublique")
void organisationPublique() {
Organisation o = baseOrganisation();
o.setOrganisationPublique(false);
assertThat(o.getOrganisationPublique()).isFalse();
o.setOrganisationPublique(true);
assertThat(o.getOrganisationPublique()).isTrue();
}
@Test
@DisplayName("Relations : membresOrganisations, adresses, comptesWave")
void relations() {
Organisation o = baseOrganisation();
o.setMembresOrganisations(new ArrayList<>());
o.setAdresses(new ArrayList<>());
o.setComptesWave(new ArrayList<>());
assertThat(o.getMembresOrganisations()).isNotNull().isEmpty();
assertThat(o.getAdresses()).isNotNull().isEmpty();
assertThat(o.getComptesWave()).isNotNull().isEmpty();
}
@Test
@DisplayName("Champs hérités de BaseEntity (id, dateCreation, creePar, modifiePar, version, actif)")
void champsBaseEntity() {
UUID id = UUID.randomUUID();
Organisation o = baseOrganisation();
o.setId(id);
o.setCreePar("admin@unionflow.dev");
o.setModifiePar("manager@unionflow.dev");
o.setVersion(2L);
o.setActif(true);
assertThat(o.getId()).isEqualTo(id);
assertThat(o.getCreePar()).isEqualTo("admin@unionflow.dev");
assertThat(o.getModifiePar()).isEqualTo("manager@unionflow.dev");
assertThat(o.getVersion()).isEqualTo(2L);
assertThat(o.getActif()).isTrue();
}
}
@Test
@DisplayName("getAncienneteAnnees et isRecente")
void anciennete() {
Organisation o = new Organisation();
o.setNom("X");
o.setTypeOrganisation("X");
o.setStatut("ACTIVE");
o.setEmail("x@y.com");
o.setDateFondation(LocalDate.now().minusYears(5));
assertThat(o.getAncienneteAnnees()).isEqualTo(5);
assertThat(o.isRecente()).isFalse();
o.setDateFondation(LocalDate.now().minusYears(1));
assertThat(o.isRecente()).isTrue();
// ─── Builder ──────────────────────────────────────────────────────────────
@Nested
@DisplayName("Builder Lombok")
class BuilderTest {
@Test
@DisplayName("Builder avec champs obligatoires")
void builderChampsObligatoires() {
Organisation o = Organisation.builder()
.nom("Association Solidarité")
.typeOrganisation("ASSOCIATION")
.statut("ACTIVE")
.email("contact@solidarite.ci")
.build();
assertThat(o.getNom()).isEqualTo("Association Solidarité");
assertThat(o.getTypeOrganisation()).isEqualTo("ASSOCIATION");
assertThat(o.getStatut()).isEqualTo("ACTIVE");
assertThat(o.getEmail()).isEqualTo("contact@solidarite.ci");
}
@Test
@DisplayName("Builder : valeurs @Builder.Default")
void builderDefaults() {
Organisation o = Organisation.builder()
.nom("Coopérative Test")
.typeOrganisation("COOPERATIVE")
.statut("ACTIVE")
.email("coop@test.ci")
.build();
assertThat(o.getNiveauHierarchique()).isEqualTo(0);
assertThat(o.getEstOrganisationRacine()).isTrue();
assertThat(o.getNombreMembres()).isEqualTo(0);
assertThat(o.getNombreAdministrateurs()).isEqualTo(0);
assertThat(o.getDevise()).isEqualTo("XOF");
assertThat(o.getCotisationObligatoire()).isFalse();
assertThat(o.getOrganisationPublique()).isTrue();
assertThat(o.getAccepteNouveauxMembres()).isTrue();
assertThat(o.getMembresOrganisations()).isNotNull().isEmpty();
assertThat(o.getAdresses()).isNotNull().isEmpty();
assertThat(o.getComptesWave()).isNotNull().isEmpty();
}
@Test
@DisplayName("Builder avec tous les champs facultatifs")
void builderChampsOptionnels() {
LocalDate fondation = LocalDate.of(2000, 1, 1);
Organisation parent = baseOrganisation();
parent.setId(UUID.randomUUID());
Organisation o = Organisation.builder()
.nom("Lions Club Abidjan")
.nomCourt("LCA")
.typeOrganisation("LIONS")
.statut("ACTIVE")
.email("lions@abidjan.ci")
.telephone("+22520000000")
.telephoneSecondaire("+22521000000")
.emailSecondaire("info@lions.ci")
.description("Club Lions International")
.dateFondation(fondation)
.numeroEnregistrement("LCA-2000-001")
.adresse("Plateau, Av. de la République")
.ville("Abidjan")
.region("Lagunes")
.pays("Côte d'Ivoire")
.codePostal("01 BP 100")
.latitude(new BigDecimal("5.354"))
.longitude(new BigDecimal("-4.001"))
.siteWeb("https://lions.ci")
.logo("https://cdn.lions.ci/logo.png")
.reseauxSociaux("{\"facebook\":\"lionsabidjan\"}")
.organisationParente(parent)
.niveauHierarchique(1)
.estOrganisationRacine(false)
.cheminHierarchique("/" + parent.getId())
.nombreMembres(120)
.nombreAdministrateurs(8)
.budgetAnnuel(new BigDecimal("10000000.00"))
.devise("XOF")
.cotisationObligatoire(true)
.montantCotisationAnnuelle(new BigDecimal("100000.00"))
.objectifs("Service humanitaire")
.activitesPrincipales("Actions sociales")
.certifications("Lions International")
.partenaires("UNICEF")
.notes("Fondé par des membres engagés")
.organisationPublique(true)
.accepteNouveauxMembres(true)
.build();
assertThat(o.getNomCourt()).isEqualTo("LCA");
assertThat(o.getDescription()).isEqualTo("Club Lions International");
assertThat(o.getDateFondation()).isEqualTo(fondation);
assertThat(o.getNumeroEnregistrement()).isEqualTo("LCA-2000-001");
assertThat(o.getVille()).isEqualTo("Abidjan");
assertThat(o.getPays()).isEqualTo("Côte d'Ivoire");
assertThat(o.getLatitude()).isEqualByComparingTo("5.354");
assertThat(o.getLongitude()).isEqualByComparingTo("-4.001");
assertThat(o.getSiteWeb()).isEqualTo("https://lions.ci");
assertThat(o.getOrganisationParente()).isEqualTo(parent);
assertThat(o.getNiveauHierarchique()).isEqualTo(1);
assertThat(o.getEstOrganisationRacine()).isFalse();
assertThat(o.getNombreMembres()).isEqualTo(120);
assertThat(o.getBudgetAnnuel()).isEqualByComparingTo("10000000.00");
assertThat(o.getCotisationObligatoire()).isTrue();
assertThat(o.getMontantCotisationAnnuelle()).isEqualByComparingTo("100000.00");
assertThat(o.getObjectifs()).isEqualTo("Service humanitaire");
}
}
@Test
@DisplayName("isActive")
void isActive() {
Organisation o = new Organisation();
o.setNom("X");
o.setTypeOrganisation("X");
o.setStatut("ACTIVE");
o.setEmail("x@y.com");
o.setActif(true);
assertThat(o.isActive()).isTrue();
o.setStatut("SUSPENDUE");
assertThat(o.isActive()).isFalse();
// ─── Méthodes métier ──────────────────────────────────────────────────────
@Nested
@DisplayName("Méthodes métier")
class MethodesMetier {
@Test
@DisplayName("getNomComplet() avec nomCourt non vide")
void getNomComplet_avecNomCourt() {
Organisation o = baseOrganisation();
o.setNomCourt("CL Paris");
assertThat(o.getNomComplet()).isEqualTo("Club Lions Paris (CL Paris)");
}
@Test
@DisplayName("getNomComplet() avec nomCourt=null")
void getNomComplet_nomCourtNull() {
Organisation o = baseOrganisation();
o.setNomCourt(null);
assertThat(o.getNomComplet()).isEqualTo("Club Lions Paris");
}
@Test
@DisplayName("getNomComplet() avec nomCourt vide ('')")
void getNomComplet_nomCourtVide() {
Organisation o = baseOrganisation();
o.setNomCourt("");
assertThat(o.getNomComplet()).isEqualTo("Club Lions Paris");
}
@Test
@DisplayName("getAncienneteAnnees() avec dateFondation=null retourne 0")
void anciennete_dateFondationNull_retourneZero() {
Organisation o = baseOrganisation();
o.setDateFondation(null);
assertThat(o.getAncienneteAnnees()).isEqualTo(0);
}
@Test
@DisplayName("getAncienneteAnnees() avec dateFondation il y a 5 ans retourne 5")
void anciennete_cincAns_retourneCinq() {
Organisation o = baseOrganisation();
o.setDateFondation(LocalDate.now().minusYears(5));
assertThat(o.getAncienneteAnnees()).isEqualTo(5);
}
@Test
@DisplayName("isRecente() retourne false si ancienneté >= 2 ans")
void isRecente_ancienneOrganisation_retourneFalse() {
Organisation o = baseOrganisation();
o.setDateFondation(LocalDate.now().minusYears(3));
assertThat(o.isRecente()).isFalse();
}
@Test
@DisplayName("isRecente() retourne true si ancienneté < 2 ans")
void isRecente_jeuneOrganisation_retourneTrue() {
Organisation o = baseOrganisation();
o.setDateFondation(LocalDate.now().minusYears(1));
assertThat(o.isRecente()).isTrue();
}
@Test
@DisplayName("isRecente() retourne true si dateFondation=null (ancienneté=0 < 2)")
void isRecente_dateFondationNull_retourneTrue() {
Organisation o = baseOrganisation();
o.setDateFondation(null);
assertThat(o.isRecente()).isTrue();
}
@Test
@DisplayName("isActive() retourne true si statut=ACTIVE et actif=true")
void isActive_activeEtActif_retourneTrue() {
Organisation o = baseOrganisation();
o.setStatut("ACTIVE");
o.setActif(true);
assertThat(o.isActive()).isTrue();
}
@Test
@DisplayName("isActive() retourne false si statut=SUSPENDUE")
void isActive_suspendue_retourneFalse() {
Organisation o = baseOrganisation();
o.setStatut("SUSPENDUE");
o.setActif(true);
assertThat(o.isActive()).isFalse();
}
@Test
@DisplayName("isActive() retourne false si actif=false")
void isActive_inactif_retourneFalse() {
Organisation o = baseOrganisation();
o.setStatut("ACTIVE");
o.setActif(false);
assertThat(o.isActive()).isFalse();
}
@Test
@DisplayName("isActive() retourne false si actif=null")
void isActive_actifNull_retourneFalse() {
Organisation o = baseOrganisation();
o.setStatut("ACTIVE");
o.setActif(null);
assertThat(o.isActive()).isFalse();
}
@Test
@DisplayName("ajouterMembre() incrémente nombreMembres")
void ajouterMembre_incremente() {
Organisation o = baseOrganisation();
o.setNombreMembres(10);
o.ajouterMembre();
assertThat(o.getNombreMembres()).isEqualTo(11);
}
@Test
@DisplayName("ajouterMembre() avec nombreMembres=null initialise à 1")
void ajouterMembre_null_initialiseeA1() {
Organisation o = baseOrganisation();
o.setNombreMembres(null);
o.ajouterMembre();
assertThat(o.getNombreMembres()).isEqualTo(1);
}
@Test
@DisplayName("retirerMembre() décrémente nombreMembres")
void retirerMembre_decremente() {
Organisation o = baseOrganisation();
o.setNombreMembres(10);
o.retirerMembre();
assertThat(o.getNombreMembres()).isEqualTo(9);
}
@Test
@DisplayName("retirerMembre() ne passe pas sous 0 quand nombreMembres=0")
void retirerMembre_zero_resteAZero() {
Organisation o = baseOrganisation();
o.setNombreMembres(0);
o.retirerMembre();
assertThat(o.getNombreMembres()).isEqualTo(0);
}
@Test
@DisplayName("retirerMembre() ne fait rien si nombreMembres=null")
void retirerMembre_null_neFaitRien() {
Organisation o = baseOrganisation();
o.setNombreMembres(null);
o.retirerMembre();
assertThat(o.getNombreMembres()).isNull();
}
@Test
@DisplayName("activer() met statut=ACTIVE et actif=true")
void activer_metAJourStatutEtActif() {
Organisation o = baseOrganisation();
o.setStatut("SUSPENDUE");
o.setActif(false);
o.activer("admin@unionflow.dev");
assertThat(o.getStatut()).isEqualTo("ACTIVE");
assertThat(o.getActif()).isTrue();
assertThat(o.getModifiePar()).isEqualTo("admin@unionflow.dev");
assertThat(o.getDateModification()).isNotNull();
}
@Test
@DisplayName("suspendre() met statut=SUSPENDUE et accepteNouveauxMembres=false")
void suspendre_metAJourStatutEtMembres() {
Organisation o = baseOrganisation();
o.setAccepteNouveauxMembres(true);
o.suspendre("manager@unionflow.dev");
assertThat(o.getStatut()).isEqualTo("SUSPENDUE");
assertThat(o.getAccepteNouveauxMembres()).isFalse();
assertThat(o.getModifiePar()).isEqualTo("manager@unionflow.dev");
}
@Test
@DisplayName("dissoudre() met statut=DISSOUTE, actif=false, accepteNouveauxMembres=false")
void dissoudre_metAJourTousLesChamps() {
Organisation o = baseOrganisation();
o.setActif(true);
o.setAccepteNouveauxMembres(true);
o.dissoudre("liquidateur@unionflow.dev");
assertThat(o.getStatut()).isEqualTo("DISSOUTE");
assertThat(o.getActif()).isFalse();
assertThat(o.getAccepteNouveauxMembres()).isFalse();
assertThat(o.getModifiePar()).isEqualTo("liquidateur@unionflow.dev");
}
}
@Test
@DisplayName("ajouterMembre et retirerMembre")
void ajouterRetirerMembre() {
Organisation o = new Organisation();
o.setNom("X");
o.setTypeOrganisation("X");
o.setStatut("ACTIVE");
o.setEmail("x@y.com");
o.setNombreMembres(10);
o.ajouterMembre();
assertThat(o.getNombreMembres()).isEqualTo(11);
o.retirerMembre();
o.retirerMembre();
assertThat(o.getNombreMembres()).isEqualTo(9);
// ─── @PrePersist onCreate ─────────────────────────────────────────────────
@Nested
@DisplayName("@PrePersist onCreate()")
class OnCreate {
@Test
@DisplayName("statut=null est initialisé à ACTIVE")
void statut_null_initialiseeAACtive() {
Organisation o = baseOrganisation();
o.setStatut(null);
o.onCreate();
assertThat(o.getStatut()).isEqualTo("ACTIVE");
}
@Test
@DisplayName("statut déjà défini n'est pas écrasé")
void statut_dejaDefini_nonEcrase() {
Organisation o = baseOrganisation();
o.setStatut("SUSPENDUE");
o.onCreate();
assertThat(o.getStatut()).isEqualTo("SUSPENDUE");
}
@Test
@DisplayName("typeOrganisation=null est initialisé à ASSOCIATION")
void typeOrganisation_null_initialiseeAAssociation() {
Organisation o = baseOrganisation();
o.setTypeOrganisation(null);
o.onCreate();
assertThat(o.getTypeOrganisation()).isEqualTo("ASSOCIATION");
}
@Test
@DisplayName("typeOrganisation déjà défini n'est pas écrasé")
void typeOrganisation_dejaDefini_nonEcrase() {
Organisation o = baseOrganisation();
o.setTypeOrganisation("COOPERATIVE");
o.onCreate();
assertThat(o.getTypeOrganisation()).isEqualTo("COOPERATIVE");
}
@Test
@DisplayName("devise=null est initialisé à XOF")
void devise_null_initialiseeAXof() {
Organisation o = baseOrganisation();
o.setDevise(null);
o.onCreate();
assertThat(o.getDevise()).isEqualTo("XOF");
}
@Test
@DisplayName("devise déjà défini n'est pas écrasé")
void devise_dejaDefinie_nonEcrasee() {
Organisation o = baseOrganisation();
o.setDevise("EUR");
o.onCreate();
assertThat(o.getDevise()).isEqualTo("EUR");
}
@Test
@DisplayName("niveauHierarchique=null est initialisé à 0")
void niveauHierarchique_null_initialiseeAZero() {
Organisation o = baseOrganisation();
o.setNiveauHierarchique(null);
o.onCreate();
assertThat(o.getNiveauHierarchique()).isEqualTo(0);
}
@Test
@DisplayName("estOrganisationRacine=null sans parent est initialisé à true")
void estOrganisationRacine_null_sansParent_initialiseeATrue() {
Organisation o = baseOrganisation();
o.setEstOrganisationRacine(null);
o.setOrganisationParente(null);
o.onCreate();
assertThat(o.getEstOrganisationRacine()).isTrue();
}
@Test
@DisplayName("estOrganisationRacine=null avec parent est initialisé à false")
void estOrganisationRacine_null_avecParent_initialiseeAFalse() {
Organisation parent = baseOrganisation();
parent.setId(UUID.randomUUID());
Organisation o = baseOrganisation();
o.setNom("Sous-club");
o.setEmail("sous@club.fr");
o.setEstOrganisationRacine(null);
o.setOrganisationParente(parent);
o.onCreate();
assertThat(o.getEstOrganisationRacine()).isFalse();
}
@Test
@DisplayName("estOrganisationRacine déjà défini n'est pas écrasé")
void estOrganisationRacine_dejaDefini_nonEcrase() {
Organisation o = baseOrganisation();
o.setEstOrganisationRacine(false);
o.setOrganisationParente(null);
o.onCreate();
assertThat(o.getEstOrganisationRacine()).isFalse();
}
@Test
@DisplayName("nombreMembres=null est initialisé à 0")
void nombreMembres_null_initialiseeAZero() {
Organisation o = baseOrganisation();
o.setNombreMembres(null);
o.onCreate();
assertThat(o.getNombreMembres()).isEqualTo(0);
}
@Test
@DisplayName("nombreAdministrateurs=null est initialisé à 0")
void nombreAdministrateurs_null_initialiseeAZero() {
Organisation o = baseOrganisation();
o.setNombreAdministrateurs(null);
o.onCreate();
assertThat(o.getNombreAdministrateurs()).isEqualTo(0);
}
@Test
@DisplayName("organisationPublique=null est initialisé à true")
void organisationPublique_null_initialiseeATrue() {
Organisation o = baseOrganisation();
o.setOrganisationPublique(null);
o.onCreate();
assertThat(o.getOrganisationPublique()).isTrue();
}
@Test
@DisplayName("accepteNouveauxMembres=null est initialisé à true")
void accepteNouveauxMembres_null_initialiseeATrue() {
Organisation o = baseOrganisation();
o.setAccepteNouveauxMembres(null);
o.onCreate();
assertThat(o.getAccepteNouveauxMembres()).isTrue();
}
@Test
@DisplayName("cotisationObligatoire=null est initialisé à false")
void cotisationObligatoire_null_initialiseeAFalse() {
Organisation o = baseOrganisation();
o.setCotisationObligatoire(null);
o.onCreate();
assertThat(o.getCotisationObligatoire()).isFalse();
}
@Test
@DisplayName("onCreate() initialise aussi dateCreation et actif via BaseEntity")
void onCreateInitialiseBaseEntity() {
Organisation o = baseOrganisation();
o.setDateCreation(null);
o.setActif(null);
o.onCreate();
assertThat(o.getDateCreation()).isNotNull();
assertThat(o.getActif()).isTrue();
}
@Test
@DisplayName("onCreate() avec tous les champs null initialise toutes les valeurs par défaut")
void onCreateTousChampNulls_initialiseDefaults() {
Organisation o = new Organisation();
o.setNom("Test Org");
o.setEmail("test@org.ci");
// Tous les @Builder.Default sont null via new()
o.setStatut(null);
o.setTypeOrganisation(null);
o.setDevise(null);
o.setNiveauHierarchique(null);
o.setEstOrganisationRacine(null);
o.setOrganisationParente(null);
o.setNombreMembres(null);
o.setNombreAdministrateurs(null);
o.setOrganisationPublique(null);
o.setAccepteNouveauxMembres(null);
o.setCotisationObligatoire(null);
o.onCreate();
assertThat(o.getStatut()).isEqualTo("ACTIVE");
assertThat(o.getTypeOrganisation()).isEqualTo("ASSOCIATION");
assertThat(o.getDevise()).isEqualTo("XOF");
assertThat(o.getNiveauHierarchique()).isEqualTo(0);
assertThat(o.getEstOrganisationRacine()).isTrue();
assertThat(o.getNombreMembres()).isEqualTo(0);
assertThat(o.getNombreAdministrateurs()).isEqualTo(0);
assertThat(o.getOrganisationPublique()).isTrue();
assertThat(o.getAccepteNouveauxMembres()).isTrue();
assertThat(o.getCotisationObligatoire()).isFalse();
}
}
@Test
@DisplayName("activer, suspendre, dissoudre")
void activerSuspendreDissoudre() {
Organisation o = new Organisation();
o.setNom("X");
o.setTypeOrganisation("X");
o.setStatut("SUSPENDUE");
o.setEmail("x@y.com");
o.activer("admin@test.com");
assertThat(o.getStatut()).isEqualTo("ACTIVE");
assertThat(o.getActif()).isTrue();
o.suspendre("admin@test.com");
assertThat(o.getStatut()).isEqualTo("SUSPENDUE");
assertThat(o.getAccepteNouveauxMembres()).isFalse();
o.dissoudre("admin@test.com");
assertThat(o.getStatut()).isEqualTo("DISSOUTE");
assertThat(o.getActif()).isFalse();
// ─── Equals / HashCode / toString ────────────────────────────────────────
@Nested
@DisplayName("equals, hashCode et toString")
class EgaliteEtToString {
@Test
@DisplayName("Deux instances avec le même id sont égales")
void equals_memeId_egales() {
UUID id = UUID.randomUUID();
Organisation a = buildOrganisation(id, "N", "e@e.com");
Organisation b = buildOrganisation(id, "N", "e@e.com");
assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
}
@Test
@DisplayName("Deux instances avec des id différents ne sont pas égales")
void equals_idsDifferents_pasEgales() {
Organisation a = buildOrganisation(UUID.randomUUID(), "N1", "e1@e.com");
Organisation b = buildOrganisation(UUID.randomUUID(), "N2", "e2@e.com");
assertThat(a).isNotEqualTo(b);
}
@Test
@DisplayName("toString() n'est pas null ni vide")
void toString_nonNullNonVide() {
Organisation o = baseOrganisation();
assertThat(o.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("toString() contient le nom de l'organisation")
void toString_contientNom() {
Organisation o = baseOrganisation();
assertThat(o.toString()).contains("Club Lions Paris");
}
private Organisation buildOrganisation(UUID id, String nom, String email) {
Organisation o = new Organisation();
o.setId(id);
o.setNom(nom);
o.setTypeOrganisation("X");
o.setStatut("ACTIVE");
o.setEmail(email);
return o;
}
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {
UUID id = UUID.randomUUID();
Organisation a = new Organisation();
a.setId(id);
a.setNom("N");
a.setTypeOrganisation("X");
a.setStatut("ACTIVE");
a.setEmail("e@e.com");
Organisation b = new Organisation();
b.setId(id);
b.setNom("N");
b.setTypeOrganisation("X");
b.setStatut("ACTIVE");
b.setEmail("e@e.com");
assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
}
// ─── marquerCommeModifie (hérité de BaseEntity) ──────────────────────────
@Test
@DisplayName("toString non null")
void toString_nonNull() {
Organisation o = new Organisation();
o.setNom("X");
o.setTypeOrganisation("X");
o.setStatut("ACTIVE");
o.setEmail("x@y.com");
assertThat(o.toString()).isNotNull().isNotEmpty();
@Nested
@DisplayName("marquerCommeModifie() — hérité de BaseEntity")
class MarquerCommeModifie {
@Test
@DisplayName("marquerCommeModifie() met à jour dateModification et modifiePar")
void marquerCommeModifie_metAJourChamps() {
Organisation o = baseOrganisation();
o.marquerCommeModifie("operateur@unionflow.dev");
assertThat(o.getDateModification()).isNotNull();
assertThat(o.getModifiePar()).isEqualTo("operateur@unionflow.dev");
}
}
}

View File

@@ -78,4 +78,32 @@ class PaiementObjetTest {
po.setMontantApplique(BigDecimal.ONE);
assertThat(po.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("onCreate initialise dateApplication si null")
void onCreate_setsDateApplicationWhenNull() {
PaiementObjet po = new PaiementObjet();
po.setPaiement(newPaiement());
po.setTypeObjetCible("COTISATION");
po.setObjetCibleId(UUID.randomUUID());
po.setMontantApplique(BigDecimal.ONE);
// dateApplication est null
po.onCreate();
assertThat(po.getDateApplication()).isNotNull();
assertThat(po.getActif()).isTrue();
}
@Test
@DisplayName("onCreate ne remplace pas une dateApplication existante")
void onCreate_doesNotOverrideDateApplication() {
LocalDateTime fixed = LocalDateTime.of(2026, 1, 1, 0, 0);
PaiementObjet po = new PaiementObjet();
po.setDateApplication(fixed);
po.onCreate();
assertThat(po.getDateApplication()).isEqualTo(fixed);
}
}

View File

@@ -3,6 +3,7 @@ package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@@ -44,6 +45,64 @@ class PermissionTest {
assertThat(p.isCodeValide()).isFalse();
}
@Test
@DisplayName("isCodeValide: false si code null")
void isCodeValide_null() {
Permission p = new Permission();
p.setCode(null);
assertThat(p.isCodeValide()).isFalse();
}
@Test
@DisplayName("isCodeValide: false si contient ' > ' mais moins de 3 parties")
void isCodeValide_twoPartsOnly() {
Permission p = new Permission();
p.setCode("A > B");
assertThat(p.isCodeValide()).isFalse();
}
@Test
@DisplayName("onCreate: code null + module/ressource/action renseignés → code généré")
void onCreate_generatesCode_whenNullAndComponentsPresent() throws Exception {
Permission p = new Permission();
p.setCode(null);
p.setModule("org");
p.setRessource("membre");
p.setAction("create");
Method onCreate = Permission.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(p);
assertThat(p.getCode()).isEqualTo("ORG > MEMBRE > CREATE");
}
@Test
@DisplayName("onCreate: code null + composants null → code reste null")
void onCreate_codeRemainsNull_whenComponentsNull() throws Exception {
Permission p = new Permission();
p.setCode(null);
p.setModule(null);
p.setRessource(null);
p.setAction(null);
Method onCreate = Permission.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(p);
assertThat(p.getCode()).isNull();
}
@Test
@DisplayName("onCreate: code déjà renseigné → non écrasé")
void onCreate_existingCode_notOverwritten() throws Exception {
Permission p = new Permission();
p.setCode("X > Y > Z");
p.setModule("other");
p.setRessource("other");
p.setAction("other");
Method onCreate = Permission.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(p);
assertThat(p.getCode()).isEqualTo("X > Y > Z");
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {
@@ -74,4 +133,61 @@ class PermissionTest {
p.setAction("Z");
assertThat(p.toString()).isNotNull().isNotEmpty();
}
// ── Branch coverage manquantes ─────────────────────────────────────────
/**
* L85 branch manquante : code = "" (not null but isEmpty → true) avec composants présents
* → couvre la branche `code != null && code.isEmpty()` (deuxième branche du ||)
*/
@Test
@DisplayName("onCreate: code vide (empty) + composants présents → code généré (branche isEmpty)")
void onCreate_emptyCode_withComponents_generatesCode() throws Exception {
Permission p = new Permission();
p.setCode("");
p.setModule("org");
p.setRessource("membre");
p.setAction("read");
Method onCreate = Permission.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(p);
assertThat(p.getCode()).isEqualTo("ORG > MEMBRE > READ");
}
/**
* L86 branch manquante : module != null, ressource != null, action == null
* → la condition `module != null && ressource != null && action != null` est false (action null)
* → code reste null
*/
@Test
@DisplayName("onCreate: module et ressource présents, action null → code reste null (branche action null)")
void onCreate_moduleAndRessourcePresent_actionNull_codeRemainsNull() throws Exception {
Permission p = new Permission();
p.setCode(null);
p.setModule("org");
p.setRessource("membre");
p.setAction(null);
Method onCreate = Permission.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(p);
assertThat(p.getCode()).isNull();
}
/**
* L86 branch manquante : module != null, ressource == null
* → la condition `module != null && ressource != null && action != null` est false (ressource null)
*/
@Test
@DisplayName("onCreate: module présent, ressource null → code reste null (branche ressource null)")
void onCreate_modulePresent_ressourceNull_codeRemainsNull() throws Exception {
Permission p = new Permission();
p.setCode(null);
p.setModule("org");
p.setRessource(null);
p.setAction("create");
Method onCreate = Permission.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(p);
assertThat(p.getCode()).isNull();
}
}

View File

@@ -0,0 +1,84 @@
package dev.lions.unionflow.server.entity;
import dev.lions.unionflow.server.api.enums.abonnement.StatutSouscription;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests de couverture pour SouscriptionOrganisation.
* Couvre les branches manquantes de onCreate() et decrementerQuota().
*/
@DisplayName("SouscriptionOrganisation - branches manquantes")
class SouscriptionOrganisationBranchTest {
// ── onCreate() ────────────────────────────────────────────────────────────
/**
* Branch manquante : formule != null mais quotaMax est déjà renseigné
* → la ligne `quotaMax = formule.getMaxMembres()` n'est PAS exécutée.
*/
@Test
@DisplayName("onCreate: formule présente mais quotaMax déjà défini → quotaMax conservé")
void onCreate_formuleNonNull_quotaMaxDejaDefini_conserveQuotaMax() throws Exception {
FormuleAbonnement formule = new FormuleAbonnement();
formule.setMaxMembres(50);
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setDateDebut(LocalDate.now());
s.setDateFin(LocalDate.now().plusMonths(1));
s.setFormule(formule);
s.setQuotaMax(100); // déjà défini → ne doit PAS être écrasé
Method onCreate = SouscriptionOrganisation.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(s);
// quotaMax reste 100, pas remplacé par formule.getMaxMembres() (50)
assertThat(s.getQuotaMax()).isEqualTo(100);
// les autres defaults sont positionnés
assertThat(s.getStatut()).isEqualTo(StatutSouscription.ACTIVE);
assertThat(s.getTypePeriode()).isEqualTo(TypePeriodeAbonnement.MENSUEL);
assertThat(s.getQuotaUtilise()).isEqualTo(0);
}
// ── decrementerQuota() ────────────────────────────────────────────────────
/**
* Branch manquante : quotaUtilise == 0 → la condition
* `quotaUtilise != null && quotaUtilise > 0` est false, le quota ne change pas.
*/
@Test
@DisplayName("decrementerQuota: quotaUtilise=0 → reste 0 (pas de décrément)")
void decrementerQuota_quotaUtiliseZero_resterAZero() {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setDateDebut(LocalDate.now());
s.setDateFin(LocalDate.now().plusMonths(1));
s.setQuotaUtilise(0);
s.decrementerQuota();
assertThat(s.getQuotaUtilise()).isEqualTo(0);
}
/**
* Branche couverte pour référence : quotaUtilise null → décrement ignoré.
*/
@Test
@DisplayName("decrementerQuota: quotaUtilise=null → reste null")
void decrementerQuota_quotaUtiliseNull_resterNull() {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setDateDebut(LocalDate.now());
s.setDateFin(LocalDate.now().plusMonths(1));
s.setQuotaUtilise(null);
s.decrementerQuota();
assertThat(s.getQuotaUtilise()).isNull();
}
}

View File

@@ -6,6 +6,7 @@ import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.UUID;
@@ -132,4 +133,113 @@ class SouscriptionOrganisationTest {
s.setDateFin(LocalDate.now().plusYears(1));
assertThat(s.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("isQuotaDepasse: false si quotaMax null")
void isQuotaDepasse_quotaMaxNull_returnsFalse() {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setOrganisation(newOrganisation());
s.setFormule(newFormule());
s.setQuotaMax(null);
s.setQuotaUtilise(100);
assertThat(s.isQuotaDepasse()).isFalse();
}
@Test
@DisplayName("getPlacesRestantes: MAX_VALUE si quotaMax null")
void getPlacesRestantes_quotaMaxNull_returnsMaxInt() {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setOrganisation(newOrganisation());
s.setFormule(newFormule());
s.setQuotaMax(null);
assertThat(s.getPlacesRestantes()).isEqualTo(Integer.MAX_VALUE);
}
@Test
@DisplayName("incrementerQuota: quotaUtilise null → initialisé à 1")
void incrementerQuota_nullQuota_initializesTo1() {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setOrganisation(newOrganisation());
s.setFormule(newFormule());
s.setQuotaUtilise(null);
s.incrementerQuota();
assertThat(s.getQuotaUtilise()).isEqualTo(1);
}
@Test
@DisplayName("decrementerQuota: quotaUtilise=0 → ne descend pas")
void decrementerQuota_zeroQuota_noChange() {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setOrganisation(newOrganisation());
s.setFormule(newFormule());
s.setQuotaUtilise(0);
s.decrementerQuota();
assertThat(s.getQuotaUtilise()).isEqualTo(0);
}
@Test
@DisplayName("isActive: false si statut SUSPENDUE")
void isActive_statusSuspendue_returnsFalse() {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setOrganisation(newOrganisation());
s.setFormule(newFormule());
s.setDateDebut(LocalDate.now().minusMonths(1));
s.setDateFin(LocalDate.now().plusMonths(1));
s.setStatut(StatutSouscription.SUSPENDUE);
assertThat(s.isActive()).isFalse();
}
@Test
@DisplayName("onCreate: initialise les défauts si null, copie quotaMax depuis formule")
void onCreate_setsDefaults() throws Exception {
FormuleAbonnement formule = newFormule(); // maxMembres=100
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setOrganisation(newOrganisation());
s.setFormule(formule);
s.setDateDebut(LocalDate.now());
s.setDateFin(LocalDate.now().plusYears(1));
s.setStatut(null);
s.setTypePeriode(null);
s.setQuotaUtilise(null);
s.setQuotaMax(null);
Method onCreate = SouscriptionOrganisation.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(s);
assertThat(s.getStatut()).isEqualTo(StatutSouscription.ACTIVE);
assertThat(s.getTypePeriode()).isEqualTo(TypePeriodeAbonnement.MENSUEL);
assertThat(s.getQuotaUtilise()).isEqualTo(0);
assertThat(s.getQuotaMax()).isEqualTo(100);
}
// ── Branch coverage manquante ──────────────────────────────────────────
/**
* L118 branch manquante : formule == null → `if (formule != null && quotaMax == null)` → false (court-circuit)
* → quotaMax reste null (ou tel quel), pas de NPE
*/
@Test
@DisplayName("onCreate: formule == null → la branche quotaMax depuis formule n'est pas exécutée")
void onCreate_formulesNull_quotaMaxUnchanged() throws Exception {
SouscriptionOrganisation s = new SouscriptionOrganisation();
s.setOrganisation(newOrganisation());
s.setFormule(null); // formule == null → branche false (court-circuit)
s.setDateDebut(LocalDate.now());
s.setDateFin(LocalDate.now().plusYears(1));
s.setStatut(null);
s.setTypePeriode(null);
s.setQuotaUtilise(null);
s.setQuotaMax(null);
Method onCreate = SouscriptionOrganisation.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(s);
// les défauts sont positionnés, mais quotaMax reste null (formule est null)
assertThat(s.getStatut()).isEqualTo(StatutSouscription.ACTIVE);
assertThat(s.getTypePeriode()).isEqualTo(TypePeriodeAbonnement.MENSUEL);
assertThat(s.getQuotaUtilise()).isEqualTo(0);
assertThat(s.getQuotaMax()).isNull(); // formule null → quotaMax non initialisé
}
}

View File

@@ -3,6 +3,7 @@ package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@@ -26,6 +27,42 @@ class TemplateNotificationTest {
assertThat(t.getLangue()).isEqualTo("fr");
}
@Test
@DisplayName("onCreate: langue null → initialisée à 'fr'")
void onCreate_langue_null_setsDefault() throws Exception {
TemplateNotification t = new TemplateNotification();
t.setCode("T1");
t.setLangue(null);
Method onCreate = TemplateNotification.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(t);
assertThat(t.getLangue()).isEqualTo("fr");
}
@Test
@DisplayName("onCreate: langue vide → initialisée à 'fr'")
void onCreate_langue_empty_setsDefault() throws Exception {
TemplateNotification t = new TemplateNotification();
t.setCode("T2");
t.setLangue("");
Method onCreate = TemplateNotification.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(t);
assertThat(t.getLangue()).isEqualTo("fr");
}
@Test
@DisplayName("onCreate: langue déjà renseignée → non écrasée")
void onCreate_langue_alreadySet_notOverwritten() throws Exception {
TemplateNotification t = new TemplateNotification();
t.setCode("T3");
t.setLangue("en");
Method onCreate = TemplateNotification.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(t);
assertThat(t.getLangue()).isEqualTo("en");
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {

View File

@@ -0,0 +1,322 @@
package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("TransactionApproval")
class TransactionApprovalTest {
private static Organisation newOrganisation() {
Organisation o = new Organisation();
o.setId(UUID.randomUUID());
return o;
}
private static ApproverAction approvedAction() {
ApproverAction a = new ApproverAction();
a.setApproverId(UUID.randomUUID());
a.setApproverName("Approver Test");
a.setApproverRole("TRESORIER");
a.setDecision("APPROVED");
return a;
}
private static ApproverAction pendingAction() {
ApproverAction a = new ApproverAction();
a.setApproverId(UUID.randomUUID());
a.setApproverName("Approver Pending");
a.setApproverRole("PRESIDENT");
a.setDecision("PENDING");
return a;
}
// -------------------------------------------------------------------------
// getRequiredApprovals
// -------------------------------------------------------------------------
@Test
@DisplayName("getRequiredApprovals: NONE renvoie 0")
void getRequiredApprovals_none_returns0() {
TransactionApproval ta = new TransactionApproval();
ta.setRequiredLevel("NONE");
assertThat(ta.getRequiredApprovals()).isEqualTo(0);
}
@Test
@DisplayName("getRequiredApprovals: LEVEL1 renvoie 1")
void getRequiredApprovals_level1_returns1() {
TransactionApproval ta = new TransactionApproval();
ta.setRequiredLevel("LEVEL1");
assertThat(ta.getRequiredApprovals()).isEqualTo(1);
}
@Test
@DisplayName("getRequiredApprovals: LEVEL2 renvoie 2")
void getRequiredApprovals_level2_returns2() {
TransactionApproval ta = new TransactionApproval();
ta.setRequiredLevel("LEVEL2");
assertThat(ta.getRequiredApprovals()).isEqualTo(2);
}
@Test
@DisplayName("getRequiredApprovals: LEVEL3 renvoie 3")
void getRequiredApprovals_level3_returns3() {
TransactionApproval ta = new TransactionApproval();
ta.setRequiredLevel("LEVEL3");
assertThat(ta.getRequiredApprovals()).isEqualTo(3);
}
@Test
@DisplayName("getRequiredApprovals: valeur inconnue renvoie 0 (default)")
void getRequiredApprovals_default_returns0() {
TransactionApproval ta = new TransactionApproval();
ta.setRequiredLevel("INVALID");
assertThat(ta.getRequiredApprovals()).isEqualTo(0);
}
// -------------------------------------------------------------------------
// countApprovals
// -------------------------------------------------------------------------
@Test
@DisplayName("countApprovals: aucun approbateur renvoie 0")
void countApprovals_noApprovers_returns0() {
TransactionApproval ta = TransactionApproval.builder()
.transactionId(UUID.randomUUID())
.transactionType("CONTRIBUTION")
.amount(new BigDecimal("5000.00"))
.requesterId(UUID.randomUUID())
.requesterName("Test Requester")
.requiredLevel("LEVEL1")
.build();
assertThat(ta.countApprovals()).isEqualTo(0);
}
@Test
@DisplayName("countApprovals: seuls les APPROVED sont comptés")
void countApprovals_mixedDecisions_countsApprovedOnly() {
TransactionApproval ta = TransactionApproval.builder()
.transactionId(UUID.randomUUID())
.transactionType("DEPOSIT")
.amount(new BigDecimal("10000.00"))
.requesterId(UUID.randomUUID())
.requesterName("Requester")
.requiredLevel("LEVEL2")
.build();
ta.addApproverAction(approvedAction());
ta.addApproverAction(pendingAction());
ta.addApproverAction(approvedAction());
assertThat(ta.countApprovals()).isEqualTo(2);
}
// -------------------------------------------------------------------------
// hasAllApprovals
// -------------------------------------------------------------------------
@Test
@DisplayName("hasAllApprovals: NONE sans approbateur renvoie toujours true")
void hasAllApprovals_none_alwaysTrue() {
TransactionApproval ta = new TransactionApproval();
ta.setRequiredLevel("NONE");
assertThat(ta.hasAllApprovals()).isTrue();
}
@Test
@DisplayName("hasAllApprovals: LEVEL1 avec une approbation renvoie true")
void hasAllApprovals_level1_oneApproval_true() {
TransactionApproval ta = TransactionApproval.builder()
.transactionId(UUID.randomUUID())
.transactionType("CONTRIBUTION")
.amount(new BigDecimal("1000.00"))
.requesterId(UUID.randomUUID())
.requesterName("Requester")
.requiredLevel("LEVEL1")
.build();
ta.addApproverAction(approvedAction());
assertThat(ta.hasAllApprovals()).isTrue();
}
@Test
@DisplayName("hasAllApprovals: LEVEL2 avec une seule approbation renvoie false")
void hasAllApprovals_level2_onlyOneApproval_false() {
TransactionApproval ta = TransactionApproval.builder()
.transactionId(UUID.randomUUID())
.transactionType("WITHDRAWAL")
.amount(new BigDecimal("50000.00"))
.requesterId(UUID.randomUUID())
.requesterName("Requester")
.requiredLevel("LEVEL2")
.build();
ta.addApproverAction(approvedAction());
assertThat(ta.hasAllApprovals()).isFalse();
}
// -------------------------------------------------------------------------
// addApproverAction
// -------------------------------------------------------------------------
@Test
@DisplayName("addApproverAction: ajoute l'action et positionne le parent")
void addApproverAction_setsParentAndAdds() {
TransactionApproval ta = TransactionApproval.builder()
.transactionId(UUID.randomUUID())
.transactionType("CONTRIBUTION")
.amount(new BigDecimal("2000.00"))
.requesterId(UUID.randomUUID())
.requesterName("Requester")
.requiredLevel("LEVEL1")
.build();
ApproverAction action = pendingAction();
ta.addApproverAction(action);
assertThat(ta.getApprovers()).hasSize(1);
assertThat(action.getApproval()).isSameAs(ta);
}
// -------------------------------------------------------------------------
// isExpired
// -------------------------------------------------------------------------
@Test
@DisplayName("isExpired: expiresAt null renvoie false")
void isExpired_expiresAtNull_returnsFalse() {
TransactionApproval ta = new TransactionApproval();
ta.setExpiresAt(null);
assertThat(ta.isExpired()).isFalse();
}
@Test
@DisplayName("isExpired: expiresAt dans le passé renvoie true")
void isExpired_expiredInPast_returnsTrue() {
TransactionApproval ta = new TransactionApproval();
ta.setExpiresAt(LocalDateTime.now().minusMinutes(1));
assertThat(ta.isExpired()).isTrue();
}
@Test
@DisplayName("isExpired: expiresAt dans le futur renvoie false")
void isExpired_futureExpiry_returnsFalse() {
TransactionApproval ta = new TransactionApproval();
ta.setExpiresAt(LocalDateTime.now().plusDays(1));
assertThat(ta.isExpired()).isFalse();
}
// -------------------------------------------------------------------------
// isPending
// -------------------------------------------------------------------------
@Test
@DisplayName("isPending: statut PENDING renvoie true")
void isPending_pending_returnsTrue() {
TransactionApproval ta = new TransactionApproval();
ta.setStatus("PENDING");
assertThat(ta.isPending()).isTrue();
}
@Test
@DisplayName("isPending: statut APPROVED renvoie false")
void isPending_approved_returnsFalse() {
TransactionApproval ta = new TransactionApproval();
ta.setStatus("APPROVED");
assertThat(ta.isPending()).isFalse();
}
// -------------------------------------------------------------------------
// isCompleted
// -------------------------------------------------------------------------
@Test
@DisplayName("isCompleted: statut VALIDATED renvoie true")
void isCompleted_validated_returnsTrue() {
TransactionApproval ta = new TransactionApproval();
ta.setStatus("VALIDATED");
assertThat(ta.isCompleted()).isTrue();
}
@Test
@DisplayName("isCompleted: statut REJECTED renvoie true")
void isCompleted_rejected_returnsTrue() {
TransactionApproval ta = new TransactionApproval();
ta.setStatus("REJECTED");
assertThat(ta.isCompleted()).isTrue();
}
@Test
@DisplayName("isCompleted: statut CANCELLED renvoie true")
void isCompleted_cancelled_returnsTrue() {
TransactionApproval ta = new TransactionApproval();
ta.setStatus("CANCELLED");
assertThat(ta.isCompleted()).isTrue();
}
@Test
@DisplayName("isCompleted: statut PENDING renvoie false")
void isCompleted_pending_returnsFalse() {
TransactionApproval ta = new TransactionApproval();
ta.setStatus("PENDING");
assertThat(ta.isCompleted()).isFalse();
}
// -------------------------------------------------------------------------
// onCreate (réflexion)
// -------------------------------------------------------------------------
@Test
@DisplayName("onCreate: initialise createdAt, currency, status, expiresAt si null")
void onCreate_initializesNullFields() throws Exception {
TransactionApproval ta = new TransactionApproval();
ta.setCreatedAt(null);
ta.setCurrency(null);
ta.setStatus(null);
ta.setExpiresAt(null);
Method onCreate = TransactionApproval.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(ta);
assertThat(ta.getCreatedAt()).isNotNull();
assertThat(ta.getCurrency()).isEqualTo("XOF");
assertThat(ta.getStatus()).isEqualTo("PENDING");
assertThat(ta.getExpiresAt()).isNotNull();
// expiresAt doit être ~7 jours après createdAt
assertThat(ta.getExpiresAt()).isAfter(ta.getCreatedAt());
assertThat(ta.getExpiresAt()).isBeforeOrEqualTo(ta.getCreatedAt().plusDays(7).plusSeconds(1));
}
@Test
@DisplayName("onCreate: ne remplace pas createdAt si déjà renseigné")
void onCreate_doesNotOverrideExistingCreatedAt() throws Exception {
LocalDateTime existingDate = LocalDateTime.of(2025, 3, 10, 9, 0);
LocalDateTime existingExpiry = LocalDateTime.of(2025, 3, 17, 9, 0);
TransactionApproval ta = new TransactionApproval();
ta.setCreatedAt(existingDate);
ta.setCurrency("EUR");
ta.setStatus("APPROVED");
ta.setExpiresAt(existingExpiry);
Method onCreate = TransactionApproval.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(ta);
assertThat(ta.getCreatedAt()).isEqualTo(existingDate);
assertThat(ta.getCurrency()).isEqualTo("EUR");
assertThat(ta.getStatus()).isEqualTo("APPROVED");
assertThat(ta.getExpiresAt()).isEqualTo(existingExpiry);
}
}

View File

@@ -4,16 +4,22 @@ import dev.lions.unionflow.server.api.enums.wave.StatutCompteWave;
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
import dev.lions.unionflow.server.api.enums.wave.TypeTransactionWave;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("TransactionWave")
@DisplayName("TransactionWave — couverture complète")
class TransactionWaveTest {
// ─── Utilitaire ───────────────────────────────────────────────────────────
private static CompteWave newCompteWave() {
CompteWave c = new CompteWave();
c.setId(UUID.randomUUID());
@@ -22,9 +28,7 @@ class TransactionWaveTest {
return c;
}
@Test
@DisplayName("getters/setters")
void gettersSetters() {
private static TransactionWave baseTransaction() {
TransactionWave t = new TransactionWave();
t.setWaveTransactionId("wave-123");
t.setTypeTransaction(TypeTransactionWave.DEPOT);
@@ -32,78 +36,545 @@ class TransactionWaveTest {
t.setMontant(new BigDecimal("5000.00"));
t.setCodeDevise("XOF");
t.setCompteWave(newCompteWave());
assertThat(t.getWaveTransactionId()).isEqualTo("wave-123");
assertThat(t.getTypeTransaction()).isEqualTo(TypeTransactionWave.DEPOT);
assertThat(t.getStatutTransaction()).isEqualTo(StatutTransactionWave.REUSSIE);
assertThat(t.getMontant()).isEqualByComparingTo("5000.00");
return t;
}
@Test
@DisplayName("isReussie")
void isReussie() {
TransactionWave t = new TransactionWave();
t.setWaveTransactionId("x");
t.setTypeTransaction(TypeTransactionWave.DEPOT);
t.setMontant(BigDecimal.ONE);
t.setCodeDevise("XOF");
t.setCompteWave(newCompteWave());
t.setStatutTransaction(StatutTransactionWave.REUSSIE);
assertThat(t.isReussie()).isTrue();
t.setStatutTransaction(StatutTransactionWave.ECHOUE);
assertThat(t.isReussie()).isFalse();
// ─── Getters / Setters ────────────────────────────────────────────────────
@Nested
@DisplayName("Getters et setters")
class GettersSetters {
@Test
@DisplayName("Champs de base déjà couverts")
void champsDeBase() {
TransactionWave t = baseTransaction();
assertThat(t.getWaveTransactionId()).isEqualTo("wave-123");
assertThat(t.getTypeTransaction()).isEqualTo(TypeTransactionWave.DEPOT);
assertThat(t.getStatutTransaction()).isEqualTo(StatutTransactionWave.REUSSIE);
assertThat(t.getMontant()).isEqualByComparingTo("5000.00");
assertThat(t.getCodeDevise()).isEqualTo("XOF");
assertThat(t.getCompteWave()).isNotNull();
}
@Test
@DisplayName("waveRequestId et waveReference")
void waveRequestIdEtReference() {
TransactionWave t = baseTransaction();
t.setWaveRequestId("req-abc-456");
t.setWaveReference("ref-xyz-789");
assertThat(t.getWaveRequestId()).isEqualTo("req-abc-456");
assertThat(t.getWaveReference()).isEqualTo("ref-xyz-789");
}
@Test
@DisplayName("frais, montantNet")
void fraisEtMontantNet() {
TransactionWave t = baseTransaction();
t.setFrais(new BigDecimal("150.00"));
t.setMontantNet(new BigDecimal("4850.00"));
assertThat(t.getFrais()).isEqualByComparingTo("150.00");
assertThat(t.getMontantNet()).isEqualByComparingTo("4850.00");
}
@Test
@DisplayName("telephonePayeur et telephoneBeneficiaire")
void telephones() {
TransactionWave t = baseTransaction();
t.setTelephonePayeur("+22507111111");
t.setTelephoneBeneficiaire("+22507222222");
assertThat(t.getTelephonePayeur()).isEqualTo("+22507111111");
assertThat(t.getTelephoneBeneficiaire()).isEqualTo("+22507222222");
}
@Test
@DisplayName("metadonnees et reponseWaveApi")
void metadonneesEtReponse() {
TransactionWave t = baseTransaction();
t.setMetadonnees("{\"key\":\"val\"}");
t.setReponseWaveApi("{\"status\":\"success\"}");
assertThat(t.getMetadonnees()).isEqualTo("{\"key\":\"val\"}");
assertThat(t.getReponseWaveApi()).isEqualTo("{\"status\":\"success\"}");
}
@Test
@DisplayName("nombreTentatives, dateDerniereTentative, messageErreur")
void tentativesEtErreur() {
LocalDateTime now = LocalDateTime.now();
TransactionWave t = baseTransaction();
t.setNombreTentatives(3);
t.setDateDerniereTentative(now);
t.setMessageErreur("Délai dépassé");
assertThat(t.getNombreTentatives()).isEqualTo(3);
assertThat(t.getDateDerniereTentative()).isEqualTo(now);
assertThat(t.getMessageErreur()).isEqualTo("Délai dépassé");
}
@Test
@DisplayName("webhooks — liste modifiable")
void webhooks() {
TransactionWave t = baseTransaction();
List<WebhookWave> webhooks = new ArrayList<>();
WebhookWave wh = new WebhookWave();
wh.setWaveEventId("evt-001");
webhooks.add(wh);
t.setWebhooks(webhooks);
assertThat(t.getWebhooks()).hasSize(1);
assertThat(t.getWebhooks().get(0).getWaveEventId()).isEqualTo("evt-001");
}
@Test
@DisplayName("Champs hérités de BaseEntity (id, dateCreation, creePar, modifiePar, version, actif)")
void champsBaseEntity() {
UUID id = UUID.randomUUID();
LocalDateTime now = LocalDateTime.now();
TransactionWave t = baseTransaction();
t.setId(id);
t.setDateCreation(now);
t.setDateModification(now);
t.setCreePar("admin@test.com");
t.setModifiePar("user@test.com");
t.setVersion(1L);
t.setActif(true);
assertThat(t.getId()).isEqualTo(id);
assertThat(t.getDateCreation()).isEqualTo(now);
assertThat(t.getDateModification()).isEqualTo(now);
assertThat(t.getCreePar()).isEqualTo("admin@test.com");
assertThat(t.getModifiePar()).isEqualTo("user@test.com");
assertThat(t.getVersion()).isEqualTo(1L);
assertThat(t.getActif()).isTrue();
}
}
@Test
@DisplayName("peutEtreRetentee")
void peutEtreRetentee() {
TransactionWave t = new TransactionWave();
t.setWaveTransactionId("x");
t.setTypeTransaction(TypeTransactionWave.DEPOT);
t.setMontant(BigDecimal.ONE);
t.setCodeDevise("XOF");
t.setCompteWave(newCompteWave());
t.setStatutTransaction(StatutTransactionWave.ECHOUE);
t.setNombreTentatives(2);
assertThat(t.peutEtreRetentee()).isTrue();
t.setNombreTentatives(5);
assertThat(t.peutEtreRetentee()).isFalse();
// ─── Builder ──────────────────────────────────────────────────────────────
@Nested
@DisplayName("Builder Lombok")
class BuilderTest {
@Test
@DisplayName("Builder avec tous les champs obligatoires")
void builderChampsObligatoires() {
CompteWave compte = newCompteWave();
TransactionWave t = TransactionWave.builder()
.waveTransactionId("builder-wave-001")
.typeTransaction(TypeTransactionWave.PAIEMENT)
.statutTransaction(StatutTransactionWave.EN_ATTENTE)
.montant(new BigDecimal("10000.00"))
.codeDevise("XOF")
.compteWave(compte)
.build();
assertThat(t.getWaveTransactionId()).isEqualTo("builder-wave-001");
assertThat(t.getTypeTransaction()).isEqualTo(TypeTransactionWave.PAIEMENT);
assertThat(t.getStatutTransaction()).isEqualTo(StatutTransactionWave.EN_ATTENTE);
assertThat(t.getMontant()).isEqualByComparingTo("10000.00");
assertThat(t.getCodeDevise()).isEqualTo("XOF");
assertThat(t.getCompteWave()).isEqualTo(compte);
}
@Test
@DisplayName("Builder : valeurs @Builder.Default — statutTransaction=INITIALISE, nombreTentatives=0, webhooks vide")
void builderDefaults() {
CompteWave compte = newCompteWave();
TransactionWave t = TransactionWave.builder()
.waveTransactionId("builder-wave-defaults")
.typeTransaction(TypeTransactionWave.RETRAIT)
.montant(new BigDecimal("500.00"))
.codeDevise("XOF")
.compteWave(compte)
.build();
assertThat(t.getStatutTransaction()).isEqualTo(StatutTransactionWave.INITIALISE);
assertThat(t.getNombreTentatives()).isEqualTo(0);
assertThat(t.getWebhooks()).isNotNull().isEmpty();
}
@Test
@DisplayName("Builder avec tous les champs facultatifs")
void builderChampsOptionnels() {
LocalDateTime now = LocalDateTime.now();
CompteWave compte = newCompteWave();
TransactionWave t = TransactionWave.builder()
.waveTransactionId("builder-wave-full")
.waveRequestId("req-full-001")
.waveReference("ref-full-001")
.typeTransaction(TypeTransactionWave.TRANSFERT)
.statutTransaction(StatutTransactionWave.REUSSIE)
.montant(new BigDecimal("20000.00"))
.frais(new BigDecimal("200.00"))
.montantNet(new BigDecimal("19800.00"))
.codeDevise("XOF")
.telephonePayeur("+22507001001")
.telephoneBeneficiaire("+22507002002")
.metadonnees("{}")
.reponseWaveApi("{\"code\":\"200\"}")
.nombreTentatives(1)
.dateDerniereTentative(now)
.messageErreur(null)
.compteWave(compte)
.build();
assertThat(t.getWaveRequestId()).isEqualTo("req-full-001");
assertThat(t.getWaveReference()).isEqualTo("ref-full-001");
assertThat(t.getFrais()).isEqualByComparingTo("200.00");
assertThat(t.getMontantNet()).isEqualByComparingTo("19800.00");
assertThat(t.getTelephonePayeur()).isEqualTo("+22507001001");
assertThat(t.getTelephoneBeneficiaire()).isEqualTo("+22507002002");
assertThat(t.getMetadonnees()).isEqualTo("{}");
assertThat(t.getReponseWaveApi()).isEqualTo("{\"code\":\"200\"}");
assertThat(t.getNombreTentatives()).isEqualTo(1);
assertThat(t.getDateDerniereTentative()).isEqualTo(now);
assertThat(t.getMessageErreur()).isNull();
}
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {
UUID id = UUID.randomUUID();
CompteWave c = newCompteWave();
TransactionWave a = new TransactionWave();
a.setId(id);
a.setWaveTransactionId("w1");
a.setTypeTransaction(TypeTransactionWave.DEPOT);
a.setStatutTransaction(StatutTransactionWave.REUSSIE);
a.setMontant(BigDecimal.ONE);
a.setCodeDevise("XOF");
a.setCompteWave(c);
TransactionWave b = new TransactionWave();
b.setId(id);
b.setWaveTransactionId("w1");
b.setTypeTransaction(TypeTransactionWave.DEPOT);
b.setStatutTransaction(StatutTransactionWave.REUSSIE);
b.setMontant(BigDecimal.ONE);
b.setCodeDevise("XOF");
b.setCompteWave(c);
assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
// ─── Méthodes métier ──────────────────────────────────────────────────────
@Nested
@DisplayName("Méthodes métier")
class MethodesMetier {
@Test
@DisplayName("isReussie() retourne true si statut=REUSSIE")
void isReussie_statutReussie_retourneTrue() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.REUSSIE);
assertThat(t.isReussie()).isTrue();
}
@Test
@DisplayName("isReussie() retourne false si statut=ECHOUE")
void isReussie_statutEchoue_retourneFalse() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.ECHOUE);
assertThat(t.isReussie()).isFalse();
}
@Test
@DisplayName("isReussie() retourne false si statut=INITIALISE")
void isReussie_statutInitialise_retourneFalse() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
assertThat(t.isReussie()).isFalse();
}
@Test
@DisplayName("peutEtreRetentee() retourne true si statut=ECHOUE et tentatives<5")
void peutEtreRetentee_echoue_tentativesFaibles_retourneTrue() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.ECHOUE);
t.setNombreTentatives(2);
assertThat(t.peutEtreRetentee()).isTrue();
}
@Test
@DisplayName("peutEtreRetentee() retourne true si statut=EXPIRED et tentatives<5")
void peutEtreRetentee_expired_tentativesFaibles_retourneTrue() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.EXPIRED);
t.setNombreTentatives(0);
assertThat(t.peutEtreRetentee()).isTrue();
}
@Test
@DisplayName("peutEtreRetentee() retourne false si tentatives=5")
void peutEtreRetentee_tentativesMax_retourneFalse() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.ECHOUE);
t.setNombreTentatives(5);
assertThat(t.peutEtreRetentee()).isFalse();
}
@Test
@DisplayName("peutEtreRetentee() retourne false si statut=REUSSIE")
void peutEtreRetentee_statutReussie_retourneFalse() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.REUSSIE);
t.setNombreTentatives(1);
assertThat(t.peutEtreRetentee()).isFalse();
}
@Test
@DisplayName("peutEtreRetentee() retourne true si nombreTentatives=null")
void peutEtreRetentee_tentativesNull_retourneTrue() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.ECHOUE);
t.setNombreTentatives(null);
assertThat(t.peutEtreRetentee()).isTrue();
}
@Test
@DisplayName("peutEtreRetentee() retourne false si statut=INITIALISE")
void peutEtreRetentee_statutInitialise_retourneFalse() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setNombreTentatives(1);
assertThat(t.peutEtreRetentee()).isFalse();
}
@Test
@DisplayName("peutEtreRetentee() retourne false si statut=ANNULEE")
void peutEtreRetentee_statutAnnulee_retourneFalse() {
TransactionWave t = baseTransaction();
t.setStatutTransaction(StatutTransactionWave.ANNULEE);
t.setNombreTentatives(0);
assertThat(t.peutEtreRetentee()).isFalse();
}
}
@Test
@DisplayName("toString non null")
void toString_nonNull() {
TransactionWave t = new TransactionWave();
t.setWaveTransactionId("x");
t.setTypeTransaction(TypeTransactionWave.DEPOT);
t.setMontant(BigDecimal.ONE);
t.setCodeDevise("XOF");
t.setCompteWave(newCompteWave());
assertThat(t.toString()).isNotNull().isNotEmpty();
// ─── @PrePersist onCreate ─────────────────────────────────────────────────
@Nested
@DisplayName("@PrePersist onCreate()")
class OnCreate {
@Test
@DisplayName("statutTransaction=null est initialisé à INITIALISE")
void statutTransactionNull_initialiseeAInitialise() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(null);
t.setCodeDevise("XOF");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("1000.00"));
t.onCreate();
assertThat(t.getStatutTransaction()).isEqualTo(StatutTransactionWave.INITIALISE);
}
@Test
@DisplayName("statutTransaction déjà défini n'est pas écrasé")
void statutTransactionDejaDefini_nonEcrase() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.EN_COURS);
t.setCodeDevise("XOF");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("1000.00"));
t.onCreate();
assertThat(t.getStatutTransaction()).isEqualTo(StatutTransactionWave.EN_COURS);
}
@Test
@DisplayName("codeDevise=null est initialisé à XOF")
void codeDeviseNull_initialiseeAXof() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise(null);
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("1000.00"));
t.onCreate();
assertThat(t.getCodeDevise()).isEqualTo("XOF");
}
@Test
@DisplayName("codeDevise vide ('') est initialisé à XOF")
void codeDeviseVide_initialiseeAXof() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("1000.00"));
t.onCreate();
assertThat(t.getCodeDevise()).isEqualTo("XOF");
}
@Test
@DisplayName("codeDevise déjà défini n'est pas écrasé")
void codeDeviseDejaDefini_nonEcrase() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("EUR");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("1000.00"));
t.onCreate();
assertThat(t.getCodeDevise()).isEqualTo("EUR");
}
@Test
@DisplayName("nombreTentatives=null est initialisé à 0")
void nombreTentativesNull_initialiseeAZero() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("XOF");
t.setNombreTentatives(null);
t.setMontant(new BigDecimal("1000.00"));
t.onCreate();
assertThat(t.getNombreTentatives()).isEqualTo(0);
}
@Test
@DisplayName("nombreTentatives déjà défini n'est pas écrasé")
void nombreTentativesDejaDefini_nonEcrase() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("XOF");
t.setNombreTentatives(3);
t.setMontant(new BigDecimal("1000.00"));
t.onCreate();
assertThat(t.getNombreTentatives()).isEqualTo(3);
}
@Test
@DisplayName("montantNet=null est calculé quand montant et frais sont définis")
void montantNetNull_calculeDepuisMontantEtFrais() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("XOF");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("5000.00"));
t.setFrais(new BigDecimal("150.00"));
t.setMontantNet(null);
t.onCreate();
assertThat(t.getMontantNet()).isEqualByComparingTo("4850.00");
}
@Test
@DisplayName("montantNet=null n'est pas calculé si frais=null")
void montantNetNull_nonCalculeSiFraisNull() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("XOF");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("5000.00"));
t.setFrais(null);
t.setMontantNet(null);
t.onCreate();
assertThat(t.getMontantNet()).isNull();
}
@Test
@DisplayName("montantNet=null n'est pas calculé si montant=null")
void montantNetNull_nonCalculeSiMontantNull() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("XOF");
t.setNombreTentatives(0);
t.setMontant(null);
t.setFrais(new BigDecimal("150.00"));
t.setMontantNet(null);
t.onCreate();
assertThat(t.getMontantNet()).isNull();
}
@Test
@DisplayName("montantNet déjà défini n'est pas recalculé")
void montantNetDejaDefini_nonRecalcule() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("XOF");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("5000.00"));
t.setFrais(new BigDecimal("150.00"));
t.setMontantNet(new BigDecimal("9999.00"));
t.onCreate();
assertThat(t.getMontantNet()).isEqualByComparingTo("9999.00");
}
@Test
@DisplayName("onCreate() initialise aussi dateCreation et actif via BaseEntity")
void onCreateInitialiseBaseEntity() {
TransactionWave t = new TransactionWave();
t.setStatutTransaction(StatutTransactionWave.INITIALISE);
t.setCodeDevise("XOF");
t.setNombreTentatives(0);
t.setMontant(new BigDecimal("1000.00"));
t.setDateCreation(null);
t.setActif(null);
t.onCreate();
assertThat(t.getDateCreation()).isNotNull();
assertThat(t.getActif()).isTrue();
}
}
// ─── Equals / HashCode / toString ────────────────────────────────────────
@Nested
@DisplayName("equals, hashCode et toString")
class EgaliteEtToString {
@Test
@DisplayName("Deux instances avec le même id sont égales")
void equals_memeId_egales() {
UUID id = UUID.randomUUID();
CompteWave c = newCompteWave();
TransactionWave a = buildTransaction(id, "w1", c);
TransactionWave b = buildTransaction(id, "w1", c);
assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
}
@Test
@DisplayName("Deux instances avec des id différents ne sont pas égales")
void equals_idsDifferents_pasEgales() {
TransactionWave a = buildTransaction(UUID.randomUUID(), "w1", newCompteWave());
TransactionWave b = buildTransaction(UUID.randomUUID(), "w2", newCompteWave());
assertThat(a).isNotEqualTo(b);
}
@Test
@DisplayName("toString() n'est pas null ni vide")
void toString_nonNullNonVide() {
TransactionWave t = baseTransaction();
assertThat(t.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("toString() contient le waveTransactionId")
void toString_contientWaveTransactionId() {
TransactionWave t = baseTransaction();
t.setWaveTransactionId("wave-toString-test");
assertThat(t.toString()).contains("wave-toString-test");
}
private TransactionWave buildTransaction(UUID id, String waveId, CompteWave compte) {
TransactionWave t = new TransactionWave();
t.setId(id);
t.setWaveTransactionId(waveId);
t.setTypeTransaction(TypeTransactionWave.DEPOT);
t.setStatutTransaction(StatutTransactionWave.REUSSIE);
t.setMontant(BigDecimal.ONE);
t.setCodeDevise("XOF");
t.setCompteWave(compte);
return t;
}
}
// ─── marquerCommeModifie (hérité de BaseEntity) ──────────────────────────
@Nested
@DisplayName("marquerCommeModifie() — hérité de BaseEntity")
class MarquerCommeModifie {
@Test
@DisplayName("marquerCommeModifie() met à jour dateModification et modifiePar")
void marquerCommeModifie_metAJourChamps() {
TransactionWave t = baseTransaction();
t.marquerCommeModifie("agent@unionflow.dev");
assertThat(t.getDateModification()).isNotNull();
assertThat(t.getModifiePar()).isEqualTo("agent@unionflow.dev");
}
}
}

View File

@@ -3,6 +3,7 @@ package dev.lions.unionflow.server.entity;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
@@ -29,6 +30,34 @@ class TypeReferenceTest {
assertThat(tr.getEstDefaut()).isTrue();
}
@Test
@DisplayName("onCreate: domaine et code non-null → normalisés en majuscules")
void onCreate_normalisesDomaineAndCode() throws Exception {
TypeReference tr = new TypeReference();
tr.setDomaine("statut_org");
tr.setCode("active");
tr.setLibelle("Actif");
Method onCreate = TypeReference.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(tr);
assertThat(tr.getDomaine()).isEqualTo("STATUT_ORG");
assertThat(tr.getCode()).isEqualTo("ACTIVE");
}
@Test
@DisplayName("onCreate: domaine null → skipped (no NPE), code null → skipped")
void onCreate_nullDomaineAndCode_skipped() throws Exception {
TypeReference tr = new TypeReference();
tr.setDomaine(null);
tr.setCode(null);
tr.setLibelle("L");
Method onCreate = TypeReference.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(tr);
assertThat(tr.getDomaine()).isNull();
assertThat(tr.getCode()).isNull();
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {

View File

@@ -6,6 +6,8 @@ import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;
@@ -83,4 +85,69 @@ class ValidationEtapeDemandeTest {
v.setStatut(StatutValidationEtape.EN_ATTENTE);
assertThat(v.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("estFinalisee: true pour REJETEE")
void estFinalisee_rejetee() {
ValidationEtapeDemande v = new ValidationEtapeDemande();
v.setDemandeAide(newDemandeAide());
v.setEtapeNumero(1);
v.setStatut(StatutValidationEtape.REJETEE);
assertThat(v.estFinalisee()).isTrue();
}
@Test
@DisplayName("estFinalisee: true pour DELEGUEE")
void estFinalisee_deleguee() {
ValidationEtapeDemande v = new ValidationEtapeDemande();
v.setDemandeAide(newDemandeAide());
v.setEtapeNumero(1);
v.setStatut(StatutValidationEtape.DELEGUEE);
assertThat(v.estFinalisee()).isTrue();
}
@Test
@DisplayName("estFinalisee: true pour EXPIREE")
void estFinalisee_expiree() {
ValidationEtapeDemande v = new ValidationEtapeDemande();
v.setDemandeAide(newDemandeAide());
v.setEtapeNumero(1);
v.setStatut(StatutValidationEtape.EXPIREE);
assertThat(v.estFinalisee()).isTrue();
}
@Test
@DisplayName("onCreate (PrePersist) - statut null est initialisé à EN_ATTENTE via réflexion")
void onCreate_withNullStatut_setsEnAttente() throws Exception {
ValidationEtapeDemande v = new ValidationEtapeDemande();
v.setDemandeAide(newDemandeAide());
v.setEtapeNumero(1);
// Forcer statut à null via le champ Lombok
Field statutField = ValidationEtapeDemande.class.getDeclaredField("statut");
statutField.setAccessible(true);
statutField.set(v, null);
assertThat(v.getStatut()).isNull();
Method onCreate = ValidationEtapeDemande.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(v);
assertThat(v.getStatut()).isEqualTo(StatutValidationEtape.EN_ATTENTE);
}
@Test
@DisplayName("onCreate (PrePersist) - statut déjà défini n'est pas écrasé")
void onCreate_withExistingStatut_keepsStatut() throws Exception {
ValidationEtapeDemande v = new ValidationEtapeDemande();
v.setDemandeAide(newDemandeAide());
v.setEtapeNumero(1);
v.setStatut(StatutValidationEtape.APPROUVEE);
Method onCreate = ValidationEtapeDemande.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(v);
assertThat(v.getStatut()).isEqualTo(StatutValidationEtape.APPROUVEE);
}
}

View File

@@ -4,6 +4,7 @@ import dev.lions.unionflow.server.api.enums.wave.StatutWebhook;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;
@@ -51,6 +52,16 @@ class WebhookWaveTest {
assertThat(w.peutEtreRetente()).isFalse();
}
@Test
@DisplayName("peutEtreRetente: false si statut TRAITE (ni ECHOUE ni EN_ATTENTE)")
void peutEtreRetente_false_whenTraite() {
WebhookWave w = new WebhookWave();
w.setWaveEventId("x");
w.setStatutTraitement(StatutWebhook.TRAITE.name());
w.setNombreTentatives(1);
assertThat(w.peutEtreRetente()).isFalse();
}
@Test
@DisplayName("equals et hashCode")
void equalsHashCode() {
@@ -75,4 +86,32 @@ class WebhookWaveTest {
w.setStatutTraitement(StatutWebhook.EN_ATTENTE.name());
assertThat(w.toString()).isNotNull().isNotEmpty();
}
@Test
@DisplayName("peutEtreRetente: true si nombreTentatives null et EN_ATTENTE")
void peutEtreRetente_nombreTentativesNull_returnsTrue() {
WebhookWave w = new WebhookWave();
w.setWaveEventId("x");
w.setStatutTraitement(StatutWebhook.EN_ATTENTE.name());
w.setNombreTentatives(null);
assertThat(w.peutEtreRetente()).isTrue();
}
@Test
@DisplayName("onCreate: initialise les défauts si null")
void onCreate_setsDefaults() throws Exception {
WebhookWave w = new WebhookWave();
w.setWaveEventId("evt-onc");
w.setStatutTraitement(null);
w.setDateReception(null);
w.setNombreTentatives(null);
Method onCreate = WebhookWave.class.getDeclaredMethod("onCreate");
onCreate.setAccessible(true);
onCreate.invoke(w);
assertThat(w.getStatutTraitement()).isEqualTo(StatutWebhook.EN_ATTENTE.name());
assertThat(w.getDateReception()).isNotNull();
assertThat(w.getNombreTentatives()).isEqualTo(0);
}
}

View File

@@ -48,16 +48,19 @@ class CampagneCollecteTest {
void equalsHashCode() {
UUID id = UUID.randomUUID();
Organisation o = newOrganisation();
LocalDateTime dateOuverture = LocalDateTime.of(2026, 1, 1, 0, 0, 0);
CampagneCollecte a = new CampagneCollecte();
a.setId(id);
a.setOrganisation(o);
a.setTitre("T");
a.setStatut(StatutCampagneCollecte.BROUILLON);
a.setDateOuverture(dateOuverture);
CampagneCollecte b = new CampagneCollecte();
b.setId(id);
b.setOrganisation(o);
b.setTitre("T");
b.setStatut(StatutCampagneCollecte.BROUILLON);
b.setDateOuverture(dateOuverture);
assertThat(a).isEqualTo(b);
assertThat(a.hashCode()).isEqualTo(b.hashCode());
}

View File

@@ -2,10 +2,16 @@ package dev.lions.unionflow.server.entity.listener;
import dev.lions.unionflow.server.entity.Adresse;
import dev.lions.unionflow.server.entity.BaseEntity;
import dev.lions.unionflow.server.service.KeycloakService;
import io.quarkus.arc.Arc;
import io.quarkus.arc.ArcContainer;
import io.quarkus.arc.InstanceHandle;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;
/**
* Tests unitaires pour AuditEntityListener (avantCreation, avantModification, toutes les branches).
@@ -80,4 +86,129 @@ class AuditEntityListenerTest {
assertThat(entity.getModifiePar()).isNotNull();
}
@Test
@SuppressWarnings("unchecked")
@DisplayName("avantCreation: retourne l'email quand KeycloakService authentifié et email non vide")
void avantCreation_authenticated_withEmail_setsEmail() {
ArcContainer mockContainer = mock(ArcContainer.class);
InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
KeycloakService mockService = mock(KeycloakService.class);
when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
when(mockHandle.get()).thenReturn(mockService);
when(mockService.isAuthenticated()).thenReturn(true);
when(mockService.getCurrentUserEmail()).thenReturn("user@example.com");
try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
arcMock.when(Arc::container).thenReturn(mockContainer);
BaseEntity entity = new Adresse();
entity.setCreePar(null);
new AuditEntityListener().avantCreation(entity);
assertThat(entity.getCreePar()).isEqualTo("user@example.com");
}
}
// -----------------------------------------------------------------------
// Branches manquantes dans obtenirUtilisateurCourant()
// -----------------------------------------------------------------------
@Test
@SuppressWarnings("unchecked")
@DisplayName("obtenirUtilisateurCourant: keycloakService null → retourne 'system' (L92 branche keycloakService==null)")
void avantCreation_keycloakServiceNull_returnsSystem() {
ArcContainer mockContainer = mock(ArcContainer.class);
InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
// get() retourne null → L92 condition (keycloakService != null) = false → retourne "system"
when(mockHandle.get()).thenReturn(null);
try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
arcMock.when(Arc::container).thenReturn(mockContainer);
BaseEntity entity = new Adresse();
entity.setCreePar(null);
new AuditEntityListener().avantCreation(entity);
// keycloakService null → fallback "system"
assertThat(entity.getCreePar()).isEqualTo("system");
}
}
@Test
@SuppressWarnings("unchecked")
@DisplayName("obtenirUtilisateurCourant: authentifié mais email blank → retourne 'system' (L95 branche email.isBlank)")
void avantCreation_authenticated_blankEmail_returnsSystem() {
ArcContainer mockContainer = mock(ArcContainer.class);
InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
KeycloakService mockService = mock(KeycloakService.class);
when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
when(mockHandle.get()).thenReturn(mockService);
when(mockService.isAuthenticated()).thenReturn(true);
// email blank → L95 condition !email.isBlank() = false → ne retourne pas email → tombe sur "system"
when(mockService.getCurrentUserEmail()).thenReturn(" ");
try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
arcMock.when(Arc::container).thenReturn(mockContainer);
BaseEntity entity = new Adresse();
entity.setCreePar(null);
new AuditEntityListener().avantCreation(entity);
assertThat(entity.getCreePar()).isEqualTo("system");
}
}
@Test
@SuppressWarnings("unchecked")
@DisplayName("obtenirUtilisateurCourant: authentifié mais email null → retourne 'system' (L95 branche email==null)")
void avantCreation_authenticated_nullEmail_returnsSystem() {
ArcContainer mockContainer = mock(ArcContainer.class);
InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
KeycloakService mockService = mock(KeycloakService.class);
when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
when(mockHandle.get()).thenReturn(mockService);
when(mockService.isAuthenticated()).thenReturn(true);
// email null → L95 condition (email != null && !email.isBlank()) = false → retourne "system"
when(mockService.getCurrentUserEmail()).thenReturn(null);
try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
arcMock.when(Arc::container).thenReturn(mockContainer);
BaseEntity entity = new Adresse();
entity.setCreePar(null);
new AuditEntityListener().avantCreation(entity);
assertThat(entity.getCreePar()).isEqualTo("system");
}
}
@Test
@SuppressWarnings("unchecked")
@DisplayName("obtenirUtilisateurCourant: non authentifié → retourne 'system' (L93 branche isAuthenticated=false)")
void avantCreation_notAuthenticated_returnsSystem() {
ArcContainer mockContainer = mock(ArcContainer.class);
InstanceHandle<KeycloakService> mockHandle = mock(InstanceHandle.class);
KeycloakService mockService = mock(KeycloakService.class);
when(mockContainer.instance(KeycloakService.class)).thenReturn(mockHandle);
when(mockHandle.get()).thenReturn(mockService);
// isAuthenticated=false → L93 condition false → ne rentre pas dans le if → retourne "system"
when(mockService.isAuthenticated()).thenReturn(false);
try (MockedStatic<Arc> arcMock = mockStatic(Arc.class)) {
arcMock.when(Arc::container).thenReturn(mockContainer);
BaseEntity entity = new Adresse();
entity.setCreePar(null);
new AuditEntityListener().avantCreation(entity);
assertThat(entity.getCreePar()).isEqualTo("system");
}
}
}