2235 lines
94 KiB
Java
2235 lines
94 KiB
Java
package dev.lions.unionflow.server.service;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
import static org.mockito.ArgumentMatchers.any;
|
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
import static org.mockito.ArgumentMatchers.anyString;
|
|
import static org.mockito.ArgumentMatchers.eq;
|
|
import static org.mockito.Mockito.doNothing;
|
|
import static org.mockito.Mockito.doReturn;
|
|
import static org.mockito.Mockito.never;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
import dev.lions.unionflow.server.api.dto.membre.request.CreateMembreRequest;
|
|
import dev.lions.unionflow.server.api.dto.membre.request.UpdateMembreRequest;
|
|
import dev.lions.unionflow.server.api.dto.membre.response.MembreResponse;
|
|
import dev.lions.unionflow.server.api.dto.membre.response.MembreSummaryResponse;
|
|
import dev.lions.unionflow.server.entity.Membre;
|
|
import dev.lions.unionflow.server.entity.MembreOrganisation;
|
|
import dev.lions.unionflow.server.entity.MembreRole;
|
|
import dev.lions.unionflow.server.entity.Organisation;
|
|
import dev.lions.unionflow.server.entity.Role;
|
|
import dev.lions.unionflow.server.repository.MembreRepository;
|
|
import dev.lions.unionflow.server.repository.MembreRoleRepository;
|
|
import dev.lions.unionflow.server.repository.TypeReferenceRepository;
|
|
import io.quarkus.panache.common.Page;
|
|
import io.quarkus.panache.common.Sort;
|
|
import io.quarkus.security.identity.SecurityIdentity;
|
|
import io.quarkus.test.InjectMock;
|
|
import io.quarkus.test.junit.QuarkusTest;
|
|
import io.quarkus.test.junit.mockito.InjectSpy;
|
|
import io.quarkus.test.TestTransaction;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.persistence.EntityManager;
|
|
import java.security.Principal;
|
|
import java.time.LocalDate;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.DisplayName;
|
|
import org.junit.jupiter.api.Nested;
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
@QuarkusTest
|
|
class MembreServiceTest {
|
|
|
|
@Inject
|
|
MembreService membreService;
|
|
|
|
@InjectSpy
|
|
MembreRepository membreRepository;
|
|
|
|
@Inject
|
|
EntityManager em;
|
|
|
|
@InjectMock
|
|
MembreRoleRepository membreRoleRepository;
|
|
|
|
@InjectMock
|
|
TypeReferenceRepository typeReferenceRepository;
|
|
|
|
@InjectMock
|
|
MembreImportExportService membreImportExportService;
|
|
|
|
@InjectMock
|
|
OrganisationService organisationService;
|
|
|
|
@InjectMock
|
|
SecurityIdentity securityIdentity;
|
|
|
|
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
|
|
private Membre membreFixture(String email) {
|
|
Membre m = new Membre();
|
|
m.setId(UUID.randomUUID());
|
|
m.setEmail(email);
|
|
m.setNom("Doe");
|
|
m.setPrenom("John");
|
|
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
m.setNumeroMembre("UF2024-ABCD1234");
|
|
m.setActif(true);
|
|
m.setStatutCompte("ACTIF");
|
|
m.setVersion(0L);
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
m.setAdresses(new ArrayList<>());
|
|
return m;
|
|
}
|
|
|
|
/** By default, securityIdentity behaves as anonymous (no ADMIN_ORGANISATION). */
|
|
@BeforeEach
|
|
void setupDefaultSecurity() {
|
|
Principal principal = () -> "anonymous";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of());
|
|
}
|
|
|
|
// =========================================================================
|
|
// creerMembre
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("creerMembre")
|
|
class CreerMembreTests {
|
|
|
|
@Test
|
|
@DisplayName("Happy path: numéro généré automatiquement, statut EN_ATTENTE_VALIDATION")
|
|
void creerMembre_generatesNumeroAndSetsEnAttenteValidation() {
|
|
Membre membre = new Membre();
|
|
membre.setEmail("test@unionflow.dev");
|
|
membre.setNom("Doe");
|
|
membre.setPrenom("John");
|
|
membre.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
|
|
when(membreRepository.findByEmail("test@unionflow.dev")).thenReturn(Optional.empty());
|
|
when(membreRepository.findByNumeroMembre(any())).thenReturn(Optional.empty());
|
|
doNothing().when(membreRepository).persist(any(Membre.class));
|
|
|
|
Membre created = membreService.creerMembre(membre);
|
|
|
|
assertThat(created.getNumeroMembre()).startsWith("UF");
|
|
assertThat(created.getStatutCompte()).isEqualTo("EN_ATTENTE_VALIDATION");
|
|
assertThat(created.getActif()).isFalse();
|
|
verify(membreRepository).persist(membre);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Numéro déjà fourni: conservé tel quel")
|
|
void creerMembre_withExistingNumero_keepsIt() {
|
|
Membre membre = new Membre();
|
|
membre.setEmail("test2@unionflow.dev");
|
|
membre.setNom("Smith");
|
|
membre.setPrenom("Jane");
|
|
membre.setNumeroMembre("UF2024-CUSTOM");
|
|
membre.setDateNaissance(LocalDate.of(1985, 5, 15));
|
|
|
|
when(membreRepository.findByEmail(anyString())).thenReturn(Optional.empty());
|
|
when(membreRepository.findByNumeroMembre("UF2024-CUSTOM")).thenReturn(Optional.empty());
|
|
doNothing().when(membreRepository).persist(any(Membre.class));
|
|
|
|
Membre created = membreService.creerMembre(membre);
|
|
|
|
assertThat(created.getNumeroMembre()).isEqualTo("UF2024-CUSTOM");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Date de naissance null: valeur par défaut 18 ans")
|
|
void creerMembre_nullDateNaissance_setsDefault() {
|
|
Membre membre = new Membre();
|
|
membre.setEmail("nodate@unionflow.dev");
|
|
membre.setNom("NoDate");
|
|
membre.setPrenom("User");
|
|
// dateNaissance = null intentionally
|
|
|
|
when(membreRepository.findByEmail(anyString())).thenReturn(Optional.empty());
|
|
when(membreRepository.findByNumeroMembre(any())).thenReturn(Optional.empty());
|
|
doNothing().when(membreRepository).persist(any(Membre.class));
|
|
|
|
Membre created = membreService.creerMembre(membre);
|
|
|
|
assertThat(created.getDateNaissance()).isNotNull();
|
|
assertThat(created.getDateNaissance()).isEqualTo(LocalDate.now().minusYears(18));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Email déjà existant: lève IllegalArgumentException")
|
|
void creerMembre_duplicateEmail_throws() {
|
|
Membre membre = new Membre();
|
|
membre.setEmail("dup@unionflow.dev");
|
|
membre.setNom("Dup");
|
|
membre.setPrenom("User");
|
|
|
|
when(membreRepository.findByEmail("dup@unionflow.dev"))
|
|
.thenReturn(Optional.of(membreFixture("dup@unionflow.dev")));
|
|
|
|
assertThatThrownBy(() -> membreService.creerMembre(membre))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("email existe déjà");
|
|
|
|
verify(membreRepository, never()).persist(any(Membre.class));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Numéro de membre déjà existant: lève IllegalArgumentException")
|
|
void creerMembre_duplicateNumero_throws() {
|
|
Membre membre = new Membre();
|
|
membre.setEmail("unique@unionflow.dev");
|
|
membre.setNom("Unique");
|
|
membre.setPrenom("User");
|
|
membre.setNumeroMembre("UF2024-EXIST");
|
|
|
|
when(membreRepository.findByEmail("unique@unionflow.dev")).thenReturn(Optional.empty());
|
|
when(membreRepository.findByNumeroMembre("UF2024-EXIST"))
|
|
.thenReturn(Optional.of(membreFixture("other@unionflow.dev")));
|
|
|
|
assertThatThrownBy(() -> membreService.creerMembre(membre))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("numéro existe déjà");
|
|
|
|
verify(membreRepository, never()).persist(any(Membre.class));
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// activerMembre
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("activerMembre")
|
|
class ActiverMembreTests {
|
|
|
|
@Test
|
|
@DisplayName("Happy path: membre EN_ATTENTE_VALIDATION → ACTIF + actif=true")
|
|
void activerMembre_pendingMember_setsActif() {
|
|
UUID id = UUID.randomUUID();
|
|
Membre membre = membreFixture("pending@unionflow.dev");
|
|
membre.setId(id);
|
|
membre.setStatutCompte("EN_ATTENTE_VALIDATION");
|
|
membre.setActif(false);
|
|
|
|
when(membreRepository.findByIdOptional(id)).thenReturn(Optional.of(membre));
|
|
doNothing().when(membreRepository).persist(any(Membre.class));
|
|
|
|
Membre activated = membreService.activerMembre(id);
|
|
|
|
assertThat(activated.getStatutCompte()).isEqualTo("ACTIF");
|
|
assertThat(activated.getActif()).isTrue();
|
|
verify(membreRepository).persist(membre);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre introuvable: lève NotFoundException")
|
|
void activerMembre_notFound_throws() {
|
|
UUID id = UUID.randomUUID();
|
|
when(membreRepository.findByIdOptional(id)).thenReturn(Optional.empty());
|
|
|
|
assertThatThrownBy(() -> membreService.activerMembre(id))
|
|
.isInstanceOf(jakarta.ws.rs.NotFoundException.class)
|
|
.hasMessageContaining("non trouvé");
|
|
|
|
verify(membreRepository, never()).persist(any(Membre.class));
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// promouvoirAdminOrganisation
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("promouvoirAdminOrganisation")
|
|
class PromouvoirAdminOrganisationTests {
|
|
|
|
@Test
|
|
@DisplayName("Happy path: membre promu → ACTIF + actif=true immédiatement")
|
|
void promouvoirAdminOrganisation_setsActifImmediately() {
|
|
UUID id = UUID.randomUUID();
|
|
Membre membre = new Membre();
|
|
membre.setId(id);
|
|
membre.setEmail("future-admin@unionflow.dev");
|
|
membre.setNom("Admin");
|
|
membre.setPrenom("New");
|
|
membre.setStatutCompte("EN_ATTENTE_VALIDATION");
|
|
membre.setActif(false);
|
|
|
|
when(membreRepository.findByIdOptional(id)).thenReturn(Optional.of(membre));
|
|
doNothing().when(membreRepository).persist(any(Membre.class));
|
|
|
|
Membre result = membreService.promouvoirAdminOrganisation(id);
|
|
|
|
assertThat(result.getStatutCompte()).isEqualTo("ACTIF");
|
|
assertThat(result.getActif()).isTrue();
|
|
verify(membreRepository).persist(membre);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre introuvable: lève NotFoundException")
|
|
void promouvoirAdminOrganisation_notFound_throws() {
|
|
UUID id = UUID.randomUUID();
|
|
when(membreRepository.findByIdOptional(id)).thenReturn(Optional.empty());
|
|
|
|
assertThatThrownBy(() -> membreService.promouvoirAdminOrganisation(id))
|
|
.isInstanceOf(jakarta.ws.rs.NotFoundException.class)
|
|
.hasMessageContaining("non trouvé");
|
|
|
|
verify(membreRepository, never()).persist(any(Membre.class));
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// mettreAJourMembre
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("mettreAJourMembre")
|
|
class MettreAJourMembreTests {
|
|
|
|
@Test
|
|
@DisplayName("Membre introuvable: lève IllegalArgumentException")
|
|
void mettreAJourMembre_notFound_throws() {
|
|
UUID id = UUID.randomUUID();
|
|
// UUID aléatoire non présent en H2 → findById retourne null → service throws
|
|
|
|
Membre modifie = new Membre();
|
|
modifie.setEmail("any@unionflow.dev");
|
|
modifie.setNom("X");
|
|
modifie.setPrenom("Y");
|
|
|
|
assertThatThrownBy(() -> membreService.mettreAJourMembre(id, modifie))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("non trouvé");
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// trouverParId / trouverParEmail
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("trouverParId / trouverParEmail")
|
|
class TrouverTests {
|
|
|
|
@Test
|
|
@DisplayName("trouverParId: retourne empty si non trouvé")
|
|
void trouverParId_notFound() {
|
|
UUID id = UUID.randomUUID();
|
|
// UUID aléatoire non présent en H2 → findById retourne null → trouverParId retourne empty
|
|
|
|
Optional<Membre> result = membreService.trouverParId(id);
|
|
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("trouverParEmail: délègue au repository")
|
|
void trouverParEmail_delegates() {
|
|
Membre m = membreFixture("byemail@unionflow.dev");
|
|
when(membreRepository.findByEmail("byemail@unionflow.dev")).thenReturn(Optional.of(m));
|
|
|
|
Optional<Membre> result = membreService.trouverParEmail("byemail@unionflow.dev");
|
|
|
|
assertThat(result).isPresent();
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// listerMembresActifs / rechercherMembres (simple)
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("listerMembresActifs / rechercherMembres simple")
|
|
class ListerTests {
|
|
|
|
@Test
|
|
@DisplayName("listerMembresActifs sans pagination: délègue au repository")
|
|
void listerMembresActifs_delegates() {
|
|
List<Membre> membres = List.of(membreFixture("a@test.dev"), membreFixture("b@test.dev"));
|
|
when(membreRepository.findAllActifs()).thenReturn(membres);
|
|
|
|
List<Membre> result = membreService.listerMembresActifs();
|
|
|
|
assertThat(result).hasSize(2);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("listerMembresActifs avec pagination: délègue au repository")
|
|
void listerMembresActifs_paged() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> membres = List.of(membreFixture("c@test.dev"));
|
|
when(membreRepository.findAllActifs(page, sort)).thenReturn(membres);
|
|
|
|
List<Membre> result = membreService.listerMembresActifs(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("rechercherMembres sans pagination: délègue au repository")
|
|
void rechercherMembres_simple() {
|
|
List<Membre> membres = List.of(membreFixture("search@test.dev"));
|
|
when(membreRepository.findByNomOrPrenom("doe")).thenReturn(membres);
|
|
|
|
List<Membre> result = membreService.rechercherMembres("doe");
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("compterMembresActifs: délègue au repository")
|
|
void compterMembresActifs() {
|
|
when(membreRepository.countActifs()).thenReturn(42L);
|
|
|
|
long count = membreService.compterMembresActifs();
|
|
|
|
assertThat(count).isEqualTo(42L);
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// desactiverMembre
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("desactiverMembre")
|
|
class DesactiverTests {
|
|
|
|
@Test
|
|
@DisplayName("Membre introuvable: lève IllegalArgumentException")
|
|
void desactiverMembre_notFound_throws() {
|
|
UUID id = UUID.randomUUID();
|
|
// UUID aléatoire non présent en H2 → findById retourne null → service throws
|
|
|
|
assertThatThrownBy(() -> membreService.desactiverMembre(id))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("non trouvé");
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// listerMembres (avec logique ADMIN_ORGANISATION)
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("listerMembres (avec logique ADMIN_ORGANISATION)")
|
|
class ListerMembresSecurityTests {
|
|
|
|
@Test
|
|
@DisplayName("Utilisateur sans rôle ADMIN_ORGANISATION: liste tous les membres")
|
|
void listerMembres_noAdminOrgRole_returnsAll() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> all = List.of(membreFixture("x@test.dev"));
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("USER"));
|
|
doReturn(all).when(membreRepository).findAll(page, sort);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
verify(membreRepository).findAll(page, sort);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION avec organisations: filtre par organisations")
|
|
void listerMembres_adminOrg_filtersToOrgs() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
|
|
UUID orgId = UUID.randomUUID();
|
|
Organisation org = new Organisation();
|
|
org.setId(orgId);
|
|
org.setNom("Test Org");
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of(org));
|
|
|
|
List<Membre> orgMembres = List.of(membreFixture("m@test.dev"));
|
|
when(membreRepository.findDistinctByOrganisationIdIn(any(), eq(page), eq(sort)))
|
|
.thenReturn(orgMembres);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION sans organisations: retourne liste vide")
|
|
void listerMembres_adminOrg_noOrgs_returnsEmpty() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of());
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION + ADMIN: pas de restriction (retourne tous)")
|
|
void listerMembres_adminOrgAndAdmin_returnsAll() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> all = List.of(membreFixture("y@test.dev"));
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION", "ADMIN"));
|
|
when(membreRepository.findAll(page, sort)).thenReturn(all);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("SecurityIdentity null (principal null): retourne tous les membres")
|
|
void listerMembres_nullPrincipal_returnsAll() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> all = List.of(membreFixture("z@test.dev"));
|
|
|
|
when(securityIdentity.getPrincipal()).thenReturn(null);
|
|
when(membreRepository.findAll(page, sort)).thenReturn(all);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Roles null: retourne tous les membres")
|
|
void listerMembres_nullRoles_returnsAll() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> all = List.of(membreFixture("z2@test.dev"));
|
|
|
|
Principal principal = () -> "user@test.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(null);
|
|
when(membreRepository.findAll(page, sort)).thenReturn(all);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// compterMembres (avec logique ADMIN_ORGANISATION)
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("compterMembres")
|
|
class CompterMembresTests {
|
|
|
|
@Test
|
|
@DisplayName("Utilisateur normal: compte tous les membres")
|
|
void compterMembres_noAdminOrg_countsAll() {
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of());
|
|
doReturn(100L).when(membreRepository).count();
|
|
|
|
long count = membreService.compterMembres();
|
|
|
|
assertThat(count).isEqualTo(100L);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION avec organisations: compte par org")
|
|
void compterMembres_adminOrg_countsFiltered() {
|
|
UUID orgId = UUID.randomUUID();
|
|
Organisation org = new Organisation();
|
|
org.setId(orgId);
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of(org));
|
|
when(membreRepository.countDistinctByOrganisationIdIn(any())).thenReturn(15L);
|
|
|
|
long count = membreService.compterMembres();
|
|
|
|
assertThat(count).isEqualTo(15L);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION sans organisations: retourne 0")
|
|
void compterMembres_adminOrg_noOrgs_returnsZero() {
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of());
|
|
|
|
long count = membreService.compterMembres();
|
|
|
|
assertThat(count).isEqualTo(0L);
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// rechercherMembres avec pagination
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("rechercherMembres (avec pagination)")
|
|
class RechercherMembresPagedTests {
|
|
|
|
@Test
|
|
@DisplayName("Utilisateur normal: recherche tous")
|
|
void rechercherMembres_paged_noAdminOrg() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> membres = List.of(membreFixture("r@test.dev"));
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of());
|
|
when(membreRepository.findByNomOrPrenom("doe", page, sort)).thenReturn(membres);
|
|
|
|
List<Membre> result = membreService.rechercherMembres("doe", page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION avec organisations: filtre")
|
|
void rechercherMembres_paged_adminOrg() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
|
|
UUID orgId = UUID.randomUUID();
|
|
Organisation org = new Organisation();
|
|
org.setId(orgId);
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of(org));
|
|
when(membreRepository.findByNomOrPrenomAndOrganisationIdIn(eq("doe"), any(), eq(page), eq(sort)))
|
|
.thenReturn(List.of(membreFixture("r@test.dev")));
|
|
|
|
List<Membre> result = membreService.rechercherMembres("doe", page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION sans organisations: retourne liste vide")
|
|
void rechercherMembres_paged_adminOrg_noOrgs() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of());
|
|
|
|
List<Membre> result = membreService.rechercherMembres("doe", page, sort);
|
|
|
|
assertThat(result).isEmpty();
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// obtenirStatistiquesAvancees
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("obtenirStatistiquesAvancees: calcule le taux d'activité correctement")
|
|
void obtenirStatistiquesAvancees_calculatesCorrectly() {
|
|
doReturn(100L).when(membreRepository).count();
|
|
doReturn(80L).when(membreRepository).countActifs();
|
|
doReturn(10L).when(membreRepository).countNouveauxMembres(any());
|
|
|
|
Map<String, Object> stats = membreService.obtenirStatistiquesAvancees();
|
|
|
|
assertThat(stats).containsKey("totalMembres");
|
|
assertThat(stats).containsKey("membresActifs");
|
|
assertThat(stats).containsKey("membresInactifs");
|
|
assertThat(stats).containsKey("tauxActivite");
|
|
assertThat(stats.get("totalMembres")).isEqualTo(100L);
|
|
assertThat(stats.get("membresActifs")).isEqualTo(80L);
|
|
assertThat(stats.get("membresInactifs")).isEqualTo(20L);
|
|
assertThat((Double) stats.get("tauxActivite")).isEqualTo(80.0);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("obtenirStatistiquesAvancees: taux 0 si pas de membres")
|
|
void obtenirStatistiquesAvancees_noMembers_tauxZero() {
|
|
doReturn(0L).when(membreRepository).count();
|
|
doReturn(0L).when(membreRepository).countActifs();
|
|
doReturn(0L).when(membreRepository).countNouveauxMembres(any());
|
|
|
|
Map<String, Object> stats = membreService.obtenirStatistiquesAvancees();
|
|
|
|
assertThat((Double) stats.get("tauxActivite")).isEqualTo(0.0);
|
|
}
|
|
|
|
// =========================================================================
|
|
// convertToResponse
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("convertToResponse")
|
|
class ConvertToResponseTests {
|
|
|
|
@Test
|
|
@DisplayName("null retourne null")
|
|
void convertToResponse_null() {
|
|
assertThat(membreService.convertToResponse(null)).isNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre minimal sans organisation ni rôles")
|
|
void convertToResponse_minimalMembre() {
|
|
Membre m = membreFixture("resp@test.dev");
|
|
m.setStatutCompte(null);
|
|
m.setStatutMatrimonial(null);
|
|
m.setTypeIdentite(null);
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
MembreResponse resp = membreService.convertToResponse(m);
|
|
|
|
assertThat(resp).isNotNull();
|
|
assertThat(resp.getEmail()).isEqualTo("resp@test.dev");
|
|
assertThat(resp.getRoles()).isEmpty();
|
|
assertThat(resp.getOrganisationId()).isNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec statut matrimonial et typeIdentite: résout les libellés")
|
|
void convertToResponse_withReferenceData() {
|
|
Membre m = membreFixture("full@test.dev");
|
|
m.setStatutMatrimonial("MARIE");
|
|
m.setTypeIdentite("CNI");
|
|
m.setStatutCompte("ACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
m.setVersion(2L);
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
when(typeReferenceRepository.findLibelleByDomaineAndCode("STATUT_MATRIMONIAL", "MARIE"))
|
|
.thenReturn("Marié(e)");
|
|
when(typeReferenceRepository.findLibelleByDomaineAndCode("TYPE_IDENTITE", "CNI"))
|
|
.thenReturn("Carte Nationale d'Identité");
|
|
when(typeReferenceRepository.findLibelleByDomaineAndCode("STATUT_COMPTE", "ACTIF"))
|
|
.thenReturn("Actif");
|
|
when(typeReferenceRepository.findSeverityByDomaineAndCode("STATUT_COMPTE", "ACTIF"))
|
|
.thenReturn("success");
|
|
|
|
MembreResponse resp = membreService.convertToResponse(m);
|
|
|
|
assertThat(resp.getStatutMatrimonialLibelle()).isEqualTo("Marié(e)");
|
|
assertThat(resp.getTypeIdentiteLibelle()).isEqualTo("Carte Nationale d'Identité");
|
|
assertThat(resp.getStatutCompteLibelle()).isEqualTo("Actif");
|
|
assertThat(resp.getStatutCompteSeverity()).isEqualTo("success");
|
|
assertThat(resp.getVersion()).isEqualTo(2L);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec rôles actifs: les codes sont inclus")
|
|
void convertToResponse_withRoles() {
|
|
Membre m = membreFixture("roles@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
|
|
Role role = new Role();
|
|
role.setCode("PRESIDENT");
|
|
|
|
MembreRole mr = new MembreRole();
|
|
mr.setRole(role);
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of(mr));
|
|
|
|
MembreResponse resp = membreService.convertToResponse(m);
|
|
|
|
assertThat(resp.getRoles()).contains("PRESIDENT");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec rôle dont role est null: filtré")
|
|
void convertToResponse_roleNullFiltered() {
|
|
Membre m = membreFixture("nullrole@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
|
|
MembreRole mr = new MembreRole();
|
|
mr.setRole(null); // null role
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of(mr));
|
|
|
|
MembreResponse resp = membreService.convertToResponse(m);
|
|
|
|
assertThat(resp.getRoles()).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec organisation: organisationId et nom remplis")
|
|
void convertToResponse_withOrganisation() {
|
|
UUID orgId = UUID.randomUUID();
|
|
Organisation org = new Organisation();
|
|
org.setId(orgId);
|
|
org.setNom("Lions Club");
|
|
|
|
MembreOrganisation mo = new MembreOrganisation();
|
|
mo.setOrganisation(org);
|
|
mo.setDateAdhesion(LocalDate.of(2022, 6, 1));
|
|
mo.setMembre(new Membre());
|
|
|
|
Membre m = membreFixture("org@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>(List.of(mo)));
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
MembreResponse resp = membreService.convertToResponse(m);
|
|
|
|
assertThat(resp.getOrganisationId()).isEqualTo(orgId);
|
|
assertThat(resp.getAssociationNom()).isEqualTo("Lions Club");
|
|
assertThat(resp.getDateAdhesion()).isEqualTo(LocalDate.of(2022, 6, 1));
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec MembreOrganisation dont organisation est null: pas d'exception")
|
|
void convertToResponse_moWithNullOrg() {
|
|
MembreOrganisation mo = new MembreOrganisation();
|
|
mo.setOrganisation(null);
|
|
mo.setMembre(new Membre());
|
|
|
|
Membre m = membreFixture("nullorg@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>(List.of(mo)));
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
MembreResponse resp = membreService.convertToResponse(m);
|
|
|
|
assertThat(resp.getOrganisationId()).isNull();
|
|
assertThat(resp.getAssociationNom()).isNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Version null: retourne 0")
|
|
void convertToResponse_versionNull() {
|
|
Membre m = membreFixture("ver@test.dev");
|
|
m.setVersion(null);
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
MembreResponse resp = membreService.convertToResponse(m);
|
|
|
|
assertThat(resp.getVersion()).isEqualTo(0L);
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// convertToSummaryResponse
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("convertToSummaryResponse")
|
|
class ConvertToSummaryResponseTests {
|
|
|
|
@Test
|
|
@DisplayName("null retourne null")
|
|
void convertToSummaryResponse_null() {
|
|
assertThat(membreService.convertToSummaryResponse(null)).isNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre sans rôles ni statut: rôles vides, libellés null")
|
|
void convertToSummaryResponse_noRolesNoStatut() {
|
|
Membre m = membreFixture("sum@test.dev");
|
|
m.setStatutCompte(null);
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
MembreSummaryResponse resp = membreService.convertToSummaryResponse(m);
|
|
|
|
assertThat(resp).isNotNull();
|
|
assertThat(resp.roles()).isEmpty();
|
|
assertThat(resp.statutCompteLibelle()).isNull();
|
|
assertThat(resp.statutCompteSeverity()).isNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec statut ACTIF: libellé et severity résolus")
|
|
void convertToSummaryResponse_withStatut() {
|
|
Membre m = membreFixture("statut@test.dev");
|
|
m.setStatutCompte("ACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
when(typeReferenceRepository.findLibelleByDomaineAndCode("STATUT_COMPTE", "ACTIF"))
|
|
.thenReturn("Actif");
|
|
when(typeReferenceRepository.findSeverityByDomaineAndCode("STATUT_COMPTE", "ACTIF"))
|
|
.thenReturn("success");
|
|
|
|
MembreSummaryResponse resp = membreService.convertToSummaryResponse(m);
|
|
|
|
assertThat(resp.statutCompteLibelle()).isEqualTo("Actif");
|
|
assertThat(resp.statutCompteSeverity()).isEqualTo("success");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec rôles actifs: codes inclus")
|
|
void convertToSummaryResponse_withRoles() {
|
|
Membre m = membreFixture("sumroles@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
|
|
Role role = new Role();
|
|
role.setCode("TRESORIER");
|
|
MembreRole mr = new MembreRole();
|
|
mr.setRole(role);
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of(mr));
|
|
|
|
MembreSummaryResponse resp = membreService.convertToSummaryResponse(m);
|
|
|
|
assertThat(resp.roles()).contains("TRESORIER");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Membre avec organisation: organisationId rempli")
|
|
void convertToSummaryResponse_withOrganisation() {
|
|
UUID orgId = UUID.randomUUID();
|
|
Organisation org = new Organisation();
|
|
org.setId(orgId);
|
|
org.setNom("Assoc Test");
|
|
|
|
MembreOrganisation mo = new MembreOrganisation();
|
|
mo.setOrganisation(org);
|
|
mo.setMembre(new Membre());
|
|
|
|
Membre m = membreFixture("sumorg@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>(List.of(mo)));
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
MembreSummaryResponse resp = membreService.convertToSummaryResponse(m);
|
|
|
|
assertThat(resp.organisationId()).isEqualTo(orgId);
|
|
assertThat(resp.associationNom()).isEqualTo("Assoc Test");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("MembreOrganisation avec organisation null: pas d'exception")
|
|
void convertToSummaryResponse_moWithNullOrg() {
|
|
MembreOrganisation mo = new MembreOrganisation();
|
|
mo.setOrganisation(null);
|
|
mo.setMembre(new Membre());
|
|
|
|
Membre m = membreFixture("nullorgsum@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>(List.of(mo)));
|
|
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
MembreSummaryResponse resp = membreService.convertToSummaryResponse(m);
|
|
|
|
assertThat(resp.organisationId()).isNull();
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// convertFromCreateRequest
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("convertFromCreateRequest")
|
|
class ConvertFromCreateRequestTests {
|
|
|
|
@Test
|
|
@DisplayName("null retourne null")
|
|
void convertFromCreateRequest_null() {
|
|
assertThat(membreService.convertFromCreateRequest(null)).isNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("DTO complet: tous les champs copiés")
|
|
void convertFromCreateRequest_fullDto() {
|
|
LocalDate dateNaissance = LocalDate.of(1990, 5, 15);
|
|
CreateMembreRequest req = CreateMembreRequest.builder()
|
|
.nom("Diallo")
|
|
.prenom("Aminata")
|
|
.email("aminata@test.dev")
|
|
.telephone("0600000001")
|
|
.telephoneWave("+2250700000001")
|
|
.dateNaissance(dateNaissance)
|
|
.profession("Ingénieure")
|
|
.photoUrl("https://example.com/photo.jpg")
|
|
.statutMatrimonial("CELIBATAIRE")
|
|
.nationalite("Ivoirienne")
|
|
.typeIdentite("CNI")
|
|
.numeroIdentite("ABC123456")
|
|
.organisationId(UUID.randomUUID())
|
|
.build();
|
|
|
|
Membre membre = membreService.convertFromCreateRequest(req);
|
|
|
|
assertThat(membre.getNom()).isEqualTo("Diallo");
|
|
assertThat(membre.getPrenom()).isEqualTo("Aminata");
|
|
assertThat(membre.getEmail()).isEqualTo("aminata@test.dev");
|
|
assertThat(membre.getTelephone()).isEqualTo("0600000001");
|
|
assertThat(membre.getTelephoneWave()).isEqualTo("+2250700000001");
|
|
assertThat(membre.getDateNaissance()).isEqualTo(dateNaissance);
|
|
assertThat(membre.getProfession()).isEqualTo("Ingénieure");
|
|
assertThat(membre.getPhotoUrl()).isEqualTo("https://example.com/photo.jpg");
|
|
assertThat(membre.getStatutMatrimonial()).isEqualTo("CELIBATAIRE");
|
|
assertThat(membre.getNationalite()).isEqualTo("Ivoirienne");
|
|
assertThat(membre.getTypeIdentite()).isEqualTo("CNI");
|
|
assertThat(membre.getNumeroIdentite()).isEqualTo("ABC123456");
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// updateFromRequest
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("updateFromRequest")
|
|
class UpdateFromRequestTests {
|
|
|
|
@Test
|
|
@DisplayName("null membre ou null dto: aucune mise à jour")
|
|
void updateFromRequest_nullInputs_noOp() {
|
|
Membre m = membreFixture("before@test.dev");
|
|
// Both null
|
|
membreService.updateFromRequest(null, null);
|
|
// null dto
|
|
membreService.updateFromRequest(m, null);
|
|
// null membre
|
|
UpdateMembreRequest req = UpdateMembreRequest.builder()
|
|
.nom("X")
|
|
.prenom("Y")
|
|
.email("z@test.dev")
|
|
.dateNaissance(LocalDate.now())
|
|
.build();
|
|
membreService.updateFromRequest(null, req);
|
|
|
|
// email remains unchanged on original membre
|
|
assertThat(m.getEmail()).isEqualTo("before@test.dev");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("DTO complet avec actif non null: tous les champs mis à jour")
|
|
void updateFromRequest_fullDto_allUpdated() {
|
|
Membre m = membreFixture("old@test.dev");
|
|
LocalDate newDate = LocalDate.of(1995, 8, 20);
|
|
|
|
UpdateMembreRequest req = UpdateMembreRequest.builder()
|
|
.nom("NouveauNom")
|
|
.prenom("NouveauPrenom")
|
|
.email("new@test.dev")
|
|
.telephone("0700000002")
|
|
.telephoneWave("+2250700000002")
|
|
.dateNaissance(newDate)
|
|
.profession("Médecin")
|
|
.photoUrl("https://example.com/new.jpg")
|
|
.statutMatrimonial("MARIE")
|
|
.nationalite("Sénégalaise")
|
|
.typeIdentite("PASSEPORT")
|
|
.numeroIdentite("PA123456")
|
|
.actif(false)
|
|
.build();
|
|
|
|
membreService.updateFromRequest(m, req);
|
|
|
|
assertThat(m.getNom()).isEqualTo("NouveauNom");
|
|
assertThat(m.getPrenom()).isEqualTo("NouveauPrenom");
|
|
assertThat(m.getEmail()).isEqualTo("new@test.dev");
|
|
assertThat(m.getTelephone()).isEqualTo("0700000002");
|
|
assertThat(m.getDateNaissance()).isEqualTo(newDate);
|
|
assertThat(m.getProfession()).isEqualTo("Médecin");
|
|
assertThat(m.getActif()).isFalse();
|
|
assertThat(m.getDateModification()).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("actif null dans le DTO: flag actif non modifié")
|
|
void updateFromRequest_actifNull_notModified() {
|
|
Membre m = membreFixture("keep@test.dev");
|
|
m.setActif(true);
|
|
|
|
UpdateMembreRequest req = UpdateMembreRequest.builder()
|
|
.nom("KeepActif")
|
|
.prenom("Test")
|
|
.email("keep@test.dev")
|
|
.dateNaissance(LocalDate.now())
|
|
.actif(null) // null → should not change actif
|
|
.build();
|
|
|
|
membreService.updateFromRequest(m, req);
|
|
|
|
assertThat(m.getActif()).isTrue();
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// convertToResponseList / convertToSummaryResponseList
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("convertToResponseList / convertToSummaryResponseList")
|
|
class ConvertListTests {
|
|
|
|
@Test
|
|
@DisplayName("liste null: retourne liste vide")
|
|
void convertToResponseList_null_returnsEmpty() {
|
|
List<MembreResponse> result = membreService.convertToResponseList(null);
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("liste vide: retourne liste vide")
|
|
void convertToResponseList_empty_returnsEmpty() {
|
|
List<MembreResponse> result = membreService.convertToResponseList(new ArrayList<>());
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("liste de 2 membres: retourne 2 réponses")
|
|
void convertToResponseList_twoMembres() {
|
|
Membre m1 = membreFixture("a@test.dev");
|
|
Membre m2 = membreFixture("b@test.dev");
|
|
when(membreRoleRepository.findActifsByMembreId(m1.getId())).thenReturn(List.of());
|
|
when(membreRoleRepository.findActifsByMembreId(m2.getId())).thenReturn(List.of());
|
|
|
|
List<MembreResponse> result = membreService.convertToResponseList(List.of(m1, m2));
|
|
|
|
assertThat(result).hasSize(2);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("convertToSummaryResponseList null: retourne liste vide")
|
|
void convertToSummaryResponseList_null() {
|
|
List<MembreSummaryResponse> result = membreService.convertToSummaryResponseList(null);
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("convertToSummaryResponseList de 1 membre")
|
|
void convertToSummaryResponseList_oneMembre() {
|
|
Membre m = membreFixture("s@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
List<MembreSummaryResponse> result = membreService.convertToSummaryResponseList(List.of(m));
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// rechercheAvancee (deprecated)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("rechercheAvancee (deprecated): délègue au repository")
|
|
void rechercheAvancee_deprecated_delegates() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> membres = List.of(membreFixture("adv@test.dev"));
|
|
LocalDate min = LocalDate.of(2020, 1, 1);
|
|
LocalDate max = LocalDate.of(2024, 12, 31);
|
|
|
|
when(membreRepository.rechercheAvancee("john", true, min, max, page, sort))
|
|
.thenReturn(membres);
|
|
|
|
List<Membre> result = membreService.rechercheAvancee("john", true, min, max, page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
// =========================================================================
|
|
// exporterMembresSelectionnes
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("exporterMembresSelectionnes")
|
|
class ExporterMembresTests {
|
|
|
|
@Test
|
|
@DisplayName("liste null: lève IllegalArgumentException")
|
|
void exporterMembresSelectionnes_null_throws() {
|
|
assertThatThrownBy(() -> membreService.exporterMembresSelectionnes(null, "CSV"))
|
|
.isInstanceOf(IllegalArgumentException.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("liste vide: lève IllegalArgumentException")
|
|
void exporterMembresSelectionnes_empty_throws() {
|
|
assertThatThrownBy(() -> membreService.exporterMembresSelectionnes(List.of(), "CSV"))
|
|
.isInstanceOf(IllegalArgumentException.class);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("membres non trouvés: retourne CSV avec en-tête seulement")
|
|
void exporterMembresSelectionnes_notFound_returnsHeaderOnlyCsv() {
|
|
UUID id = UUID.randomUUID();
|
|
when(membreRepository.findByIdOptional(id)).thenReturn(Optional.empty());
|
|
|
|
byte[] result = membreService.exporterMembresSelectionnes(List.of(id), "CSV");
|
|
|
|
assertThat(result).isNotNull();
|
|
String csv = new String(result, java.nio.charset.StandardCharsets.UTF_8);
|
|
assertThat(csv).contains("Numéro");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("membres trouvés: retourne CSV avec données")
|
|
void exporterMembresSelectionnes_found_returnsCsvWithData() {
|
|
UUID id = UUID.randomUUID();
|
|
Membre m = membreFixture("exp@test.dev");
|
|
m.setId(id);
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
when(membreRepository.findByIdOptional(id)).thenReturn(Optional.of(m));
|
|
when(membreRoleRepository.findActifsByMembreId(id)).thenReturn(List.of());
|
|
|
|
byte[] result = membreService.exporterMembresSelectionnes(List.of(id), "CSV");
|
|
|
|
assertThat(result).isNotNull();
|
|
String csv = new String(result, java.nio.charset.StandardCharsets.UTF_8);
|
|
assertThat(csv).contains("exp@test.dev");
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// exporterVersExcel / exporterVersCSV / exporterVersPDF / genererModeleImport
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("Export/Import delegation tests")
|
|
class ExportImportDelegationTests {
|
|
|
|
@Test
|
|
@DisplayName("exporterVersExcel: délègue et retourne résultat")
|
|
void exporterVersExcel_delegates() throws Exception {
|
|
byte[] data = "excel".getBytes();
|
|
when(membreImportExportService.exporterVersExcel(any(), any(), eq(true), eq(true), eq(false), any()))
|
|
.thenReturn(data);
|
|
|
|
byte[] result = membreService.exporterVersExcel(List.of(), List.of(), true, true, false, null);
|
|
|
|
assertThat(result).isEqualTo(data);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("exporterVersExcel: exception propagée comme RuntimeException")
|
|
void exporterVersExcel_exception_wraps() throws Exception {
|
|
when(membreImportExportService.exporterVersExcel(any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any()))
|
|
.thenThrow(new RuntimeException("Excel error"));
|
|
|
|
assertThatThrownBy(() -> membreService.exporterVersExcel(List.of(), List.of(), true, true, false, null))
|
|
.isInstanceOf(RuntimeException.class)
|
|
.hasMessageContaining("Excel");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("exporterVersCSV: délègue et retourne résultat")
|
|
void exporterVersCSV_delegates() throws Exception {
|
|
byte[] data = "csv".getBytes();
|
|
when(membreImportExportService.exporterVersCSV(any(), any(), eq(true), eq(false)))
|
|
.thenReturn(data);
|
|
|
|
byte[] result = membreService.exporterVersCSV(List.of(), List.of(), true, false);
|
|
|
|
assertThat(result).isEqualTo(data);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("exporterVersCSV: exception propagée comme RuntimeException")
|
|
void exporterVersCSV_exception_wraps() throws Exception {
|
|
when(membreImportExportService.exporterVersCSV(any(), any(), anyBoolean(), anyBoolean()))
|
|
.thenThrow(new RuntimeException("CSV error"));
|
|
|
|
assertThatThrownBy(() -> membreService.exporterVersCSV(List.of(), List.of(), true, false))
|
|
.isInstanceOf(RuntimeException.class)
|
|
.hasMessageContaining("CSV");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("exporterVersPDF: délègue et retourne résultat")
|
|
void exporterVersPDF_delegates() throws Exception {
|
|
byte[] data = "pdf".getBytes();
|
|
when(membreImportExportService.exporterVersPDF(any(), any(), eq(true), eq(false), eq(true)))
|
|
.thenReturn(data);
|
|
|
|
byte[] result = membreService.exporterVersPDF(List.of(), List.of(), true, false, true);
|
|
|
|
assertThat(result).isEqualTo(data);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("exporterVersPDF: exception propagée comme RuntimeException")
|
|
void exporterVersPDF_exception_wraps() throws Exception {
|
|
when(membreImportExportService.exporterVersPDF(any(), any(), anyBoolean(), anyBoolean(), anyBoolean()))
|
|
.thenThrow(new RuntimeException("PDF error"));
|
|
|
|
assertThatThrownBy(() -> membreService.exporterVersPDF(List.of(), List.of(), true, false, true))
|
|
.isInstanceOf(RuntimeException.class)
|
|
.hasMessageContaining("PDF");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("genererModeleImport: délègue et retourne résultat")
|
|
void genererModeleImport_delegates() throws Exception {
|
|
byte[] data = "template".getBytes();
|
|
when(membreImportExportService.genererModeleImport()).thenReturn(data);
|
|
|
|
byte[] result = membreService.genererModeleImport();
|
|
|
|
assertThat(result).isEqualTo(data);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("genererModeleImport: exception propagée comme RuntimeException")
|
|
void genererModeleImport_exception_wraps() throws Exception {
|
|
when(membreImportExportService.genererModeleImport()).thenThrow(new RuntimeException("Template error"));
|
|
|
|
assertThatThrownBy(() -> membreService.genererModeleImport())
|
|
.isInstanceOf(RuntimeException.class)
|
|
.hasMessageContaining("modèle");
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// listerMembresParOrganisations (uses EntityManager — excluded from mock tests)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("listerMembresParOrganisations: liste null retourne liste vide")
|
|
void listerMembresParOrganisations_null_returnsEmpty() {
|
|
// This path doesn't touch EntityManager
|
|
List<Membre> result = membreService.listerMembresParOrganisations(null, Page.of(0, 10), Sort.by("nom"));
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("listerMembresParOrganisations: liste vide retourne liste vide")
|
|
void listerMembresParOrganisations_empty_returnsEmpty() {
|
|
List<Membre> result = membreService.listerMembresParOrganisations(List.of(), Page.of(0, 10), Sort.by("nom"));
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
// =========================================================================
|
|
// obtenirVillesDistinctes / obtenirProfessionsDistinctes
|
|
// (use EntityManager directly — available in @QuarkusTest context)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("obtenirVillesDistinctes sans query: retourne une liste (peut être vide)")
|
|
void obtenirVillesDistinctes_noQuery_returnsList() {
|
|
List<String> villes = membreService.obtenirVillesDistinctes(null);
|
|
assertThat(villes).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("obtenirVillesDistinctes avec query vide: retourne une liste")
|
|
void obtenirVillesDistinctes_emptyQuery_returnsList() {
|
|
List<String> villes = membreService.obtenirVillesDistinctes("");
|
|
assertThat(villes).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("obtenirVillesDistinctes avec query: retourne une liste filtrée")
|
|
void obtenirVillesDistinctes_withQuery_returnsList() {
|
|
List<String> villes = membreService.obtenirVillesDistinctes("Paris");
|
|
assertThat(villes).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("obtenirProfessionsDistinctes sans query: retourne une liste")
|
|
void obtenirProfessionsDistinctes_noQuery_returnsList() {
|
|
List<String> professions = membreService.obtenirProfessionsDistinctes(null);
|
|
assertThat(professions).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("obtenirProfessionsDistinctes avec query vide: retourne une liste")
|
|
void obtenirProfessionsDistinctes_emptyQuery_returnsList() {
|
|
List<String> professions = membreService.obtenirProfessionsDistinctes("");
|
|
assertThat(professions).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("obtenirProfessionsDistinctes avec query: retourne une liste filtrée")
|
|
void obtenirProfessionsDistinctes_withQuery_returnsList() {
|
|
List<String> professions = membreService.obtenirProfessionsDistinctes("Ingénieur");
|
|
assertThat(professions).isNotNull();
|
|
}
|
|
|
|
// =========================================================================
|
|
// exporterVersCSV / exporterVersPDF / genererModeleImport — error branches
|
|
// covered by ExportImportDelegationTests above; these cover listerMembres
|
|
// =========================================================================
|
|
|
|
// =========================================================================
|
|
// listerMembresPourExport
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("listerMembresPourExport")
|
|
class ListerMembresPourExportTests {
|
|
|
|
@Test
|
|
@DisplayName("sans associationId: liste tous les membres")
|
|
void listerMembresPourExport_noAssociationId_listAll() {
|
|
Membre m = membreFixture("export@test.dev");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
when(membreRepository.listAll()).thenReturn(List.of(m));
|
|
when(membreRoleRepository.findActifsByMembreId(m.getId())).thenReturn(List.of());
|
|
|
|
List<dev.lions.unionflow.server.api.dto.membre.response.MembreResponse> result =
|
|
membreService.listerMembresPourExport(null, null, null, null, null);
|
|
|
|
assertThat(result).hasSize(1);
|
|
verify(membreRepository).listAll();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("filtre par statut ACTIF: garde seulement les membres actifs")
|
|
void listerMembresPourExport_filtreActif() {
|
|
Membre actif = membreFixture("actif@test.dev");
|
|
actif.setActif(true);
|
|
actif.setMembresOrganisations(new ArrayList<>());
|
|
|
|
Membre inactif = membreFixture("inactif@test.dev");
|
|
inactif.setActif(false);
|
|
inactif.setMembresOrganisations(new ArrayList<>());
|
|
|
|
when(membreRepository.listAll()).thenReturn(List.of(actif, inactif));
|
|
when(membreRoleRepository.findActifsByMembreId(actif.getId())).thenReturn(List.of());
|
|
|
|
List<dev.lions.unionflow.server.api.dto.membre.response.MembreResponse> result =
|
|
membreService.listerMembresPourExport(null, "ACTIF", null, null, null);
|
|
|
|
assertThat(result).hasSize(1);
|
|
assertThat(result.get(0).getEmail()).isEqualTo("actif@test.dev");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("filtre par statut INACTIF: garde seulement les membres inactifs")
|
|
void listerMembresPourExport_filtreInactif() {
|
|
Membre actif = membreFixture("actif2@test.dev");
|
|
actif.setActif(true);
|
|
actif.setMembresOrganisations(new ArrayList<>());
|
|
|
|
Membre inactif = membreFixture("inactif2@test.dev");
|
|
inactif.setActif(false);
|
|
inactif.setMembresOrganisations(new ArrayList<>());
|
|
|
|
when(membreRepository.listAll()).thenReturn(List.of(actif, inactif));
|
|
when(membreRoleRepository.findActifsByMembreId(inactif.getId())).thenReturn(List.of());
|
|
|
|
List<dev.lions.unionflow.server.api.dto.membre.response.MembreResponse> result =
|
|
membreService.listerMembresPourExport(null, "INACTIF", null, null, null);
|
|
|
|
assertThat(result).hasSize(1);
|
|
assertThat(result.get(0).getEmail()).isEqualTo("inactif2@test.dev");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("statut vide: tous les membres retournés sans filtre statut")
|
|
void listerMembresPourExport_statutVide_noFilter() {
|
|
Membre m1 = membreFixture("e1@test.dev");
|
|
m1.setActif(true);
|
|
m1.setMembresOrganisations(new ArrayList<>());
|
|
Membre m2 = membreFixture("e2@test.dev");
|
|
m2.setActif(false);
|
|
m2.setMembresOrganisations(new ArrayList<>());
|
|
|
|
when(membreRepository.listAll()).thenReturn(List.of(m1, m2));
|
|
when(membreRoleRepository.findActifsByMembreId(m1.getId())).thenReturn(List.of());
|
|
when(membreRoleRepository.findActifsByMembreId(m2.getId())).thenReturn(List.of());
|
|
|
|
List<dev.lions.unionflow.server.api.dto.membre.response.MembreResponse> result =
|
|
membreService.listerMembresPourExport(null, "", null, null, null);
|
|
|
|
assertThat(result).hasSize(2);
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// importerMembres (delegation)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("importerMembres: délègue à membreImportExportService")
|
|
void importerMembres_delegates() {
|
|
java.io.InputStream is = new java.io.ByteArrayInputStream(new byte[0]);
|
|
UUID orgId = UUID.randomUUID();
|
|
dev.lions.unionflow.server.service.MembreImportExportService.ResultatImport mockResult =
|
|
new dev.lions.unionflow.server.service.MembreImportExportService.ResultatImport();
|
|
mockResult.totalLignes = 0;
|
|
mockResult.lignesTraitees = 0;
|
|
mockResult.lignesErreur = 0;
|
|
mockResult.erreurs = new ArrayList<>();
|
|
|
|
when(membreImportExportService.importerMembres(is, "test.csv", orgId, "ACTIF", false, true))
|
|
.thenReturn(mockResult);
|
|
|
|
dev.lions.unionflow.server.service.MembreImportExportService.ResultatImport result =
|
|
membreService.importerMembres(is, "test.csv", orgId, "ACTIF", false, true);
|
|
|
|
assertThat(result).isNotNull();
|
|
verify(membreImportExportService).importerMembres(is, "test.csv", orgId, "ACTIF", false, true);
|
|
}
|
|
|
|
// =========================================================================
|
|
// lierMembreOrganisationEtIncrementerQuota
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("lierMembreOrganisationEtIncrementerQuota")
|
|
class LierMembreOrganisationTests {
|
|
|
|
@Test
|
|
@DisplayName("membre null: lève IllegalArgumentException")
|
|
void lierMembre_nullMembre_throws() {
|
|
assertThatThrownBy(() ->
|
|
membreService.lierMembreOrganisationEtIncrementerQuota(null, UUID.randomUUID(), "ACTIF"))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("obligatoires");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("organisationId null: lève IllegalArgumentException")
|
|
void lierMembre_nullOrganisationId_throws() {
|
|
Membre m = membreFixture("lier@test.dev");
|
|
assertThatThrownBy(() ->
|
|
membreService.lierMembreOrganisationEtIncrementerQuota(m, null, "ACTIF"))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("obligatoires");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("organisation introuvable: lève IllegalArgumentException")
|
|
void lierMembre_organisationIntrouvable_throws() {
|
|
Membre m = membreFixture("lier2@test.dev");
|
|
UUID orgId = UUID.randomUUID(); // UUID inexistant en base
|
|
|
|
// EntityManager real + DB vide pour cet UUID → retourne null
|
|
assertThatThrownBy(() ->
|
|
membreService.lierMembreOrganisationEtIncrementerQuota(m, orgId, "ACTIF"))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("Organisation non trouvée");
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("typeMembreDefaut non-ACTIF: statut EN_ATTENTE_VALIDATION")
|
|
void lierMembre_typeNonActif_statutEnAttente() {
|
|
// Vérification que le chemin EN_ATTENTE_VALIDATION est bien sélectionné
|
|
// via l'enum (le test de l'organisation introuvable intervient après)
|
|
Membre m = membreFixture("lier3@test.dev");
|
|
UUID orgId = UUID.randomUUID();
|
|
|
|
assertThatThrownBy(() ->
|
|
membreService.lierMembreOrganisationEtIncrementerQuota(m, orgId, "EN_ATTENTE_VALIDATION"))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("Organisation non trouvée");
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// searchMembresAdvanced — branches ADMIN_ORGANISATION
|
|
// =========================================================================
|
|
|
|
@Nested
|
|
@DisplayName("searchMembresAdvanced — branches sécurité")
|
|
class SearchMembresAdvancedSecurityTests {
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION sans organisations: retourne résultat vide")
|
|
void searchMembresAdvanced_adminOrg_noOrgs_returnsEmpty() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of());
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
assertThat(result.getTotalElements()).isEqualTo(0L);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION avec organisations: restreint par orgIds")
|
|
void searchMembresAdvanced_adminOrg_withOrgs_restrictsToOrgs() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
UUID orgId = UUID.randomUUID();
|
|
dev.lions.unionflow.server.entity.Organisation org = new dev.lions.unionflow.server.entity.Organisation();
|
|
org.setId(orgId);
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of(org));
|
|
|
|
// Appelle le vrai EntityManager (DB vide → totalElements = 0)
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("ADMIN_ORGANISATION avec organisationIds en intersection: filtre correctement")
|
|
void searchMembresAdvanced_adminOrg_intersectionOrgIds() {
|
|
UUID orgId1 = UUID.randomUUID();
|
|
UUID orgId2 = UUID.randomUUID();
|
|
|
|
dev.lions.unionflow.server.entity.Organisation org1 = new dev.lions.unionflow.server.entity.Organisation();
|
|
org1.setId(orgId1);
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder()
|
|
.organisationIds(new ArrayList<>(List.of(orgId2)))
|
|
.build();
|
|
|
|
Principal principal = () -> "admin@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin@org.dev"))
|
|
.thenReturn(List.of(org1));
|
|
|
|
// L'intersection est vide → totalElements = 0
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Utilisateur normal: recherche sans restriction")
|
|
void searchMembresAdvanced_normalUser_noRestriction() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
// Appelle le vrai EntityManager (DB vide ou non → pas d'exception)
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Critères avec statut ACTIF: filtre par actif=true")
|
|
void searchMembresAdvanced_avecStatutActif() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder()
|
|
.statut("ACTIF")
|
|
.build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Critères avec includeInactifs: inclut tous")
|
|
void searchMembresAdvanced_avecIncludeInactifs() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder()
|
|
.includeInactifs(true)
|
|
.build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Critères avec statut non-ACTIF: inclut inactifs")
|
|
void searchMembresAdvanced_avecStatutInactif() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder()
|
|
.statut("INACTIF")
|
|
.build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Critères avec query, nom, prenom, email, telephone: construit la requête JPQL correctement")
|
|
void searchMembresAdvanced_avecTousCriteres() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder()
|
|
.query("test")
|
|
.nom("Doe")
|
|
.prenom("John")
|
|
.email("john@test.dev")
|
|
.telephone("0600")
|
|
.build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom"));
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Sort avec direction Descending: clause ORDER BY contient DESC")
|
|
void searchMembresAdvanced_sortDescending_construitClauseDesc() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
// Sort.by("nom").descending() crée une colonne Direction.Descending → couvre la branche DESC
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom").descending());
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Sort null: searchMembresAdvanced ne lève pas d'exception")
|
|
void searchMembresAdvanced_sortNull_pasDException() {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
// null sort → buildOrderByClause retourne "m.nom ASC" (branche default)
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), null);
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("Sort avec colonnes vides: buildOrderByClause retourne 'm.nom ASC' (branche isEmpty)")
|
|
void searchMembresAdvanced_sortEmptyColumns_coversEmptyBranch() throws Exception {
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
// Sort non-null mais avec colonnes vides → branche sort.getColumns().isEmpty()
|
|
Sort emptySort;
|
|
try {
|
|
java.lang.reflect.Constructor<Sort> ctor = Sort.class.getDeclaredConstructor();
|
|
ctor.setAccessible(true);
|
|
emptySort = ctor.newInstance();
|
|
} catch (NoSuchMethodException e) {
|
|
return;
|
|
}
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), emptySort);
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
}
|
|
|
|
// =========================================================================
|
|
// listerMembresParOrganisations — avec liste non vide (EntityManager réel)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("listerMembresParOrganisations: UUID inexistant → liste vide (aucun membre lié)")
|
|
void listerMembresParOrganisations_uuidInexistant_listeVide() {
|
|
// Passe une liste non vide avec un UUID inexistant → la requête JPQL retourne []
|
|
List<Membre> result = membreService.listerMembresParOrganisations(
|
|
List.of(UUID.randomUUID()),
|
|
Page.of(0, 10),
|
|
Sort.by("nom"));
|
|
assertThat(result).isNotNull();
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("listerMembresParOrganisations: sans pagination (page null) → ne lève pas d'exception")
|
|
void listerMembresParOrganisations_pagingNull_pasDException() {
|
|
List<Membre> result = membreService.listerMembresParOrganisations(
|
|
List.of(UUID.randomUUID()),
|
|
null,
|
|
null);
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
// =========================================================================
|
|
// listerMembresPourExport — branche associationId != null
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("listerMembresPourExport avec associationId inexistant: retourne liste vide")
|
|
void listerMembresPourExport_avecAssociationId_listeVide() {
|
|
// EntityManager réel disponible dans @QuarkusTest — UUID inexistant → liste vide
|
|
List<dev.lions.unionflow.server.api.dto.membre.response.MembreResponse> result =
|
|
membreService.listerMembresPourExport(UUID.randomUUID(), null, null, null, null);
|
|
|
|
assertThat(result).isNotNull();
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("listerMembresPourExport avec associationId et filtre ACTIF: filtre les membres")
|
|
void listerMembresPourExport_avecAssociationIdEtFiltreActif_resultatFiltre() {
|
|
UUID orgId = UUID.randomUUID(); // inexistant → 0 membres
|
|
List<dev.lions.unionflow.server.api.dto.membre.response.MembreResponse> result =
|
|
membreService.listerMembresPourExport(orgId, "ACTIF", null, null, null);
|
|
|
|
assertThat(result).isNotNull();
|
|
}
|
|
|
|
// =========================================================================
|
|
// Tests @TestTransaction (doivent être dans la classe externe — CDI interdit
|
|
// les interceptor bindings sur les méthodes des classes internes JUnit @Nested)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("mettreAJourMembre: champs mis à jour correctement (même email)")
|
|
void mettreAJourMembre_sameEmail_updatesFields() {
|
|
// Persist without pre-set ID — let Hibernate generate it (@GeneratedValue UUID)
|
|
Membre existing = new Membre();
|
|
existing.setEmail("same@unionflow.dev");
|
|
existing.setNom("Doe");
|
|
existing.setPrenom("John");
|
|
existing.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
existing.setNumeroMembre("UF2024-SAME");
|
|
existing.setActif(true);
|
|
existing.setStatutCompte("ACTIF");
|
|
existing.setMembresOrganisations(new ArrayList<>());
|
|
existing.setAdresses(new ArrayList<>());
|
|
em.persist(existing);
|
|
em.flush();
|
|
UUID id = existing.getId();
|
|
|
|
Membre modifie = new Membre();
|
|
modifie.setEmail("same@unionflow.dev");
|
|
modifie.setNom("Smith");
|
|
modifie.setPrenom("Jane");
|
|
modifie.setTelephone("0600000001");
|
|
modifie.setDateNaissance(LocalDate.of(1992, 3, 10));
|
|
modifie.setActif(false);
|
|
|
|
Membre updated = membreService.mettreAJourMembre(id, modifie);
|
|
|
|
assertThat(updated.getNom()).isEqualTo("Smith");
|
|
assertThat(updated.getPrenom()).isEqualTo("Jane");
|
|
assertThat(updated.getTelephone()).isEqualTo("0600000001");
|
|
assertThat(updated.getActif()).isFalse();
|
|
}
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("mettreAJourMembre: email modifié, nouveau email disponible")
|
|
void mettreAJourMembre_newEmailAvailable_updatesEmail() {
|
|
Membre existing = new Membre();
|
|
existing.setEmail("old@unionflow.dev");
|
|
existing.setNom("Doe");
|
|
existing.setPrenom("John");
|
|
existing.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
existing.setNumeroMembre("UF2024-OLD");
|
|
existing.setActif(true);
|
|
existing.setStatutCompte("ACTIF");
|
|
existing.setMembresOrganisations(new ArrayList<>());
|
|
existing.setAdresses(new ArrayList<>());
|
|
em.persist(existing);
|
|
em.flush();
|
|
UUID id = existing.getId();
|
|
|
|
Membre modifie = new Membre();
|
|
modifie.setEmail("new@unionflow.dev");
|
|
modifie.setNom("Smith");
|
|
modifie.setPrenom("Jane");
|
|
modifie.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
modifie.setActif(true);
|
|
|
|
Membre updated = membreService.mettreAJourMembre(id, modifie);
|
|
|
|
assertThat(updated.getEmail()).isEqualTo("new@unionflow.dev");
|
|
}
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("mettreAJourMembre: email modifié mais déjà pris → IllegalArgumentException")
|
|
void mettreAJourMembre_emailAlreadyTaken_throws() {
|
|
Membre existing = new Membre();
|
|
existing.setEmail("old2@unionflow.dev");
|
|
existing.setNom("Doe");
|
|
existing.setPrenom("John");
|
|
existing.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
existing.setNumeroMembre("UF2024-OLD2");
|
|
existing.setActif(true);
|
|
existing.setStatutCompte("ACTIF");
|
|
existing.setMembresOrganisations(new ArrayList<>());
|
|
existing.setAdresses(new ArrayList<>());
|
|
em.persist(existing);
|
|
em.flush();
|
|
UUID id = existing.getId();
|
|
|
|
Membre modifie = new Membre();
|
|
modifie.setEmail("taken@unionflow.dev");
|
|
modifie.setNom("Smith");
|
|
modifie.setPrenom("Jane");
|
|
modifie.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
|
|
when(membreRepository.findByEmail("taken@unionflow.dev"))
|
|
.thenReturn(Optional.of(membreFixture("taken@unionflow.dev")));
|
|
|
|
assertThatThrownBy(() -> membreService.mettreAJourMembre(id, modifie))
|
|
.isInstanceOf(IllegalArgumentException.class)
|
|
.hasMessageContaining("email existe déjà");
|
|
}
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("trouverParId: retourne le membre si trouvé")
|
|
void trouverParId_found() {
|
|
Membre m = new Membre();
|
|
m.setEmail("find@unionflow.dev");
|
|
m.setNom("Doe");
|
|
m.setPrenom("John");
|
|
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
m.setNumeroMembre("UF2024-FIND");
|
|
m.setActif(true);
|
|
m.setStatutCompte("ACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
m.setAdresses(new ArrayList<>());
|
|
em.persist(m);
|
|
em.flush();
|
|
UUID generatedId = m.getId();
|
|
|
|
Optional<Membre> result = membreService.trouverParId(generatedId);
|
|
|
|
assertThat(result).isPresent();
|
|
assertThat(result.get().getEmail()).isEqualTo("find@unionflow.dev");
|
|
}
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("desactiverMembre: flag actif mis à false")
|
|
void desactiverMembre_setsActifFalse() {
|
|
Membre existing = new Membre();
|
|
existing.setEmail("active@unionflow.dev");
|
|
existing.setNom("Doe");
|
|
existing.setPrenom("John");
|
|
existing.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
existing.setNumeroMembre("UF2024-ACTIVE");
|
|
existing.setActif(true);
|
|
existing.setStatutCompte("ACTIF");
|
|
existing.setMembresOrganisations(new ArrayList<>());
|
|
existing.setAdresses(new ArrayList<>());
|
|
em.persist(existing);
|
|
em.flush();
|
|
UUID id = existing.getId();
|
|
|
|
membreService.desactiverMembre(id);
|
|
|
|
// existing is the managed entity — service modifies the same instance
|
|
assertThat(existing.getActif()).isFalse();
|
|
}
|
|
|
|
// =========================================================================
|
|
// getOrganisationIdsForCurrentUserIfAdminOrg — branches manquantes
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("listerMembres avec SUPER_ADMIN + ADMIN_ORGANISATION: pas de restriction (branche SUPER_ADMIN couverte)")
|
|
void listerMembres_superAdminAndAdminOrg_returnsAll() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> all = List.of(membreFixture("super@test.dev"));
|
|
|
|
Principal principal = () -> "super@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION", "SUPER_ADMIN"));
|
|
when(membreRepository.findAll(page, sort)).thenReturn(all);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("listerMembres avec ADMIN_ORGANISATION + email null depuis principal: retourne tous les membres")
|
|
void listerMembres_adminOrg_nullEmail_returnsAll() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> all = List.of(membreFixture("n@test.dev"));
|
|
|
|
// Principal whose getName() returns null
|
|
Principal principal = () -> null;
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(membreRepository.findAll(page, sort)).thenReturn(all);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
// =========================================================================
|
|
// searchMembresAdvanced + buildOrderByClause — branche Descending avec données réelles
|
|
// Nécessite totalElements > 0 pour que buildOrderByClause soit appelé
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("searchMembresAdvanced avec données réelles et sort Descending couvre buildOrderByClause DESC")
|
|
void searchMembresAdvanced_avecDonnees_sortDescending_couvreDescBranch() {
|
|
// Persister un membre pour que totalElements > 0
|
|
Membre m = new Membre();
|
|
m.setEmail("search-desc-" + UUID.randomUUID() + "@test.com");
|
|
m.setNom("Zorro");
|
|
m.setPrenom("Test");
|
|
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
m.setNumeroMembre("UF-DESC-" + UUID.randomUUID().toString().substring(0, 8));
|
|
m.setActif(true);
|
|
m.setStatutCompte("ACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
m.setAdresses(new ArrayList<>());
|
|
em.persist(m);
|
|
em.flush();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
// Sort Descending → totalElements > 0 → buildOrderByClause appelé avec Descending → branche DESC couverte
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), Sort.by("nom").descending());
|
|
|
|
assertThat(result).isNotNull();
|
|
assertThat(result.getTotalElements()).isGreaterThan(0L);
|
|
}
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("searchMembresAdvanced avec données réelles et sort colonnes vides couvre buildOrderByClause isEmpty()")
|
|
void searchMembresAdvanced_avecDonnees_sortColonnesVides_couvreIsEmptyBranch() throws Exception {
|
|
// Persister un membre pour que totalElements > 0 → buildOrderByClause est appelé
|
|
Membre m = new Membre();
|
|
m.setEmail("search-empty-sort-" + UUID.randomUUID() + "@test.com");
|
|
m.setNom("Alpha");
|
|
m.setPrenom("Test");
|
|
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
m.setNumeroMembre("UF-EMPTY-" + UUID.randomUUID().toString().substring(0, 8));
|
|
m.setActif(true);
|
|
m.setStatutCompte("ACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
m.setAdresses(new ArrayList<>());
|
|
em.persist(m);
|
|
em.flush();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
// Sort non-null mais colonnes vides → buildOrderByClause appelé → branche isEmpty() couverte
|
|
Sort emptySort;
|
|
try {
|
|
java.lang.reflect.Constructor<Sort> ctor = Sort.class.getDeclaredConstructor();
|
|
ctor.setAccessible(true);
|
|
emptySort = ctor.newInstance();
|
|
} catch (NoSuchMethodException e) {
|
|
// Si le constructeur n'est pas accessible, le test passe sans erreur
|
|
return;
|
|
}
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 10), emptySort);
|
|
|
|
assertThat(result).isNotNull();
|
|
assertThat(result.getTotalElements()).isGreaterThan(0L);
|
|
}
|
|
|
|
// =========================================================================
|
|
// creerMembre — branche getNumeroMembre().isEmpty() = true (non-null mais vide)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("creerMembre avec numeroMembre vide ('') → génère un nouveau numéro (branche isEmpty()=true)")
|
|
void creerMembre_emptyNumeroMembre_generatesNew() {
|
|
Membre m = new Membre();
|
|
m.setEmail("creer-empty-num-" + UUID.randomUUID() + "@test.dev");
|
|
m.setNom("Vide");
|
|
m.setPrenom("Num");
|
|
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
m.setNumeroMembre(""); // non-null mais vide → isEmpty()=true → génère un nouveau numéro
|
|
m.setActif(true);
|
|
m.setStatutCompte("ACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
m.setAdresses(new ArrayList<>());
|
|
|
|
Membre created = membreService.creerMembre(m);
|
|
|
|
assertThat(created).isNotNull();
|
|
assertThat(created.getNumeroMembre()).isNotNull().isNotEmpty();
|
|
}
|
|
|
|
// =========================================================================
|
|
// getOrganisationIdsForCurrentUserIfAdminOrg — branche email.isBlank()=true
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("listerMembres avec ADMIN_ORGANISATION + email blanc depuis principal → retourne tous (branche isBlank=true L224)")
|
|
void listerMembres_adminOrg_blankEmail_returnsAll() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
List<Membre> all = List.of(membreFixture("blank-email@test.dev"));
|
|
|
|
// Principal dont getName() retourne une chaîne vide → isBlank()=true
|
|
Principal principal = () -> " ";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
when(membreRepository.findAll(page, sort)).thenReturn(all);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).hasSize(1);
|
|
}
|
|
|
|
// =========================================================================
|
|
// getOrganisationIdsForCurrentUserIfAdminOrg — branche orgs == null (L226)
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@DisplayName("listerMembres avec ADMIN_ORGANISATION + service retourne null → retourne liste vide (branche orgs==null L226)")
|
|
void listerMembres_adminOrg_nullOrgsList_returnsEmpty() {
|
|
Page page = Page.of(0, 10);
|
|
Sort sort = Sort.by("nom").ascending();
|
|
|
|
Principal principal = () -> "admin-null-orgs@org.dev";
|
|
when(securityIdentity.getPrincipal()).thenReturn(principal);
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN_ORGANISATION"));
|
|
// listerOrganisationsPourUtilisateur retourne null → orgs == null = true → Optional.of(Set.of())
|
|
when(organisationService.listerOrganisationsPourUtilisateur("admin-null-orgs@org.dev"))
|
|
.thenReturn(null);
|
|
|
|
List<Membre> result = membreService.listerMembres(page, sort);
|
|
|
|
assertThat(result).isEmpty();
|
|
}
|
|
|
|
// =========================================================================
|
|
// addSearchCriteria — branche getStatut()==null && includeInactifs==true (L595)
|
|
// Couvre la branche "else if(!includeInactifs) = false" → rien n'est ajouté
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("searchMembresAdvanced avec includeInactifs=true et statut=null → pas de filtre actif (branche else if false)")
|
|
void searchMembresAdvanced_includeInactifsTrue_noActifFilter() {
|
|
// Persister un membre inactif
|
|
Membre m = new Membre();
|
|
m.setEmail("search-inactif-" + UUID.randomUUID() + "@test.com");
|
|
m.setNom("Inactif");
|
|
m.setPrenom("Test");
|
|
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
m.setNumeroMembre("UF-INACT-" + UUID.randomUUID().toString().substring(0, 8));
|
|
m.setActif(false); // inactif
|
|
m.setStatutCompte("INACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
m.setAdresses(new ArrayList<>());
|
|
em.persist(m);
|
|
em.flush();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
// includeInactifs=true, statut=null → condition `else if(!includeInactifs)` = false → pas de filtre actif
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder()
|
|
.includeInactifs(true)
|
|
.build();
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 100), Sort.by("nom").ascending());
|
|
|
|
assertThat(result).isNotNull();
|
|
// Les membres inactifs sont inclus
|
|
assertThat(result.getTotalElements()).isGreaterThan(0L);
|
|
}
|
|
|
|
// =========================================================================
|
|
// lambda$calculateSearchStatistics$11:710 — branche r != null && !r.isEmpty() false (r = "")
|
|
// =========================================================================
|
|
|
|
@Test
|
|
@TestTransaction
|
|
@DisplayName("calculateSearchStatistics avec région vide ('') couvre la branche r != null && !r.isEmpty() = false")
|
|
void searchMembresAdvanced_regionVide_brancheFalse() {
|
|
// Persister un membre avec une adresse dont la région est une chaîne vide
|
|
Membre m = new Membre();
|
|
m.setEmail("search-region-empty-" + UUID.randomUUID() + "@test.com");
|
|
m.setNom("RegionVide");
|
|
m.setPrenom("Test");
|
|
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
|
m.setNumeroMembre("UF-REGV-" + UUID.randomUUID().toString().substring(0, 8));
|
|
m.setActif(true);
|
|
m.setStatutCompte("ACTIF");
|
|
m.setMembresOrganisations(new ArrayList<>());
|
|
|
|
// Adresse avec région = "" (non-null mais vide → !r.isEmpty() = false → filtrée)
|
|
dev.lions.unionflow.server.entity.Adresse adresse = new dev.lions.unionflow.server.entity.Adresse();
|
|
adresse.setRegion(""); // chaîne vide → filtrée par lambda
|
|
adresse.setTypeAdresse("DOMICILE"); // NOT NULL constraint
|
|
adresse.setMembre(m);
|
|
m.setAdresses(List.of(adresse));
|
|
|
|
em.persist(m);
|
|
em.flush();
|
|
|
|
when(securityIdentity.getRoles()).thenReturn(Set.of("ADMIN"));
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria criteria =
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria.builder().build();
|
|
|
|
dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO result =
|
|
membreService.searchMembresAdvanced(criteria, Page.of(0, 100), Sort.by("nom").ascending());
|
|
|
|
assertThat(result).isNotNull();
|
|
assertThat(result.getTotalElements()).isGreaterThan(0L);
|
|
}
|
|
}
|