feat(messaging): module messagerie unifié avec contact policies + member blocks
Refactor complet : fusion de Conversation + Message en un module Messaging unique avec ContactPolicy (règles qui-peut-parler-à-qui) et MemberBlock (blocages utilisateur). - Migration V28 : tables conversations/conversation_participants/messages/ contact_policies/member_blocks - Nouvelles entités : ContactPolicy, ConversationParticipant, MemberBlock (Conversation/Message mises à jour avec relations) - Nouvelles repositories : ContactPolicyRepository, ConversationParticipantRepository, MemberBlockRepository - MessagingResource (nouveau) remplace ConversationResource + MessageResource - MessagingService (nouveau) remplace ConversationService + MessageService avec vérifications appartenance org + policies + blocages avant envoi - Anciens fichiers Conversation/Message Resource/Service/Tests supprimés
This commit is contained in:
@@ -1,95 +1,166 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
|
||||
import dev.lions.unionflow.server.api.enums.messagerie.StatutConversation;
|
||||
import dev.lions.unionflow.server.api.enums.messagerie.TypeConversation;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
|
||||
@DisplayName("Conversation")
|
||||
class ConversationTest {
|
||||
|
||||
private static Membre newMembre() {
|
||||
Membre m = new Membre();
|
||||
m.setId(UUID.randomUUID());
|
||||
m.setNumeroMembre("M1");
|
||||
m.setPrenom("Alpha");
|
||||
m.setNom("Diallo");
|
||||
m.setEmail("alpha@test.com");
|
||||
m.setDateNaissance(LocalDate.now());
|
||||
return m;
|
||||
}
|
||||
|
||||
private static Organisation newOrg() {
|
||||
Organisation org = new Organisation();
|
||||
org.setId(UUID.randomUUID());
|
||||
org.setNom("Tontine Test");
|
||||
return org;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getters/setters de base")
|
||||
@DisplayName("getters/setters — tous les champs")
|
||||
void gettersSetters() {
|
||||
Conversation c = new Conversation();
|
||||
c.setName("Groupe Test");
|
||||
c.setDescription("Description groupe");
|
||||
c.setType(ConversationType.GROUP);
|
||||
c.setIsMuted(false);
|
||||
c.setIsPinned(true);
|
||||
c.setIsArchived(false);
|
||||
Conversation conv = new Conversation();
|
||||
Organisation org = newOrg();
|
||||
conv.setOrganisation(org);
|
||||
conv.setTypeConversation(TypeConversation.DIRECTE);
|
||||
conv.setStatut(StatutConversation.ACTIVE);
|
||||
conv.setTitre("Discussion");
|
||||
conv.setNombreMessages(5);
|
||||
|
||||
assertThat(c.getName()).isEqualTo("Groupe Test");
|
||||
assertThat(c.getDescription()).isEqualTo("Description groupe");
|
||||
assertThat(c.getType()).isEqualTo(ConversationType.GROUP);
|
||||
assertThat(c.getIsMuted()).isFalse();
|
||||
assertThat(c.getIsPinned()).isTrue();
|
||||
assertThat(c.getIsArchived()).isFalse();
|
||||
assertThat(conv.getOrganisation()).isEqualTo(org);
|
||||
assertThat(conv.getTypeConversation()).isEqualTo(TypeConversation.DIRECTE);
|
||||
assertThat(conv.getStatut()).isEqualTo(StatutConversation.ACTIVE);
|
||||
assertThat(conv.getTitre()).isEqualTo("Discussion");
|
||||
assertThat(conv.getNombreMessages()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("onUpdate (PreUpdate) - met à jour updatedAt via réflexion")
|
||||
void onUpdate_setsUpdatedAt() throws Exception {
|
||||
Conversation c = new Conversation();
|
||||
assertThat(c.getUpdatedAt()).isNull();
|
||||
|
||||
Method onUpdate = Conversation.class.getDeclaredMethod("onUpdate");
|
||||
onUpdate.setAccessible(true);
|
||||
|
||||
LocalDateTime before = LocalDateTime.now().minusSeconds(1);
|
||||
onUpdate.invoke(c);
|
||||
LocalDateTime after = LocalDateTime.now().plusSeconds(1);
|
||||
|
||||
assertThat(c.getUpdatedAt()).isNotNull();
|
||||
assertThat(c.getUpdatedAt()).isAfter(before);
|
||||
assertThat(c.getUpdatedAt()).isBefore(after);
|
||||
@DisplayName("estActive — ACTIVE → true")
|
||||
void estActive_active() {
|
||||
Conversation conv = buildMinimal();
|
||||
conv.setStatut(StatutConversation.ACTIVE);
|
||||
assertThat(conv.estActive()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("onUpdate appelé deux fois met à jour updatedAt à chaque fois")
|
||||
void onUpdate_calledTwice_updatesEachTime() throws Exception {
|
||||
Conversation c = new Conversation();
|
||||
|
||||
Method onUpdate = Conversation.class.getDeclaredMethod("onUpdate");
|
||||
onUpdate.setAccessible(true);
|
||||
|
||||
onUpdate.invoke(c);
|
||||
LocalDateTime first = c.getUpdatedAt();
|
||||
|
||||
// petit délai pour différencier les timestamps
|
||||
Thread.sleep(5);
|
||||
|
||||
onUpdate.invoke(c);
|
||||
LocalDateTime second = c.getUpdatedAt();
|
||||
|
||||
assertThat(second).isAfterOrEqualTo(first);
|
||||
@DisplayName("estActive — ARCHIVEE → false")
|
||||
void estActive_archivee() {
|
||||
Conversation conv = buildMinimal();
|
||||
conv.setStatut(StatutConversation.ARCHIVEE);
|
||||
assertThat(conv.estActive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("participants initialisé à liste vide")
|
||||
@DisplayName("archiver — passe le statut à ARCHIVEE")
|
||||
void archiver() {
|
||||
Conversation conv = buildMinimal();
|
||||
conv.setStatut(StatutConversation.ACTIVE);
|
||||
conv.archiver();
|
||||
assertThat(conv.getStatut()).isEqualTo(StatutConversation.ARCHIVEE);
|
||||
assertThat(conv.estActive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("enregistrerNouveauMessage — incrémente le compteur et met à jour la date")
|
||||
void enregistrerNouveauMessage() {
|
||||
Conversation conv = buildMinimal();
|
||||
conv.setNombreMessages(3);
|
||||
conv.enregistrerNouveauMessage();
|
||||
assertThat(conv.getNombreMessages()).isEqualTo(4);
|
||||
assertThat(conv.getDernierMessageAt()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("enregistrerNouveauMessage — part de null")
|
||||
void enregistrerNouveauMessage_partDeNull() {
|
||||
Conversation conv = buildMinimal();
|
||||
conv.setNombreMessages(null);
|
||||
conv.enregistrerNouveauMessage();
|
||||
assertThat(conv.getNombreMessages()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("onCreate — initialise statut et nombreMessages si null")
|
||||
void onCreate_initDefaults() {
|
||||
Conversation conv = new Conversation();
|
||||
conv.setOrganisation(newOrg());
|
||||
conv.setTypeConversation(TypeConversation.DIRECTE);
|
||||
|
||||
conv.onCreate();
|
||||
|
||||
assertThat(conv.getStatut()).isEqualTo(StatutConversation.ACTIVE);
|
||||
assertThat(conv.getNombreMessages()).isEqualTo(0);
|
||||
assertThat(conv.getDateCreation()).isNotNull();
|
||||
assertThat(conv.getActif()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("ROLE_CANAL — roleCible renseigné")
|
||||
void roleCanalType() {
|
||||
Conversation conv = buildMinimal();
|
||||
conv.setTypeConversation(TypeConversation.ROLE_CANAL);
|
||||
conv.setRoleCible("TRESORIER");
|
||||
conv.setTitre("Trésorier");
|
||||
|
||||
assertThat(conv.getTypeConversation()).isEqualTo(TypeConversation.ROLE_CANAL);
|
||||
assertThat(conv.getRoleCible()).isEqualTo("TRESORIER");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("participants initialisé à liste vide (builder default)")
|
||||
void participants_initializedEmpty() {
|
||||
Conversation c = new Conversation();
|
||||
assertThat(c.getParticipants()).isNotNull().isEmpty();
|
||||
Conversation conv = Conversation.builder()
|
||||
.organisation(newOrg())
|
||||
.typeConversation(TypeConversation.DIRECTE)
|
||||
.build();
|
||||
assertThat(conv.getParticipants()).isNotNull().isEmpty();
|
||||
assertThat(conv.getMessages()).isNotNull().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("messages initialisé à liste vide")
|
||||
void messages_initializedEmpty() {
|
||||
Conversation c = new Conversation();
|
||||
assertThat(c.getMessages()).isNotNull().isEmpty();
|
||||
@DisplayName("equals et hashCode")
|
||||
void equalsHashCode() {
|
||||
UUID id = UUID.randomUUID();
|
||||
Organisation org = newOrg();
|
||||
|
||||
Conversation a = buildMinimal();
|
||||
a.setId(id);
|
||||
a.setOrganisation(org);
|
||||
Conversation b = buildMinimal();
|
||||
b.setId(id);
|
||||
b.setOrganisation(org);
|
||||
|
||||
assertThat(a).isEqualTo(b);
|
||||
assertThat(a.hashCode()).isEqualTo(b.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("isMuted et isPinned et isArchived défaut false")
|
||||
void defaultFlags_areFalse() {
|
||||
Conversation c = new Conversation();
|
||||
assertThat(c.getIsMuted()).isFalse();
|
||||
assertThat(c.getIsPinned()).isFalse();
|
||||
assertThat(c.getIsArchived()).isFalse();
|
||||
@DisplayName("toString non null")
|
||||
void toString_nonNull() {
|
||||
assertThat(buildMinimal().toString()).isNotNull().isNotEmpty();
|
||||
}
|
||||
|
||||
private Conversation buildMinimal() {
|
||||
Conversation conv = new Conversation();
|
||||
conv.setOrganisation(newOrg());
|
||||
conv.setTypeConversation(TypeConversation.DIRECTE);
|
||||
conv.setStatut(StatutConversation.ACTIVE);
|
||||
conv.setNombreMessages(0);
|
||||
return conv;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,173 @@
|
||||
package dev.lions.unionflow.server.entity;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
|
||||
import dev.lions.unionflow.server.api.enums.messagerie.TypeContenu;
|
||||
import dev.lions.unionflow.server.api.enums.messagerie.TypeConversation;
|
||||
import dev.lions.unionflow.server.api.enums.messagerie.StatutConversation;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DisplayName("Message")
|
||||
class MessageTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("markAsRead sets status to READ and sets readAt")
|
||||
void markAsRead_setsStatusAndReadAt() {
|
||||
Message m = new Message();
|
||||
m.setStatus(MessageStatus.SENT);
|
||||
assertThat(m.getReadAt()).isNull();
|
||||
private static Membre newMembre() {
|
||||
Membre m = new Membre();
|
||||
m.setId(UUID.randomUUID());
|
||||
m.setNumeroMembre("M1");
|
||||
m.setPrenom("Alpha");
|
||||
m.setNom("Diallo");
|
||||
m.setEmail("alpha@test.com");
|
||||
m.setDateNaissance(LocalDate.now());
|
||||
return m;
|
||||
}
|
||||
|
||||
m.markAsRead();
|
||||
|
||||
assertThat(m.getStatus()).isEqualTo(MessageStatus.READ);
|
||||
assertThat(m.getReadAt()).isNotNull();
|
||||
private static Conversation newConversation() {
|
||||
Organisation org = new Organisation();
|
||||
org.setId(UUID.randomUUID());
|
||||
org.setNom("Org Test");
|
||||
Conversation c = new Conversation();
|
||||
c.setId(UUID.randomUUID());
|
||||
c.setOrganisation(org);
|
||||
c.setTypeConversation(TypeConversation.DIRECTE);
|
||||
c.setStatut(StatutConversation.ACTIVE);
|
||||
c.setNombreMessages(0);
|
||||
return c;
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("markAsEdited sets isEdited true and sets editedAt")
|
||||
void markAsEdited_setsIsEditedAndEditedAt() {
|
||||
Message m = new Message();
|
||||
assertThat(m.getIsEdited()).isFalse();
|
||||
assertThat(m.getEditedAt()).isNull();
|
||||
@DisplayName("getters/setters — message texte")
|
||||
void gettersSetters_texte() {
|
||||
Message msg = new Message();
|
||||
Conversation conv = newConversation();
|
||||
Membre expediteur = newMembre();
|
||||
msg.setConversation(conv);
|
||||
msg.setExpediteur(expediteur);
|
||||
msg.setTypeMessage(TypeContenu.TEXTE);
|
||||
msg.setContenu("Bonjour tout le monde !");
|
||||
|
||||
m.markAsEdited();
|
||||
assertThat(msg.getConversation()).isEqualTo(conv);
|
||||
assertThat(msg.getExpediteur()).isEqualTo(expediteur);
|
||||
assertThat(msg.getTypeMessage()).isEqualTo(TypeContenu.TEXTE);
|
||||
assertThat(msg.getContenu()).isEqualTo("Bonjour tout le monde !");
|
||||
}
|
||||
|
||||
assertThat(m.getIsEdited()).isTrue();
|
||||
assertThat(m.getEditedAt()).isNotNull();
|
||||
@Test
|
||||
@DisplayName("getters/setters — note vocale")
|
||||
void gettersSetters_vocal() {
|
||||
Message msg = new Message();
|
||||
msg.setConversation(newConversation());
|
||||
msg.setExpediteur(newMembre());
|
||||
msg.setTypeMessage(TypeContenu.VOCAL);
|
||||
msg.setUrlFichier("https://storage.example.com/audio.opus");
|
||||
msg.setDureeAudio(45);
|
||||
|
||||
assertThat(msg.getTypeMessage()).isEqualTo(TypeContenu.VOCAL);
|
||||
assertThat(msg.getUrlFichier()).isEqualTo("https://storage.example.com/audio.opus");
|
||||
assertThat(msg.getDureeAudio()).isEqualTo(45);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("estTextuel — TEXTE → true")
|
||||
void estTextuel_true() {
|
||||
Message msg = buildMinimal(TypeContenu.TEXTE);
|
||||
assertThat(msg.estTextuel()).isTrue();
|
||||
assertThat(msg.estVocal()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("estVocal — VOCAL → true")
|
||||
void estVocal_true() {
|
||||
Message msg = buildMinimal(TypeContenu.VOCAL);
|
||||
assertThat(msg.estVocal()).isTrue();
|
||||
assertThat(msg.estTextuel()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("estSupprime — null → false")
|
||||
void estSupprime_nullFalse() {
|
||||
Message msg = buildMinimal(TypeContenu.TEXTE);
|
||||
msg.setSupprimeLe(null);
|
||||
assertThat(msg.estSupprime()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("supprimer — contenu remplacé par marqueur, urlFichier null")
|
||||
void supprimer() {
|
||||
Message msg = buildMinimal(TypeContenu.TEXTE);
|
||||
msg.setContenu("Message secret");
|
||||
msg.setUrlFichier("https://example.com/file");
|
||||
|
||||
msg.supprimer();
|
||||
|
||||
assertThat(msg.estSupprime()).isTrue();
|
||||
assertThat(msg.getContenu()).isEqualTo("[Message supprimé]");
|
||||
assertThat(msg.getUrlFichier()).isNull();
|
||||
assertThat(msg.getSupprimeLe()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("onCreate — initialise typeMessage si null")
|
||||
void onCreate_initDefaults() {
|
||||
Message msg = new Message();
|
||||
msg.setConversation(newConversation());
|
||||
msg.setExpediteur(newMembre());
|
||||
msg.setContenu("Test");
|
||||
|
||||
msg.onCreate();
|
||||
|
||||
assertThat(msg.getTypeMessage()).isEqualTo(TypeContenu.TEXTE);
|
||||
assertThat(msg.getDateCreation()).isNotNull();
|
||||
assertThat(msg.getActif()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("onCreate — préserve typeMessage existant")
|
||||
void onCreate_preservesType() {
|
||||
Message msg = new Message();
|
||||
msg.setConversation(newConversation());
|
||||
msg.setExpediteur(newMembre());
|
||||
msg.setTypeMessage(TypeContenu.VOCAL);
|
||||
msg.setUrlFichier("url");
|
||||
msg.setDureeAudio(30);
|
||||
|
||||
msg.onCreate();
|
||||
|
||||
assertThat(msg.getTypeMessage()).isEqualTo(TypeContenu.VOCAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("equals et hashCode")
|
||||
void equalsHashCode() {
|
||||
UUID id = UUID.randomUUID();
|
||||
Message a = buildMinimal(TypeContenu.TEXTE);
|
||||
a.setId(id);
|
||||
Message b = buildMinimal(TypeContenu.TEXTE);
|
||||
b.setId(id);
|
||||
assertThat(a).isEqualTo(b);
|
||||
assertThat(a.hashCode()).isEqualTo(b.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("toString non null")
|
||||
void toString_nonNull() {
|
||||
assertThat(buildMinimal(TypeContenu.TEXTE).toString()).isNotNull().isNotEmpty();
|
||||
}
|
||||
|
||||
private Message buildMinimal(TypeContenu type) {
|
||||
Message msg = new Message();
|
||||
msg.setConversation(newConversation());
|
||||
msg.setExpediteur(newMembre());
|
||||
msg.setTypeMessage(type);
|
||||
if (TypeContenu.TEXTE.equals(type)) {
|
||||
msg.setContenu("Texte test");
|
||||
} else if (TypeContenu.VOCAL.equals(type)) {
|
||||
msg.setUrlFichier("https://example.com/audio.opus");
|
||||
msg.setDureeAudio(30);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,149 +1,85 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
|
||||
import dev.lions.unionflow.server.api.enums.messagerie.TypeConversation;
|
||||
import dev.lions.unionflow.server.entity.Conversation;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import io.quarkus.test.TestTransaction;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import jakarta.inject.Inject;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests d'intégration pour {@link ConversationRepository}.
|
||||
* Couvre les 3 méthodes : findByParticipant, findByIdAndParticipant, findByOrganisation.
|
||||
*/
|
||||
@QuarkusTest
|
||||
@DisplayName("ConversationRepository")
|
||||
class ConversationRepositoryTest {
|
||||
|
||||
@Inject
|
||||
ConversationRepository conversationRepository;
|
||||
|
||||
@Inject
|
||||
OrganisationRepository organisationRepository;
|
||||
|
||||
private Organisation createOrganisation() {
|
||||
Organisation o = new Organisation();
|
||||
o.setNom("Org Conversation");
|
||||
o.setTypeOrganisation("ASSOCIATION");
|
||||
o.setStatut("ACTIVE");
|
||||
o.setEmail("conv-" + UUID.randomUUID() + "@test.com");
|
||||
o.setActif(true);
|
||||
o.setDateCreation(LocalDateTime.now());
|
||||
organisationRepository.persist(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
private Conversation createConversation(String name, Organisation org) {
|
||||
Conversation c = new Conversation();
|
||||
c.setName(name);
|
||||
c.setType(ConversationType.GROUP);
|
||||
c.setOrganisation(org);
|
||||
c.setActif(true);
|
||||
c.setDateCreation(LocalDateTime.now());
|
||||
conversationRepository.persist(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// findByParticipant — branches avec includeArchived=true et false
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByParticipant avec includeArchived=true retourne liste vide si aucune conversation")
|
||||
void findByParticipant_noConversations_returnsEmptyList() {
|
||||
UUID randomMembre = UUID.randomUUID();
|
||||
|
||||
List<Conversation> result = conversationRepository.findByParticipant(randomMembre, true);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result).isEmpty();
|
||||
@DisplayName("findById retourne null pour UUID inexistant")
|
||||
void findById_inexistant_returnsNull() {
|
||||
assertThat(conversationRepository.findById(UUID.randomUUID())).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByParticipant avec includeArchived=false retourne liste vide si aucune conversation")
|
||||
void findByParticipant_excludeArchived_returnsEmptyList() {
|
||||
UUID randomMembre = UUID.randomUUID();
|
||||
|
||||
List<Conversation> result = conversationRepository.findByParticipant(randomMembre, false);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// findByIdAndParticipant
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByIdAndParticipant retourne empty pour ID et membreId inconnus")
|
||||
void findByIdAndParticipant_unknownIds_returnsEmpty() {
|
||||
Optional<Conversation> result = conversationRepository.findByIdAndParticipant(
|
||||
UUID.randomUUID(), UUID.randomUUID());
|
||||
|
||||
assertThat(result).isEmpty();
|
||||
@DisplayName("findConversationById retourne empty pour UUID inexistant")
|
||||
void findConversationById_inexistant_returnsEmpty() {
|
||||
Optional<Conversation> opt = conversationRepository.findConversationById(UUID.randomUUID());
|
||||
assertThat(opt).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByIdAndParticipant retourne empty pour conversationId inexistant")
|
||||
void findByIdAndParticipant_nonExistentConversation_returnsEmpty() {
|
||||
UUID nonExistentId = UUID.randomUUID();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
Optional<Conversation> result = conversationRepository.findByIdAndParticipant(
|
||||
nonExistentId, membreId);
|
||||
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// findByOrganisation
|
||||
// =========================================================================
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByOrganisation retourne liste vide si organisation sans conversation")
|
||||
void findByOrganisation_noConversations_returnsEmptyList() {
|
||||
Organisation org = createOrganisation();
|
||||
|
||||
List<Conversation> result = conversationRepository.findByOrganisation(org.getId());
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result).isEmpty();
|
||||
@DisplayName("findByMembreId retourne liste non nulle pour membre inexistant")
|
||||
void findByMembreId_inconnu_returnsEmpty() {
|
||||
List<Conversation> list = conversationRepository.findByMembreId(UUID.randomUUID());
|
||||
assertThat(list).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByOrganisation retourne les conversations de l'organisation persistées")
|
||||
void findByOrganisation_withConversations_returnsList() {
|
||||
Organisation org = createOrganisation();
|
||||
createConversation("Conv1-" + UUID.randomUUID(), org);
|
||||
createConversation("Conv2-" + UUID.randomUUID(), org);
|
||||
|
||||
List<Conversation> result = conversationRepository.findByOrganisation(org.getId());
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result).hasSize(2);
|
||||
@DisplayName("findConversationDirecte retourne empty si aucune conversation")
|
||||
void findConversationDirecte_inconnu_returnsEmpty() {
|
||||
Optional<Conversation> opt = conversationRepository.findConversationDirecte(
|
||||
UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID());
|
||||
assertThat(opt).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByOrganisation retourne liste vide pour organisationId inexistant")
|
||||
void findByOrganisation_unknownOrganisationId_returnsEmptyList() {
|
||||
List<Conversation> result = conversationRepository.findByOrganisation(UUID.randomUUID());
|
||||
@DisplayName("findCanalRole retourne empty si aucun canal")
|
||||
void findCanalRole_inconnu_returnsEmpty() {
|
||||
Optional<Conversation> opt = conversationRepository.findCanalRole(UUID.randomUUID(), "TRESORIER");
|
||||
assertThat(opt).isEmpty();
|
||||
}
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result).isEmpty();
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findActivesByMembre retourne liste non nulle")
|
||||
void findActivesByMembre_returnsNonNull() {
|
||||
List<Conversation> list = conversationRepository.findActivesByMembre(UUID.randomUUID());
|
||||
assertThat(list).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("count retourne un nombre >= 0")
|
||||
void count_returnsNonNegative() {
|
||||
assertThat(conversationRepository.count()).isGreaterThanOrEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("listAll retourne une liste non nulle")
|
||||
void listAll_returnsNonNull() {
|
||||
assertThat(conversationRepository.listAll()).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,256 +1,69 @@
|
||||
package dev.lions.unionflow.server.repository;
|
||||
|
||||
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessagePriority;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageType;
|
||||
import dev.lions.unionflow.server.entity.Conversation;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Message;
|
||||
import io.quarkus.test.TestTransaction;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@QuarkusTest
|
||||
@DisplayName("MessageRepository — tests des méthodes de requête")
|
||||
@DisplayName("MessageRepository")
|
||||
class MessageRepositoryTest {
|
||||
|
||||
@Inject
|
||||
MessageRepository messageRepository;
|
||||
|
||||
@Inject
|
||||
EntityManager em;
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
private Membre persistMembre() {
|
||||
Membre m = new Membre();
|
||||
m.setNumeroMembre("MSG-MEM-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase());
|
||||
m.setPrenom("Jean");
|
||||
m.setNom("Messagerie");
|
||||
m.setEmail("msg." + UUID.randomUUID() + "@test.com");
|
||||
m.setDateNaissance(LocalDate.of(1990, 1, 1));
|
||||
m.setActif(true);
|
||||
em.persist(m);
|
||||
em.flush();
|
||||
return m;
|
||||
}
|
||||
|
||||
private Conversation persistConversation() {
|
||||
Conversation conv = new Conversation();
|
||||
conv.setName("Conv Test " + UUID.randomUUID().toString().substring(0, 8));
|
||||
conv.setType(ConversationType.GROUP);
|
||||
conv.setActif(true);
|
||||
em.persist(conv);
|
||||
em.flush();
|
||||
return conv;
|
||||
}
|
||||
|
||||
private Message persistMessage(Conversation conv, Membre sender, MessageStatus status) {
|
||||
Message msg = new Message();
|
||||
msg.setConversation(conv);
|
||||
msg.setSender(sender);
|
||||
msg.setSenderName(sender.getPrenom() + " " + sender.getNom());
|
||||
msg.setContent("Contenu test " + UUID.randomUUID());
|
||||
msg.setType(MessageType.INDIVIDUAL);
|
||||
msg.setStatus(status);
|
||||
msg.setPriority(MessagePriority.NORMAL);
|
||||
msg.setIsEdited(false);
|
||||
msg.setIsDeleted(false);
|
||||
msg.setActif(true);
|
||||
em.persist(msg);
|
||||
em.flush();
|
||||
return msg;
|
||||
}
|
||||
|
||||
// ─── Tests ───────────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByConversation retourne les messages actifs non supprimés")
|
||||
void findByConversation_retourneMessagesActifs() {
|
||||
Membre sender = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
persistMessage(conv, sender, MessageStatus.SENT);
|
||||
persistMessage(conv, sender, MessageStatus.READ);
|
||||
|
||||
List<Message> messages = messageRepository.findByConversation(conv.getId(), 10);
|
||||
|
||||
assertThat(messages).isNotNull();
|
||||
assertThat(messages).hasSizeGreaterThanOrEqualTo(2);
|
||||
messages.forEach(m -> assertThat(m.getIsDeleted()).isFalse());
|
||||
@DisplayName("findMessageById retourne empty pour UUID inexistant")
|
||||
void findMessageById_inexistant_returnsEmpty() {
|
||||
Optional<Message> opt = messageRepository.findMessageById(UUID.randomUUID());
|
||||
assertThat(opt).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByConversation respecte la limite de pagination")
|
||||
void findByConversation_respecteLimite() {
|
||||
Membre sender = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
persistMessage(conv, sender, MessageStatus.SENT);
|
||||
}
|
||||
|
||||
List<Message> messages = messageRepository.findByConversation(conv.getId(), 3);
|
||||
|
||||
assertThat(messages).hasSizeLessThanOrEqualTo(3);
|
||||
@DisplayName("findByConversationPagine retourne liste vide pour conversation inexistante")
|
||||
void findByConversationPagine_returnsEmpty() {
|
||||
List<Message> list = messageRepository.findByConversationPagine(UUID.randomUUID(), 0, 20);
|
||||
assertThat(list).isNotNull().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByConversation retourne liste vide pour conversation sans messages")
|
||||
void findByConversation_conversationVide_retourneListe() {
|
||||
Conversation conv = persistConversation();
|
||||
|
||||
List<Message> messages = messageRepository.findByConversation(conv.getId(), 10);
|
||||
|
||||
assertThat(messages).isNotNull();
|
||||
assertThat(messages).isEmpty();
|
||||
@DisplayName("countNonLus retourne >= 0 pour conversation inexistante")
|
||||
void countNonLus_returnsNonNegative() {
|
||||
long count = messageRepository.countNonLus(UUID.randomUUID(), UUID.randomUUID());
|
||||
assertThat(count).isGreaterThanOrEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findByConversation exclut les messages supprimés")
|
||||
void findByConversation_exclutMessagesSupprimés() {
|
||||
Membre sender = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
// Message supprimé
|
||||
Message msgSupprime = persistMessage(conv, sender, MessageStatus.SENT);
|
||||
msgSupprime.setIsDeleted(true);
|
||||
em.flush();
|
||||
|
||||
List<Message> messages = messageRepository.findByConversation(conv.getId(), 10);
|
||||
|
||||
assertThat(messages).noneMatch(m -> m.getId().equals(msgSupprime.getId()));
|
||||
@DisplayName("findActifsByConversation retourne liste vide pour conversation inexistante")
|
||||
void findActifsByConversation_returnsEmpty() {
|
||||
List<Message> list = messageRepository.findActifsByConversation(UUID.randomUUID());
|
||||
assertThat(list).isNotNull().isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("countUnreadByConversationAndMember compte les messages SENT et DELIVERED d'autres membres")
|
||||
void countUnreadByConversationAndMember_compteMsgNonLusAutresMembres() {
|
||||
Membre sender = persistMembre();
|
||||
Membre reader = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
persistMessage(conv, sender, MessageStatus.SENT);
|
||||
persistMessage(conv, sender, MessageStatus.DELIVERED);
|
||||
persistMessage(conv, sender, MessageStatus.READ); // déjà lu → exclu
|
||||
|
||||
long count = messageRepository.countUnreadByConversationAndMember(conv.getId(), reader.getId());
|
||||
|
||||
assertThat(count).isGreaterThanOrEqualTo(2);
|
||||
@DisplayName("findDernierMessage retourne empty pour conversation inexistante")
|
||||
void findDernierMessage_returnsEmpty() {
|
||||
Optional<Message> opt = messageRepository.findDernierMessage(UUID.randomUUID());
|
||||
assertThat(opt).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("countUnreadByConversationAndMember exclut les messages du membre lui-même")
|
||||
void countUnreadByConversationAndMember_excluMessagesPropresMembre() {
|
||||
Membre sender = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
persistMessage(conv, sender, MessageStatus.SENT);
|
||||
|
||||
// Le sender lui-même : ses propres messages ne sont pas comptés comme non lus
|
||||
long count = messageRepository.countUnreadByConversationAndMember(conv.getId(), sender.getId());
|
||||
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("countUnreadByConversationAndMember retourne 0 pour conversation vide")
|
||||
void countUnreadByConversationAndMember_conversationVide_retourneZero() {
|
||||
Conversation conv = persistConversation();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
long count = messageRepository.countUnreadByConversationAndMember(conv.getId(), membreId);
|
||||
|
||||
assertThat(count).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("markAllAsReadByConversationAndMember marque les messages SENT et DELIVERED en READ")
|
||||
void markAllAsReadByConversationAndMember_marqueLesMessagesEnRead() {
|
||||
Membre sender = persistMembre();
|
||||
Membre reader = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
persistMessage(conv, sender, MessageStatus.SENT);
|
||||
persistMessage(conv, sender, MessageStatus.DELIVERED);
|
||||
|
||||
int updated = messageRepository.markAllAsReadByConversationAndMember(conv.getId(), reader.getId());
|
||||
|
||||
assertThat(updated).isGreaterThanOrEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("markAllAsReadByConversationAndMember ne touche pas les messages déjà READ")
|
||||
void markAllAsReadByConversationAndMember_neTouchePasMsgDejaRead() {
|
||||
Membre sender = persistMembre();
|
||||
Membre reader = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
persistMessage(conv, sender, MessageStatus.READ); // déjà lu
|
||||
|
||||
int updated = messageRepository.markAllAsReadByConversationAndMember(conv.getId(), reader.getId());
|
||||
|
||||
assertThat(updated).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findLastByConversation retourne le dernier message")
|
||||
void findLastByConversation_retourneDernierMessage() {
|
||||
Membre sender = persistMembre();
|
||||
Conversation conv = persistConversation();
|
||||
// Set explicit dateCreation to guarantee ordering (PrePersist skips if non-null)
|
||||
Message premierMsg = new Message();
|
||||
premierMsg.setConversation(conv);
|
||||
premierMsg.setSender(sender);
|
||||
premierMsg.setSenderName(sender.getPrenom() + " " + sender.getNom());
|
||||
premierMsg.setContent("Premier message");
|
||||
premierMsg.setType(dev.lions.unionflow.server.api.enums.communication.MessageType.INDIVIDUAL);
|
||||
premierMsg.setStatus(MessageStatus.SENT);
|
||||
premierMsg.setPriority(dev.lions.unionflow.server.api.enums.communication.MessagePriority.NORMAL);
|
||||
premierMsg.setIsEdited(false);
|
||||
premierMsg.setIsDeleted(false);
|
||||
premierMsg.setActif(true);
|
||||
premierMsg.setDateCreation(java.time.LocalDateTime.now().minusSeconds(10));
|
||||
em.persist(premierMsg);
|
||||
em.flush();
|
||||
|
||||
Message dernierMsg = persistMessage(conv, sender, MessageStatus.DELIVERED);
|
||||
|
||||
Message last = messageRepository.findLastByConversation(conv.getId());
|
||||
|
||||
assertThat(last).isNotNull();
|
||||
assertThat(last.getId()).isEqualTo(dernierMsg.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findLastByConversation retourne null pour conversation vide")
|
||||
void findLastByConversation_conversationVide_retourneNull() {
|
||||
Conversation conv = persistConversation();
|
||||
|
||||
Message last = messageRepository.findLastByConversation(conv.getId());
|
||||
|
||||
assertThat(last).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestTransaction
|
||||
@DisplayName("findLastByConversation retourne null pour conversation inexistante")
|
||||
void findLastByConversation_conversationInexistante_retourneNull() {
|
||||
Message last = messageRepository.findLastByConversation(UUID.randomUUID());
|
||||
|
||||
assertThat(last).isNull();
|
||||
@DisplayName("count retourne un nombre >= 0")
|
||||
void count_returnsNonNegative() {
|
||||
assertThat(messageRepository.count()).isGreaterThanOrEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,377 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.communication.request.CreateConversationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.communication.response.ConversationResponse;
|
||||
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
|
||||
import dev.lions.unionflow.server.service.ConversationService;
|
||||
import dev.lions.unionflow.server.service.support.SecuriteHelper;
|
||||
import io.quarkus.test.InjectMock;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.security.TestSecurity;
|
||||
import io.restassured.http.ContentType;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests d'intégration REST pour ConversationResource.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-21
|
||||
*/
|
||||
@QuarkusTest
|
||||
class ConversationResourceTest {
|
||||
|
||||
private static final String BASE_PATH = "/api/conversations";
|
||||
private static final String MEMBRE_ID = "00000000-0000-0000-0000-000000000010";
|
||||
private static final String CONVERSATION_ID = "00000000-0000-0000-0000-000000000011";
|
||||
private static final String ORG_ID = "00000000-0000-0000-0000-000000000012";
|
||||
|
||||
@InjectMock
|
||||
ConversationService conversationService;
|
||||
|
||||
@InjectMock
|
||||
SecuriteHelper securiteHelper;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
when(securiteHelper.resolveMembreId()).thenReturn(UUID.fromString(MEMBRE_ID));
|
||||
}
|
||||
|
||||
private ConversationResponse buildConversationResponse() {
|
||||
return ConversationResponse.builder()
|
||||
.id(UUID.fromString(CONVERSATION_ID))
|
||||
.name("Discussion générale")
|
||||
.description("Conversation test")
|
||||
.type(ConversationType.GROUP)
|
||||
.participantIds(List.of(UUID.fromString(MEMBRE_ID)))
|
||||
.unreadCount(0)
|
||||
.muted(false)
|
||||
.pinned(false)
|
||||
.archived(false)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.updatedAt(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// GET /api/conversations
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getConversations retourne 200 avec liste vide")
|
||||
void getConversations_returnsEmptyList_200() {
|
||||
when(conversationService.getConversations(any(), any(), anyBoolean()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("$", notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getConversations avec includeArchived=true retourne 200")
|
||||
void getConversations_withIncludeArchived_returns200() {
|
||||
when(conversationService.getConversations(any(), any(), eq(true)))
|
||||
.thenReturn(List.of(buildConversationResponse()));
|
||||
|
||||
given()
|
||||
.queryParam("includeArchived", true)
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getConversations avec organisationId filtre par organisation")
|
||||
void getConversations_withOrganisationId_returns200() {
|
||||
when(conversationService.getConversations(any(), eq(UUID.fromString(ORG_ID)), anyBoolean()))
|
||||
.thenReturn(List.of(buildConversationResponse()));
|
||||
|
||||
given()
|
||||
.queryParam("organisationId", ORG_ID)
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getConversations retourne 200 avec liste non vide")
|
||||
void getConversations_withResults_returns200() {
|
||||
when(conversationService.getConversations(any(), any(), anyBoolean()))
|
||||
.thenReturn(List.of(buildConversationResponse()));
|
||||
|
||||
given()
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// GET /api/conversations/{id}
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getConversationById retourne 200 quand conversation trouvée")
|
||||
void getConversationById_found_returns200() {
|
||||
when(conversationService.getConversationById(eq(UUID.fromString(CONVERSATION_ID)), any()))
|
||||
.thenReturn(buildConversationResponse());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.get(BASE_PATH + "/{id}", CONVERSATION_ID)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("id", notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getConversationById retourne 404 quand conversation non trouvée")
|
||||
void getConversationById_notFound_returns404() {
|
||||
when(conversationService.getConversationById(any(), any()))
|
||||
.thenThrow(new NotFoundException("Conversation non trouvée ou accès refusé"));
|
||||
|
||||
given()
|
||||
.when()
|
||||
.get(BASE_PATH + "/{id}", UUID.randomUUID().toString())
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// POST /api/conversations
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("createConversation retourne 201 avec body valide")
|
||||
void createConversation_validRequest_returns201() {
|
||||
when(conversationService.createConversation(any(CreateConversationRequest.class), any()))
|
||||
.thenReturn(buildConversationResponse());
|
||||
|
||||
String body = """
|
||||
{
|
||||
"name": "Nouveau groupe",
|
||||
"description": "Description du groupe",
|
||||
"type": "GROUP",
|
||||
"participantIds": ["%s"]
|
||||
}
|
||||
""".formatted(MEMBRE_ID);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("createConversation avec body invalide (sans name) retourne 400")
|
||||
void createConversation_invalidRequest_returns400() {
|
||||
String body = """
|
||||
{
|
||||
"description": "Sans nom",
|
||||
"type": "GROUP",
|
||||
"participantIds": ["%s"]
|
||||
}
|
||||
""".formatted(MEMBRE_ID);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// PUT /api/conversations/{id}/archive
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("archiveConversation retourne 204 quand succès")
|
||||
void archiveConversation_success_returns204() {
|
||||
doNothing().when(conversationService).archiveConversation(any(), any(), anyBoolean());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/archive", CONVERSATION_ID)
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("archiveConversation avec archive=false retourne 204")
|
||||
void archiveConversation_unarchive_returns204() {
|
||||
doNothing().when(conversationService).archiveConversation(any(), any(), eq(false));
|
||||
|
||||
given()
|
||||
.queryParam("archive", false)
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/archive", CONVERSATION_ID)
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("archiveConversation non trouvée retourne 404")
|
||||
void archiveConversation_notFound_returns404() {
|
||||
doThrow(new NotFoundException("Conversation non trouvée"))
|
||||
.when(conversationService).archiveConversation(any(), any(), anyBoolean());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/archive", UUID.randomUUID().toString())
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// PUT /api/conversations/{id}/mark-read
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("markAsRead retourne 204 quand succès")
|
||||
void markAsRead_success_returns204() {
|
||||
doNothing().when(conversationService).markAsRead(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/mark-read", CONVERSATION_ID)
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("markAsRead conversation non trouvée retourne 404")
|
||||
void markAsRead_notFound_returns404() {
|
||||
doThrow(new NotFoundException("Conversation non trouvée"))
|
||||
.when(conversationService).markAsRead(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/mark-read", UUID.randomUUID().toString())
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// PUT /api/conversations/{id}/toggle-mute
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("toggleMute retourne 204 quand succès")
|
||||
void toggleMute_success_returns204() {
|
||||
doNothing().when(conversationService).toggleMute(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/toggle-mute", CONVERSATION_ID)
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("toggleMute conversation non trouvée retourne 404")
|
||||
void toggleMute_notFound_returns404() {
|
||||
doThrow(new NotFoundException("Conversation non trouvée"))
|
||||
.when(conversationService).toggleMute(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/toggle-mute", UUID.randomUUID().toString())
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// PUT /api/conversations/{id}/toggle-pin
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("togglePin retourne 204 quand succès")
|
||||
void togglePin_success_returns204() {
|
||||
doNothing().when(conversationService).togglePin(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/toggle-pin", CONVERSATION_ID)
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("togglePin conversation non trouvée retourne 404")
|
||||
void togglePin_notFound_returns404() {
|
||||
doThrow(new NotFoundException("Conversation non trouvée"))
|
||||
.when(conversationService).togglePin(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}/toggle-pin", UUID.randomUUID().toString())
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Sécurité — non authentifié
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("getConversations sans authentification retourne 401")
|
||||
void getConversations_unauthenticated_returns401() {
|
||||
given()
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.communication.request.SendMessageRequest;
|
||||
import dev.lions.unionflow.server.api.dto.communication.response.MessageResponse;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessagePriority;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageType;
|
||||
import dev.lions.unionflow.server.service.MessageService;
|
||||
import dev.lions.unionflow.server.service.support.SecuriteHelper;
|
||||
import io.quarkus.test.InjectMock;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.security.TestSecurity;
|
||||
import io.restassured.http.ContentType;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests d'intégration REST pour MessageResource.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-21
|
||||
*/
|
||||
@QuarkusTest
|
||||
class MessageResourceTest {
|
||||
|
||||
private static final String BASE_PATH = "/api/messages";
|
||||
private static final String MEMBRE_ID = "00000000-0000-0000-0000-000000000020";
|
||||
private static final String MESSAGE_ID = "00000000-0000-0000-0000-000000000021";
|
||||
private static final String CONVERSATION_ID = "00000000-0000-0000-0000-000000000022";
|
||||
|
||||
@InjectMock
|
||||
MessageService messageService;
|
||||
|
||||
@InjectMock
|
||||
SecuriteHelper securiteHelper;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
when(securiteHelper.resolveMembreId()).thenReturn(UUID.fromString(MEMBRE_ID));
|
||||
}
|
||||
|
||||
private MessageResponse buildMessageResponse() {
|
||||
return MessageResponse.builder()
|
||||
.id(UUID.fromString(MESSAGE_ID))
|
||||
.conversationId(UUID.fromString(CONVERSATION_ID))
|
||||
.senderId(UUID.fromString(MEMBRE_ID))
|
||||
.senderName("Alice Martin")
|
||||
.content("Bonjour !")
|
||||
.type(MessageType.INDIVIDUAL)
|
||||
.status(MessageStatus.SENT)
|
||||
.priority(MessagePriority.NORMAL)
|
||||
.edited(false)
|
||||
.deleted(false)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.build();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// GET /api/messages
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getMessages sans conversationId retourne 400")
|
||||
void getMessages_missingConversationId_returns400() {
|
||||
given()
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getMessages avec conversationId valide retourne 200")
|
||||
void getMessages_withConversationId_returns200() {
|
||||
when(messageService.getMessages(eq(UUID.fromString(CONVERSATION_ID)), any(), anyInt()))
|
||||
.thenReturn(List.of(buildMessageResponse()));
|
||||
|
||||
given()
|
||||
.queryParam("conversationId", CONVERSATION_ID)
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("$", notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getMessages avec conversationId valide retourne liste vide")
|
||||
void getMessages_emptyConversation_returns200() {
|
||||
when(messageService.getMessages(any(), any(), anyInt()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
given()
|
||||
.queryParam("conversationId", CONVERSATION_ID)
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getMessages avec limit personnalisé retourne 200")
|
||||
void getMessages_withLimit_returns200() {
|
||||
when(messageService.getMessages(any(), any(), eq(10)))
|
||||
.thenReturn(List.of(buildMessageResponse()));
|
||||
|
||||
given()
|
||||
.queryParam("conversationId", CONVERSATION_ID)
|
||||
.queryParam("limit", 10)
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("getMessages conversation non trouvée retourne 404")
|
||||
void getMessages_conversationNotFound_returns404() {
|
||||
when(messageService.getMessages(any(), any(), anyInt()))
|
||||
.thenThrow(new NotFoundException("Conversation non trouvée ou accès refusé"));
|
||||
|
||||
given()
|
||||
.queryParam("conversationId", CONVERSATION_ID)
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// POST /api/messages
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("sendMessage avec body valide retourne 201")
|
||||
void sendMessage_validRequest_returns201() {
|
||||
when(messageService.sendMessage(any(SendMessageRequest.class), any()))
|
||||
.thenReturn(buildMessageResponse());
|
||||
|
||||
String body = """
|
||||
{
|
||||
"conversationId": "%s",
|
||||
"content": "Bonjour à tous !"
|
||||
}
|
||||
""".formatted(CONVERSATION_ID);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(201)
|
||||
.contentType(ContentType.JSON)
|
||||
.body("id", notNullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("sendMessage sans conversationId retourne 400")
|
||||
void sendMessage_missingConversationId_returns400() {
|
||||
String body = """
|
||||
{
|
||||
"content": "Message sans conversation"
|
||||
}
|
||||
""";
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("sendMessage sans contenu retourne 400")
|
||||
void sendMessage_missingContent_returns400() {
|
||||
String body = """
|
||||
{
|
||||
"conversationId": "%s"
|
||||
}
|
||||
""".formatted(CONVERSATION_ID);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("sendMessage conversation non trouvée retourne 404")
|
||||
void sendMessage_conversationNotFound_returns404() {
|
||||
when(messageService.sendMessage(any(), any()))
|
||||
.thenThrow(new NotFoundException("Conversation non trouvée ou accès refusé"));
|
||||
|
||||
String body = """
|
||||
{
|
||||
"conversationId": "%s",
|
||||
"content": "Test"
|
||||
}
|
||||
""".formatted(CONVERSATION_ID);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// PUT /api/messages/{id}
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("editMessage avec body valide retourne 200")
|
||||
void editMessage_validRequest_returns200() {
|
||||
when(messageService.editMessage(any(), any(), anyString()))
|
||||
.thenReturn(buildMessageResponse());
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("{\"content\": \"Message modifié\"}")
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}", MESSAGE_ID)
|
||||
.then()
|
||||
.statusCode(200)
|
||||
.contentType(ContentType.JSON);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("editMessage sans contenu retourne 400")
|
||||
void editMessage_missingContent_returns400() {
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("{}")
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}", MESSAGE_ID)
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("editMessage avec contenu vide retourne 400")
|
||||
void editMessage_emptyContent_returns400() {
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("{\"content\": \"\"}")
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}", MESSAGE_ID)
|
||||
.then()
|
||||
.statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("editMessage message non trouvé retourne 404")
|
||||
void editMessage_notFound_returns404() {
|
||||
when(messageService.editMessage(any(), any(), anyString()))
|
||||
.thenThrow(new NotFoundException("Message non trouvé"));
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body("{\"content\": \"Contenu modifié\"}")
|
||||
.when()
|
||||
.put(BASE_PATH + "/{id}", UUID.randomUUID().toString())
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// DELETE /api/messages/{id}
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("deleteMessage retourne 204 quand succès")
|
||||
void deleteMessage_success_returns204() {
|
||||
doNothing().when(messageService).deleteMessage(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.delete(BASE_PATH + "/{id}", MESSAGE_ID)
|
||||
.then()
|
||||
.statusCode(204);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestSecurity(user = "alice@test.com", roles = {"MEMBRE"})
|
||||
@DisplayName("deleteMessage message non trouvé retourne 404")
|
||||
void deleteMessage_notFound_returns404() {
|
||||
doThrow(new NotFoundException("Message non trouvé"))
|
||||
.when(messageService).deleteMessage(any(), any());
|
||||
|
||||
given()
|
||||
.when()
|
||||
.delete(BASE_PATH + "/{id}", UUID.randomUUID().toString())
|
||||
.then()
|
||||
.statusCode(404);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Sécurité — non authentifié
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("getMessages sans authentification retourne 401")
|
||||
void getMessages_unauthenticated_returns401() {
|
||||
given()
|
||||
.queryParam("conversationId", CONVERSATION_ID)
|
||||
.when()
|
||||
.get(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage sans authentification retourne 401")
|
||||
void sendMessage_unauthenticated_returns401() {
|
||||
String body = """
|
||||
{
|
||||
"conversationId": "%s",
|
||||
"content": "Message non auth"
|
||||
}
|
||||
""".formatted(CONVERSATION_ID);
|
||||
|
||||
given()
|
||||
.contentType(ContentType.JSON)
|
||||
.body(body)
|
||||
.when()
|
||||
.post(BASE_PATH)
|
||||
.then()
|
||||
.statusCode(401);
|
||||
}
|
||||
}
|
||||
@@ -1,562 +0,0 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.communication.request.CreateConversationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.communication.response.ConversationResponse;
|
||||
import dev.lions.unionflow.server.api.enums.communication.ConversationType;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageType;
|
||||
import dev.lions.unionflow.server.entity.Conversation;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Message;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.repository.ConversationRepository;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import dev.lions.unionflow.server.repository.MessageRepository;
|
||||
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||
import io.quarkus.test.InjectMock;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour ConversationService
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-20
|
||||
*/
|
||||
@QuarkusTest
|
||||
@TestMethodOrder(MethodOrderer.DisplayName.class)
|
||||
class ConversationServiceTest {
|
||||
|
||||
@Inject
|
||||
ConversationService conversationService;
|
||||
|
||||
@InjectMock
|
||||
ConversationRepository conversationRepository;
|
||||
|
||||
@InjectMock
|
||||
MessageRepository messageRepository;
|
||||
|
||||
@InjectSpy
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@InjectSpy
|
||||
OrganisationRepository organisationRepository;
|
||||
|
||||
@InjectMock
|
||||
EntityManager entityManager;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private Conversation mockConversation() {
|
||||
Conversation c = new Conversation();
|
||||
c.setId(UUID.randomUUID());
|
||||
c.setName("Test Conv");
|
||||
c.setIsMuted(false);
|
||||
c.setIsPinned(false);
|
||||
c.setIsArchived(false);
|
||||
c.setParticipants(new ArrayList<>());
|
||||
return c;
|
||||
}
|
||||
|
||||
private Message mockMessage(Conversation conv) {
|
||||
Message msg = new Message();
|
||||
msg.setId(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
Membre sender = new Membre();
|
||||
sender.setId(UUID.randomUUID());
|
||||
msg.setSender(sender);
|
||||
msg.setSenderName("Test Sender");
|
||||
msg.setContent("Hello");
|
||||
msg.setType(MessageType.INDIVIDUAL);
|
||||
msg.setStatus(MessageStatus.SENT);
|
||||
msg.setIsEdited(false);
|
||||
msg.setIsDeleted(false);
|
||||
return msg;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// getConversations
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("getConversations_withOrgId_callsByOrganisation")
|
||||
void getConversations_withOrgId_callsByOrganisation() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
when(conversationRepository.findByOrganisation(orgId)).thenReturn(List.of(conv));
|
||||
when(messageRepository.findLastByConversation(conv.getId())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(conv.getId(), membreId)).thenReturn(0L);
|
||||
|
||||
List<ConversationResponse> result = conversationService.getConversations(membreId, orgId, false);
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
verify(conversationRepository).findByOrganisation(orgId);
|
||||
verify(conversationRepository, never()).findByParticipant(any(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getConversations_withoutOrgId_callsByParticipant")
|
||||
void getConversations_withoutOrgId_callsByParticipant() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
when(conversationRepository.findByParticipant(membreId, false)).thenReturn(List.of(conv));
|
||||
when(messageRepository.findLastByConversation(conv.getId())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(conv.getId(), membreId)).thenReturn(0L);
|
||||
|
||||
List<ConversationResponse> result = conversationService.getConversations(membreId, null, false);
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
verify(conversationRepository).findByParticipant(membreId, false);
|
||||
verify(conversationRepository, never()).findByOrganisation(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getConversations_includesLastMessageAndUnread")
|
||||
void getConversations_includesLastMessageAndUnread() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Message lastMsg = mockMessage(conv);
|
||||
|
||||
when(conversationRepository.findByParticipant(membreId, true)).thenReturn(List.of(conv));
|
||||
when(messageRepository.findLastByConversation(conv.getId())).thenReturn(lastMsg);
|
||||
when(messageRepository.countUnreadByConversationAndMember(conv.getId(), membreId)).thenReturn(3L);
|
||||
|
||||
List<ConversationResponse> result = conversationService.getConversations(membreId, null, true);
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
ConversationResponse response = result.get(0);
|
||||
assertThat(response.getLastMessage()).isNotNull();
|
||||
assertThat(response.getLastMessage().getContent()).isEqualTo("Hello");
|
||||
assertThat(response.getUnreadCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// getConversationById
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("getConversationById_notFound_throwsNotFound")
|
||||
void getConversationById_notFound_throwsNotFound() {
|
||||
UUID convId = UUID.randomUUID();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(convId, membreId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> conversationService.getConversationById(convId, membreId))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Conversation non trouvée");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getConversationById_found_returnsResponse")
|
||||
void getConversationById_found_returnsResponse() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
when(messageRepository.findLastByConversation(conv.getId())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(conv.getId(), membreId)).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.getConversationById(conv.getId(), membreId);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getId()).isEqualTo(conv.getId());
|
||||
assertThat(response.getName()).isEqualTo("Test Conv");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// createConversation
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("createConversation_withoutOrg_success")
|
||||
void createConversation_withoutOrg_success() {
|
||||
UUID creatorId = UUID.randomUUID();
|
||||
Membre creator = new Membre();
|
||||
creator.setId(creatorId);
|
||||
|
||||
CreateConversationRequest request = CreateConversationRequest.builder()
|
||||
.name("New Conv")
|
||||
.description("desc")
|
||||
.type(ConversationType.GROUP)
|
||||
.participantIds(new ArrayList<>())
|
||||
.organisationId(null)
|
||||
.build();
|
||||
|
||||
when(entityManager.find(Membre.class, creatorId)).thenReturn(creator);
|
||||
when(messageRepository.findLastByConversation(any())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(any(), eq(creatorId))).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.createConversation(request, creatorId);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getName()).isEqualTo("New Conv");
|
||||
verify(conversationRepository).persist(any(Conversation.class));
|
||||
verify(entityManager, never()).find(eq(Organisation.class), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("createConversation_withOrg_setsOrganisation")
|
||||
void createConversation_withOrg_setsOrganisation() {
|
||||
UUID creatorId = UUID.randomUUID();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
Membre creator = new Membre();
|
||||
creator.setId(creatorId);
|
||||
Organisation org = new Organisation();
|
||||
org.setId(orgId);
|
||||
|
||||
CreateConversationRequest request = CreateConversationRequest.builder()
|
||||
.name("Org Conv")
|
||||
.description(null)
|
||||
.type(ConversationType.BROADCAST)
|
||||
.participantIds(new ArrayList<>())
|
||||
.organisationId(orgId)
|
||||
.build();
|
||||
|
||||
when(entityManager.find(Membre.class, creatorId)).thenReturn(creator);
|
||||
when(entityManager.find(Organisation.class, orgId)).thenReturn(org);
|
||||
when(messageRepository.findLastByConversation(any())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(any(), eq(creatorId))).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.createConversation(request, creatorId);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getOrganisationId()).isEqualTo(orgId);
|
||||
verify(entityManager).find(Organisation.class, orgId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("createConversation_creatorNotInList_addsCreator")
|
||||
void createConversation_creatorNotInList_addsCreator() {
|
||||
UUID creatorId = UUID.randomUUID();
|
||||
UUID participant1Id = UUID.randomUUID();
|
||||
Membre creator = new Membre();
|
||||
creator.setId(creatorId);
|
||||
Membre participant1 = new Membre();
|
||||
participant1.setId(participant1Id);
|
||||
|
||||
CreateConversationRequest request = CreateConversationRequest.builder()
|
||||
.name("Conv")
|
||||
.description(null)
|
||||
.type(ConversationType.INDIVIDUAL)
|
||||
.participantIds(new ArrayList<>(List.of(participant1Id)))
|
||||
.organisationId(null)
|
||||
.build();
|
||||
|
||||
when(entityManager.find(Membre.class, participant1Id)).thenReturn(participant1);
|
||||
when(entityManager.find(Membre.class, creatorId)).thenReturn(creator);
|
||||
when(messageRepository.findLastByConversation(any())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(any(), eq(creatorId))).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.createConversation(request, creatorId);
|
||||
|
||||
// Creator + participant1 = 2 participants
|
||||
assertThat(response.getParticipantIds()).hasSize(2);
|
||||
assertThat(response.getParticipantIds()).contains(creatorId, participant1Id);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("createConversation_creatorAlreadyInList_doesNotDuplicate")
|
||||
void createConversation_creatorAlreadyInList_doesNotDuplicate() {
|
||||
UUID creatorId = UUID.randomUUID();
|
||||
Membre creator = new Membre();
|
||||
creator.setId(creatorId);
|
||||
|
||||
CreateConversationRequest request = CreateConversationRequest.builder()
|
||||
.name("Conv")
|
||||
.description(null)
|
||||
.type(ConversationType.INDIVIDUAL)
|
||||
.participantIds(new ArrayList<>(List.of(creatorId)))
|
||||
.organisationId(null)
|
||||
.build();
|
||||
|
||||
// findById(creatorId) appelé 2 fois: une pour le participant, une pour le créateur
|
||||
when(entityManager.find(Membre.class, creatorId)).thenReturn(creator);
|
||||
when(messageRepository.findLastByConversation(any())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(any(), eq(creatorId))).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.createConversation(request, creatorId);
|
||||
|
||||
// Le créateur ne doit pas être dupliqué
|
||||
assertThat(response.getParticipantIds()).hasSize(1);
|
||||
assertThat(response.getParticipantIds()).containsExactly(creatorId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("createConversation_participantNotFound_filtersNull")
|
||||
void createConversation_participantNotFound_filtersNull() {
|
||||
UUID creatorId = UUID.randomUUID();
|
||||
UUID unknownId = UUID.randomUUID();
|
||||
Membre creator = new Membre();
|
||||
creator.setId(creatorId);
|
||||
|
||||
CreateConversationRequest request = CreateConversationRequest.builder()
|
||||
.name("Conv")
|
||||
.description(null)
|
||||
.type(ConversationType.GROUP)
|
||||
.participantIds(new ArrayList<>(List.of(unknownId)))
|
||||
.organisationId(null)
|
||||
.build();
|
||||
|
||||
when(entityManager.find(Membre.class, creatorId)).thenReturn(creator);
|
||||
when(messageRepository.findLastByConversation(any())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(any(), eq(creatorId))).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.createConversation(request, creatorId);
|
||||
|
||||
// unknownId est filtré, seul le créateur reste
|
||||
assertThat(response.getParticipantIds()).hasSize(1);
|
||||
assertThat(response.getParticipantIds()).containsExactly(creatorId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("createConversation_creatorNotFound_doesNotAddCreator (creator == null → L97 false)")
|
||||
void createConversation_creatorNull_doesNotAddCreator() {
|
||||
UUID creatorId = UUID.randomUUID();
|
||||
|
||||
CreateConversationRequest request = CreateConversationRequest.builder()
|
||||
.name("No Creator Conv")
|
||||
.description(null)
|
||||
.type(ConversationType.GROUP)
|
||||
.participantIds(new ArrayList<>())
|
||||
.organisationId(null)
|
||||
.build();
|
||||
|
||||
// creator == null → condition L97: creator != null = false → pas d'ajout du créateur
|
||||
when(entityManager.find(Membre.class, creatorId)).thenReturn(null);
|
||||
when(messageRepository.findLastByConversation(any())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(any(), eq(creatorId))).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.createConversation(request, creatorId);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
// Aucun participant car creator introuvable et liste vide
|
||||
assertThat(response.getParticipantIds()).isEmpty();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// archiveConversation
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("archiveConversation_notFound_throwsNotFound")
|
||||
void archiveConversation_notFound_throwsNotFound() {
|
||||
UUID convId = UUID.randomUUID();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(convId, membreId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> conversationService.archiveConversation(convId, membreId, true))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Conversation non trouvée");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("archiveConversation_archive_setsTrue")
|
||||
void archiveConversation_archive_setsTrue() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
conv.setIsArchived(false);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
|
||||
conversationService.archiveConversation(conv.getId(), membreId, true);
|
||||
|
||||
assertThat(conv.getIsArchived()).isTrue();
|
||||
verify(conversationRepository).persist(conv);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("archiveConversation_unarchive_setsFalse")
|
||||
void archiveConversation_unarchive_setsFalse() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
conv.setIsArchived(true);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
|
||||
conversationService.archiveConversation(conv.getId(), membreId, false);
|
||||
|
||||
assertThat(conv.getIsArchived()).isFalse();
|
||||
verify(conversationRepository).persist(conv);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// markAsRead
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("markAsRead_notFound_throwsNotFound")
|
||||
void markAsRead_notFound_throwsNotFound() {
|
||||
UUID convId = UUID.randomUUID();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(convId, membreId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> conversationService.markAsRead(convId, membreId))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Conversation non trouvée");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("markAsRead_success_callsRepo")
|
||||
void markAsRead_success_callsRepo() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
when(messageRepository.markAllAsReadByConversationAndMember(conv.getId(), membreId)).thenReturn(5);
|
||||
|
||||
conversationService.markAsRead(conv.getId(), membreId);
|
||||
|
||||
verify(messageRepository).markAllAsReadByConversationAndMember(conv.getId(), membreId);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// toggleMute
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("toggleMute_notFound_throwsNotFound")
|
||||
void toggleMute_notFound_throwsNotFound() {
|
||||
UUID convId = UUID.randomUUID();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(convId, membreId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> conversationService.toggleMute(convId, membreId))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Conversation non trouvée");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("toggleMute_false_setsTrue")
|
||||
void toggleMute_false_setsTrue() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
conv.setIsMuted(false);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
|
||||
conversationService.toggleMute(conv.getId(), membreId);
|
||||
|
||||
assertThat(conv.getIsMuted()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("toggleMute_true_setsFalse")
|
||||
void toggleMute_true_setsFalse() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
conv.setIsMuted(true);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
|
||||
conversationService.toggleMute(conv.getId(), membreId);
|
||||
|
||||
assertThat(conv.getIsMuted()).isFalse();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// togglePin
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("togglePin_notFound_throwsNotFound")
|
||||
void togglePin_notFound_throwsNotFound() {
|
||||
UUID convId = UUID.randomUUID();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(convId, membreId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> conversationService.togglePin(convId, membreId))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Conversation non trouvée");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("togglePin_false_setsTrue")
|
||||
void togglePin_false_setsTrue() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
conv.setIsPinned(false);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
|
||||
conversationService.togglePin(conv.getId(), membreId);
|
||||
|
||||
assertThat(conv.getIsPinned()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("togglePin_true_setsFalse")
|
||||
void togglePin_true_setsFalse() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
conv.setIsPinned(true);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
|
||||
conversationService.togglePin(conv.getId(), membreId);
|
||||
|
||||
assertThat(conv.getIsPinned()).isFalse();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// convertToResponse (via getConversationById)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_withLastMessage_includesMessage")
|
||||
void convertToResponse_withLastMessage_includesMessage() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Message lastMsg = mockMessage(conv);
|
||||
lastMsg.setContent("Dernier message");
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
when(messageRepository.findLastByConversation(conv.getId())).thenReturn(lastMsg);
|
||||
when(messageRepository.countUnreadByConversationAndMember(conv.getId(), membreId)).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.getConversationById(conv.getId(), membreId);
|
||||
|
||||
assertThat(response.getLastMessage()).isNotNull();
|
||||
assertThat(response.getLastMessage().getContent()).isEqualTo("Dernier message");
|
||||
assertThat(response.getLastMessage().getId()).isEqualTo(lastMsg.getId());
|
||||
assertThat(response.getLastMessage().getSenderId()).isEqualTo(lastMsg.getSender().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_noLastMessage_nullLastMessage")
|
||||
void convertToResponse_noLastMessage_nullLastMessage() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
when(messageRepository.findLastByConversation(conv.getId())).thenReturn(null);
|
||||
when(messageRepository.countUnreadByConversationAndMember(conv.getId(), membreId)).thenReturn(0L);
|
||||
|
||||
ConversationResponse response = conversationService.getConversationById(conv.getId(), membreId);
|
||||
|
||||
assertThat(response.getLastMessage()).isNull();
|
||||
}
|
||||
}
|
||||
@@ -1,621 +0,0 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.communication.request.SendMessageRequest;
|
||||
import dev.lions.unionflow.server.api.dto.communication.response.MessageResponse;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessagePriority;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageStatus;
|
||||
import dev.lions.unionflow.server.api.enums.communication.MessageType;
|
||||
import dev.lions.unionflow.server.entity.Conversation;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Message;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.repository.ConversationRepository;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import dev.lions.unionflow.server.repository.MessageRepository;
|
||||
import io.quarkus.test.InjectMock;
|
||||
import io.quarkus.test.junit.QuarkusTest;
|
||||
import io.quarkus.test.junit.mockito.InjectSpy;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests unitaires pour MessageService
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-20
|
||||
*/
|
||||
@QuarkusTest
|
||||
@TestMethodOrder(MethodOrderer.DisplayName.class)
|
||||
class MessageServiceTest {
|
||||
|
||||
@Inject
|
||||
MessageService messageService;
|
||||
|
||||
@InjectSpy
|
||||
MessageRepository messageRepository;
|
||||
|
||||
@InjectMock
|
||||
ConversationRepository conversationRepository;
|
||||
|
||||
@InjectSpy
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@InjectMock
|
||||
EntityManager entityManager;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private Conversation mockConversation() {
|
||||
Conversation c = new Conversation();
|
||||
c.setId(UUID.randomUUID());
|
||||
c.setParticipants(new ArrayList<>());
|
||||
return c;
|
||||
}
|
||||
|
||||
private Membre mockMembre(UUID id) {
|
||||
Membre m = new Membre();
|
||||
m.setId(id);
|
||||
m.setPrenom("Jean");
|
||||
m.setNom("Dupont");
|
||||
return m;
|
||||
}
|
||||
|
||||
private Message mockMessage(UUID senderId) {
|
||||
Message msg = new Message();
|
||||
msg.setId(UUID.randomUUID());
|
||||
Conversation conv = mockConversation();
|
||||
msg.setConversation(conv);
|
||||
Membre sender = mockMembre(senderId);
|
||||
msg.setSender(sender);
|
||||
msg.setSenderName("Jean Dupont");
|
||||
msg.setContent("Hello");
|
||||
msg.setType(MessageType.INDIVIDUAL);
|
||||
msg.setStatus(MessageStatus.SENT);
|
||||
msg.setPriority(MessagePriority.NORMAL);
|
||||
msg.setIsEdited(false);
|
||||
msg.setIsDeleted(false);
|
||||
return msg;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// getMessages
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("getMessages_conversationNotFound_throwsNotFound")
|
||||
void getMessages_conversationNotFound_throwsNotFound() {
|
||||
UUID convId = UUID.randomUUID();
|
||||
UUID membreId = UUID.randomUUID();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(convId, membreId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> messageService.getMessages(convId, membreId, 20))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Conversation non trouvée");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("getMessages_found_returnsList")
|
||||
void getMessages_found_returnsList() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).getContent()).isEqualTo("Hello");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// sendMessage
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_conversationNotFound_throwsNotFound")
|
||||
void sendMessage_conversationNotFound_throwsNotFound() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
UUID convId = UUID.randomUUID();
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(convId)
|
||||
.content("test")
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(convId, senderId)).thenReturn(Optional.empty());
|
||||
|
||||
assertThatThrownBy(() -> messageService.sendMessage(request, senderId))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Conversation non trouvée");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_senderNotFound_throwsNotFound")
|
||||
void sendMessage_senderNotFound_throwsNotFound() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(conv.getId())
|
||||
.content("test")
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), senderId)).thenReturn(Optional.of(conv));
|
||||
// entityManager.find returns null by default → findById(senderId) returns null → service throws
|
||||
|
||||
assertThatThrownBy(() -> messageService.sendMessage(request, senderId))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Expéditeur non trouvé");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_withDefaultTypeAndPriority_success")
|
||||
void sendMessage_withDefaultTypeAndPriority_success() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Membre sender = mockMembre(senderId);
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(conv.getId())
|
||||
.content("Bonjour")
|
||||
.type(null)
|
||||
.priority(null)
|
||||
.recipientIds(null)
|
||||
.recipientRoles(null)
|
||||
.attachments(null)
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), senderId)).thenReturn(Optional.of(conv));
|
||||
when(entityManager.find(Membre.class, senderId)).thenReturn(sender);
|
||||
|
||||
MessageResponse response = messageService.sendMessage(request, senderId);
|
||||
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getContent()).isEqualTo("Bonjour");
|
||||
assertThat(response.getType()).isEqualTo(MessageType.INDIVIDUAL);
|
||||
assertThat(response.getPriority()).isEqualTo(MessagePriority.NORMAL);
|
||||
assertThat(response.getSenderName()).isEqualTo("Jean Dupont");
|
||||
verify(messageRepository).persist(any(Message.class));
|
||||
verify(conversationRepository).persist(conv);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_withExplicitType_usesType")
|
||||
void sendMessage_withExplicitType_usesType() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Membre sender = mockMembre(senderId);
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(conv.getId())
|
||||
.content("Broadcast!")
|
||||
.type(MessageType.BROADCAST)
|
||||
.priority(MessagePriority.HIGH)
|
||||
.recipientIds(null)
|
||||
.recipientRoles(null)
|
||||
.attachments(null)
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), senderId)).thenReturn(Optional.of(conv));
|
||||
when(entityManager.find(Membre.class, senderId)).thenReturn(sender);
|
||||
|
||||
MessageResponse response = messageService.sendMessage(request, senderId);
|
||||
|
||||
assertThat(response.getType()).isEqualTo(MessageType.BROADCAST);
|
||||
assertThat(response.getPriority()).isEqualTo(MessagePriority.HIGH);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_withRecipientIds_setsCSV")
|
||||
void sendMessage_withRecipientIds_setsCSV() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
UUID recipient1 = UUID.randomUUID();
|
||||
UUID recipient2 = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Membre sender = mockMembre(senderId);
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(conv.getId())
|
||||
.content("Targeted")
|
||||
.type(MessageType.TARGETED)
|
||||
.priority(null)
|
||||
.recipientIds(List.of(recipient1, recipient2))
|
||||
.recipientRoles(null)
|
||||
.attachments(null)
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), senderId)).thenReturn(Optional.of(conv));
|
||||
when(entityManager.find(Membre.class, senderId)).thenReturn(sender);
|
||||
|
||||
MessageResponse response = messageService.sendMessage(request, senderId);
|
||||
|
||||
assertThat(response.getRecipientIds()).isNotNull();
|
||||
assertThat(response.getRecipientIds()).containsExactlyInAnyOrder(recipient1, recipient2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_withRecipientRoles_setsCSV")
|
||||
void sendMessage_withRecipientRoles_setsCSV() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Membre sender = mockMembre(senderId);
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(conv.getId())
|
||||
.content("Role msg")
|
||||
.type(null)
|
||||
.priority(null)
|
||||
.recipientIds(null)
|
||||
.recipientRoles(List.of("ADMIN", "TRESORIER"))
|
||||
.attachments(null)
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), senderId)).thenReturn(Optional.of(conv));
|
||||
when(entityManager.find(Membre.class, senderId)).thenReturn(sender);
|
||||
|
||||
MessageResponse response = messageService.sendMessage(request, senderId);
|
||||
|
||||
assertThat(response.getRecipientRoles()).isNotNull();
|
||||
assertThat(response.getRecipientRoles()).containsExactly("ADMIN", "TRESORIER");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_withAttachments_setsCSV")
|
||||
void sendMessage_withAttachments_setsCSV() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Membre sender = mockMembre(senderId);
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(conv.getId())
|
||||
.content("Msg avec PJ")
|
||||
.type(null)
|
||||
.priority(null)
|
||||
.recipientIds(null)
|
||||
.recipientRoles(null)
|
||||
.attachments(List.of("https://cdn.example.com/doc1.pdf", "https://cdn.example.com/img1.png"))
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), senderId)).thenReturn(Optional.of(conv));
|
||||
when(entityManager.find(Membre.class, senderId)).thenReturn(sender);
|
||||
|
||||
MessageResponse response = messageService.sendMessage(request, senderId);
|
||||
|
||||
assertThat(response.getAttachments()).isNotNull();
|
||||
assertThat(response.getAttachments()).containsExactly(
|
||||
"https://cdn.example.com/doc1.pdf",
|
||||
"https://cdn.example.com/img1.png"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("sendMessage_noRecipientsNoRolesNoAttachments_noCSV")
|
||||
void sendMessage_noRecipientsNoRolesNoAttachments_noCSV() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Membre sender = mockMembre(senderId);
|
||||
|
||||
SendMessageRequest request = SendMessageRequest.builder()
|
||||
.conversationId(conv.getId())
|
||||
.content("Simple")
|
||||
.type(null)
|
||||
.priority(null)
|
||||
.recipientIds(new ArrayList<>())
|
||||
.recipientRoles(new ArrayList<>())
|
||||
.attachments(new ArrayList<>())
|
||||
.build();
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), senderId)).thenReturn(Optional.of(conv));
|
||||
when(entityManager.find(Membre.class, senderId)).thenReturn(sender);
|
||||
|
||||
MessageResponse response = messageService.sendMessage(request, senderId);
|
||||
|
||||
assertThat(response.getRecipientIds()).isNull();
|
||||
assertThat(response.getRecipientRoles()).isNull();
|
||||
assertThat(response.getAttachments()).isNull();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// editMessage
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("editMessage_notFound_throwsNotFound")
|
||||
void editMessage_notFound_throwsNotFound() {
|
||||
UUID messageId = UUID.randomUUID();
|
||||
UUID senderId = UUID.randomUUID();
|
||||
|
||||
// entityManager.find returns null by default → findById(messageId) returns null → service throws
|
||||
|
||||
assertThatThrownBy(() -> messageService.editMessage(messageId, senderId, "nouveau contenu"))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Message non trouvé");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("editMessage_wrongSender_throwsIllegalState")
|
||||
void editMessage_wrongSender_throwsIllegalState() {
|
||||
UUID realSenderId = UUID.randomUUID();
|
||||
UUID wrongSenderId = UUID.randomUUID();
|
||||
Message msg = mockMessage(realSenderId);
|
||||
|
||||
when(entityManager.find(Message.class, msg.getId())).thenReturn(msg);
|
||||
|
||||
assertThatThrownBy(() -> messageService.editMessage(msg.getId(), wrongSenderId, "contenu modifié"))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("propres messages");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("editMessage_success_updatesContent")
|
||||
void editMessage_success_updatesContent() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Message msg = mockMessage(senderId);
|
||||
msg.setIsEdited(false);
|
||||
|
||||
when(entityManager.find(Message.class, msg.getId())).thenReturn(msg);
|
||||
|
||||
MessageResponse response = messageService.editMessage(msg.getId(), senderId, "Contenu édité");
|
||||
|
||||
assertThat(msg.getContent()).isEqualTo("Contenu édité");
|
||||
assertThat(msg.getIsEdited()).isTrue();
|
||||
assertThat(msg.getEditedAt()).isNotNull();
|
||||
verify(messageRepository).persist(msg);
|
||||
assertThat(response.getContent()).isEqualTo("Contenu édité");
|
||||
assertThat(response.isEdited()).isTrue();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// deleteMessage
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("deleteMessage_notFound_throwsNotFound")
|
||||
void deleteMessage_notFound_throwsNotFound() {
|
||||
UUID messageId = UUID.randomUUID();
|
||||
UUID senderId = UUID.randomUUID();
|
||||
|
||||
// entityManager.find returns null by default → findById(messageId) returns null → service throws
|
||||
|
||||
assertThatThrownBy(() -> messageService.deleteMessage(messageId, senderId))
|
||||
.isInstanceOf(NotFoundException.class)
|
||||
.hasMessageContaining("Message non trouvé");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("deleteMessage_wrongSender_throwsIllegalState")
|
||||
void deleteMessage_wrongSender_throwsIllegalState() {
|
||||
UUID realSenderId = UUID.randomUUID();
|
||||
UUID wrongSenderId = UUID.randomUUID();
|
||||
Message msg = mockMessage(realSenderId);
|
||||
|
||||
when(entityManager.find(Message.class, msg.getId())).thenReturn(msg);
|
||||
|
||||
assertThatThrownBy(() -> messageService.deleteMessage(msg.getId(), wrongSenderId))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("propres messages");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("deleteMessage_success_softDeletes")
|
||||
void deleteMessage_success_softDeletes() {
|
||||
UUID senderId = UUID.randomUUID();
|
||||
Message msg = mockMessage(senderId);
|
||||
msg.setIsDeleted(false);
|
||||
msg.setContent("Contenu original");
|
||||
|
||||
when(entityManager.find(Message.class, msg.getId())).thenReturn(msg);
|
||||
|
||||
messageService.deleteMessage(msg.getId(), senderId);
|
||||
|
||||
assertThat(msg.getIsDeleted()).isTrue();
|
||||
assertThat(msg.getContent()).isEqualTo("[Message supprimé]");
|
||||
verify(messageRepository).persist(msg);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// convertToResponse (via getMessages)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_withRecipientIds_parsesCsv")
|
||||
void convertToResponse_withRecipientIds_parsesCsv() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
UUID r1 = UUID.randomUUID();
|
||||
UUID r2 = UUID.randomUUID();
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
msg.setRecipientIds(r1 + "," + r2);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result.get(0).getRecipientIds()).containsExactlyInAnyOrder(r1, r2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_withRecipientRoles_parsesCsv")
|
||||
void convertToResponse_withRecipientRoles_parsesCsv() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
msg.setRecipientRoles("ADMIN,SECRETAIRE");
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result.get(0).getRecipientRoles()).containsExactly("ADMIN", "SECRETAIRE");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_withAttachments_parsesCsv")
|
||||
void convertToResponse_withAttachments_parsesCsv() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
msg.setAttachments("https://cdn.example.com/a.pdf,https://cdn.example.com/b.png");
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result.get(0).getAttachments()).containsExactly(
|
||||
"https://cdn.example.com/a.pdf",
|
||||
"https://cdn.example.com/b.png"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_noRecipients_nullFields")
|
||||
void convertToResponse_noRecipients_nullFields() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
// recipientIds, recipientRoles et attachments sont null par défaut
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
MessageResponse response = result.get(0);
|
||||
assertThat(response.getRecipientIds()).isNull();
|
||||
assertThat(response.getRecipientRoles()).isNull();
|
||||
assertThat(response.getAttachments()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_withOrganisation_setsOrgId")
|
||||
void convertToResponse_withOrganisation_setsOrgId() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
UUID orgId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
Organisation org = new Organisation();
|
||||
org.setId(orgId);
|
||||
conv.setOrganisation(org);
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
msg.setOrganisation(org);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result.get(0).getOrganisationId()).isEqualTo(orgId);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_noOrganisation_nullOrgId")
|
||||
void convertToResponse_noOrganisation_nullOrgId() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
// organisation est null
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
msg.setOrganisation(null);
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result.get(0).getOrganisationId()).isNull();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// convertToResponse — branches isEmpty() (non-null mais vide)
|
||||
// L163: recipientIds != null && !isEmpty() → false (empty string → isEmpty = true)
|
||||
// L172: recipientRoles != null && !isEmpty() → false
|
||||
// L178: attachments != null && !isEmpty() → false
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_recipientIdsEmptyString_returnsNullRecipientIds (L163 false)")
|
||||
void convertToResponse_recipientIdsEmptyString_returnsNullRecipientIds() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
// non-null mais vide → L163: isEmpty() = true → condition false → recipientIds = null
|
||||
msg.setRecipientIds("");
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result.get(0).getRecipientIds()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_recipientRolesEmptyString_returnsNullRoles (L172 false)")
|
||||
void convertToResponse_recipientRolesEmptyString_returnsNullRoles() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
msg.setRecipientRoles(""); // non-null mais vide → L172 false → roles = null
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result.get(0).getRecipientRoles()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("convertToResponse_attachmentsEmptyString_returnsNullAttachments (L178 false)")
|
||||
void convertToResponse_attachmentsEmptyString_returnsNullAttachments() {
|
||||
UUID membreId = UUID.randomUUID();
|
||||
Conversation conv = mockConversation();
|
||||
|
||||
Message msg = mockMessage(UUID.randomUUID());
|
||||
msg.setConversation(conv);
|
||||
msg.setAttachments(""); // non-null mais vide → L178 false → attachments = null
|
||||
|
||||
when(conversationRepository.findByIdAndParticipant(conv.getId(), membreId)).thenReturn(Optional.of(conv));
|
||||
doReturn(List.of(msg)).when(messageRepository).findByConversation(conv.getId(), 20);
|
||||
|
||||
List<MessageResponse> result = messageService.getMessages(conv.getId(), membreId, 20);
|
||||
|
||||
assertThat(result.get(0).getAttachments()).isNull();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user