package dev.lions.unionflow.server.service;
import static org.assertj.core.api.Assertions.assertThat;
import dev.lions.unionflow.server.entity.Adresse;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.MembreOrganisation;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.MembreRoleRepository;
import dev.lions.unionflow.server.repository.TypeReferenceRepository;
import dev.lions.unionflow.server.service.MembreImportExportService;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
* Tests ciblant les lambdas internes de {@code MembreService.calculateSearchStatistics}
* (ligne 697-718) via réflexion.
*
*
Ces lambdas ne sont pas atteintes par {@link MembreServiceTest} car tous les membres
* fixture y ont des listes vides ({@code membresOrganisations} et {@code adresses}).
*/
@QuarkusTest
class MembreServiceSearchStatsLambdaTest {
@Inject
MembreService membreService;
@InjectMock
MembreRepository membreRepository;
@InjectMock
MembreRoleRepository membreRoleRepository;
@InjectMock
TypeReferenceRepository typeReferenceRepository;
@InjectMock
MembreImportExportService membreImportExportService;
@InjectMock
OrganisationService organisationService;
@InjectMock
SecurityIdentity securityIdentity;
// =========================================================================
// Helpers
// =========================================================================
private Object invokeCalculerStatistiques(List membres) throws Exception {
Method method = MembreService.class.getDeclaredMethod("calculateSearchStatistics", List.class);
method.setAccessible(true);
return method.invoke(membreService, membres);
}
// =========================================================================
// Lambda ligne 697-703 : flatMap membresOrganisations + map organisation.getId()
// =========================================================================
@Test
@DisplayName("calculateSearchStatistics avec membresOrganisations non-null et organisation non-null couvre lambda$11 ligne 700")
void calculateSearchStatistics_membresOrgNonNull_withOrg_coversMapOrgIdLambda() throws Exception {
// Organisation avec un ID non-null
Organisation org = new Organisation();
org.setId(UUID.randomUUID());
MembreOrganisation mo = new MembreOrganisation();
mo.setOrganisation(org);
Membre membre = new Membre();
membre.setId(UUID.randomUUID());
membre.setNom("Diallo");
membre.setPrenom("Amadou");
membre.setActif(true);
membre.setDateNaissance(LocalDate.of(1990, 1, 1));
membre.setMembresOrganisations(List.of(mo));
membre.setAdresses(new ArrayList<>());
Object stats = invokeCalculerStatistiques(List.of(membre));
assertThat(stats).isNotNull();
}
@Test
@DisplayName("calculateSearchStatistics avec MembreOrganisation.organisation null couvre branche false lambda ligne 700")
void calculateSearchStatistics_membresOrgNonNull_orgNull_coversNullOrgBranch() throws Exception {
// MembreOrganisation avec organisation null → map retourne null → filter(Objects::nonNull) retire
MembreOrganisation moNullOrg = new MembreOrganisation();
moNullOrg.setOrganisation(null);
Membre membre = new Membre();
membre.setId(UUID.randomUUID());
membre.setNom("Bah");
membre.setPrenom("Isatou");
membre.setActif(true);
membre.setDateNaissance(LocalDate.of(1992, 5, 15));
membre.setMembresOrganisations(List.of(moNullOrg));
membre.setAdresses(new ArrayList<>());
Object stats = invokeCalculerStatistiques(List.of(membre));
assertThat(stats).isNotNull();
}
@Test
@DisplayName("calculateSearchStatistics avec membresOrganisations null couvre branche false flatMap ligne 697")
void calculateSearchStatistics_membresOrgNull_coversStreamEmptyBranch() throws Exception {
Membre membre = new Membre();
membre.setId(UUID.randomUUID());
membre.setNom("Camara");
membre.setPrenom("Fatou");
membre.setActif(false);
membre.setDateNaissance(null);
membre.setMembresOrganisations(null); // → Stream.empty() branch
membre.setAdresses(new ArrayList<>());
Object stats = invokeCalculerStatistiques(List.of(membre));
assertThat(stats).isNotNull();
}
// =========================================================================
// Lambda ligne 714-718 : flatMap adresses + map region + filter non-null non-empty
// =========================================================================
@Test
@DisplayName("calculateSearchStatistics avec adresses non-null et region non-vide couvre lambdas ligne 714-716")
void calculateSearchStatistics_adressesNonNull_withRegion_coversAdresseLambdas() throws Exception {
Adresse adresse = new Adresse();
adresse.setRegion("Dakar");
adresse.setTypeAdresse("DOMICILE");
Membre membre = new Membre();
membre.setId(UUID.randomUUID());
membre.setNom("Sow");
membre.setPrenom("Mariama");
membre.setActif(true);
membre.setDateNaissance(LocalDate.of(1995, 3, 20));
membre.setMembresOrganisations(new ArrayList<>());
membre.setAdresses(List.of(adresse));
Object stats = invokeCalculerStatistiques(List.of(membre));
assertThat(stats).isNotNull();
}
@Test
@DisplayName("calculateSearchStatistics avec adresses null couvre branche false flatMap ligne 714")
void calculateSearchStatistics_adressesNull_coversStreamEmptyBranch() throws Exception {
Membre membre = new Membre();
membre.setId(UUID.randomUUID());
membre.setNom("Keita");
membre.setPrenom("Awa");
membre.setActif(true);
membre.setDateNaissance(LocalDate.of(1988, 7, 10));
membre.setMembresOrganisations(new ArrayList<>());
membre.setAdresses(null); // → Stream.empty() branch at line 714
Object stats = invokeCalculerStatistiques(List.of(membre));
assertThat(stats).isNotNull();
}
@Test
@DisplayName("calculateSearchStatistics avec adresse region null couvre filtre non-null ligne 716")
void calculateSearchStatistics_adressesWithNullRegion_coversFilterNullRegion() throws Exception {
Adresse adresseNullRegion = new Adresse();
adresseNullRegion.setRegion(null); // → filtrée par filter(r -> r != null && !r.isEmpty())
adresseNullRegion.setTypeAdresse("DOMICILE");
Membre membre = new Membre();
membre.setId(UUID.randomUUID());
membre.setNom("Traore");
membre.setPrenom("Hawa");
membre.setActif(true);
membre.setDateNaissance(LocalDate.of(1993, 11, 5));
membre.setMembresOrganisations(new ArrayList<>());
membre.setAdresses(List.of(adresseNullRegion));
Object stats = invokeCalculerStatistiques(List.of(membre));
assertThat(stats).isNotNull();
}
// =========================================================================
// Vérification que la liste vide déclenche le chemin early-return
// =========================================================================
@Test
@DisplayName("calculateSearchStatistics avec liste vide retourne des statistiques à 0")
void calculateSearchStatistics_emptyList_returnsZeroStats() throws Exception {
Object stats = invokeCalculerStatistiques(Collections.emptyList());
assertThat(stats).isNotNull();
}
// =========================================================================
// L710: filter(r -> r != null && !r.isEmpty()) — branche r != null && r.isEmpty() (vide → filtre)
// La branche r==null est couverte par calculateSearchStatistics_adressesWithNullRegion_coversFilterNullRegion
// La branche r!= null && isEmpty() manque — couverte par region="" (chaîne vide)
// =========================================================================
@Test
@DisplayName("calculateSearchStatistics avec adresse region vide → L710 filter r.isEmpty() true → filtrée")
void calculateSearchStatistics_adressesWithEmptyRegion_L710FilterEmptyTrue() throws Exception {
// region = "" → r != null = true, r.isEmpty() = true → !r.isEmpty() = false → filtrée
Adresse adresseEmptyRegion = new Adresse();
adresseEmptyRegion.setRegion(""); // vide → filter: r != null && !isEmpty() → false → filtrée
adresseEmptyRegion.setTypeAdresse("DOMICILE");
Membre membre = new Membre();
membre.setId(UUID.randomUUID());
membre.setNom("Coulibaly");
membre.setPrenom("Fatoumata");
membre.setActif(true);
membre.setDateNaissance(LocalDate.of(1995, 3, 10));
membre.setMembresOrganisations(new ArrayList<>());
membre.setAdresses(List.of(adresseEmptyRegion));
Object stats = invokeCalculerStatistiques(List.of(membre));
assertThat(stats).isNotNull();
}
}