This commit is contained in:
2026-02-19 17:50:14 +00:00
parent da95a64fb7
commit 6f629c53a6
171 changed files with 7281 additions and 1144 deletions

View File

@@ -0,0 +1,177 @@
package com.example.service.unit_testing;
import com.example.dto.dialogflow.base.DetectIntentResponseDTO;
import com.example.dto.dialogflow.conversation.*;
import com.example.dto.quickreplies.QuestionDTO;
import com.example.dto.quickreplies.QuickReplyDTO;
import com.example.mapper.conversation.ConversationEntryMapper;
import com.example.service.conversation.ConversationManagerService;
import com.example.service.conversation.FirestoreConversationService;
import com.example.service.conversation.MemoryStoreConversationService;
import com.example.service.quickreplies.QuickRepliesManagerService;
import com.example.service.quickreplies.QuickReplyContentService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.Instant;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class QuickRepliesManagerServiceTest {
@Mock private MemoryStoreConversationService memoryStoreConversationService;
@Mock private FirestoreConversationService firestoreConversationService;
@Mock private QuickReplyContentService quickReplyContentService;
@Mock private ConversationManagerService conversationManagerService;
@Mock private ConversationEntryMapper conversationEntryMapper;
private QuickRepliesManagerService quickRepliesManagerService;
// Test Data
private final String PHONE = "5555555555";
private final String SESSION_ID = "session-123";
private final String USER_ID = "user_by_phone_5555555555";
private final String CONTEXTO = "pagos";
@BeforeEach
void setUp() {
quickRepliesManagerService = new QuickRepliesManagerService(
conversationManagerService,
memoryStoreConversationService,
firestoreConversationService,
quickReplyContentService,
conversationEntryMapper
);
}
@Test
@DisplayName("manageConversation - Count 0 - NO MATCH: Should clear context and delegate to Dialogflow")
void manageConversation_Count0_NoMatch_ShouldDelegate() {
// 1. SETUP: User typed "Hola", but context "pagos" is active.
ExternalConvRequestDTO request = new ExternalConvRequestDTO("Hola", new UsuarioDTO(PHONE, "Nick"), "whatsapp", ConversationEntryType.CONVERSACION, null);
ConversationSessionDTO session = new ConversationSessionDTO(SESSION_ID, USER_ID, PHONE, Instant.now(), Instant.now(), "last", CONTEXTO);
// Mock Session Retrieval
when(memoryStoreConversationService.getSessionByTelefono(PHONE)).thenReturn(Mono.just(session));
// Mock History: Only the SYSTEM message (The Menu) exists. Count = 0.
ConversationMessageDTO sysMsg = new ConversationMessageDTO(MessageType.SYSTEM, Instant.now(), "Menu...", null, "whatsapp");
when(memoryStoreConversationService.getMessages(SESSION_ID)).thenReturn(Flux.just(sysMsg));
// Mock QR Content: The menu has options, but "Hola" is NOT one of them.
QuestionDTO q1 = new QuestionDTO("Ver Saldo", "desc", "Tu saldo is 10");
QuickReplyDTO qrDto = new QuickReplyDTO("Header", "Body", "Btn", "Section", List.of(q1));
when(quickReplyContentService.getQuickReplies(CONTEXTO)).thenReturn(Mono.just(qrDto));
// Mock Orchestrator Delegation (The expected outcome)
DetectIntentResponseDTO delegatedResponse = new DetectIntentResponseDTO("df-response", new QueryResultDTO("Hola soy Beto", null));
when(memoryStoreConversationService.updateSession(any())).thenReturn(Mono.empty()); // Clearing context
when(conversationManagerService.manageConversation(request)).thenReturn(Mono.just(delegatedResponse));
// 2. EXECUTE
StepVerifier.create(quickRepliesManagerService.manageConversation(request))
.expectNext(delegatedResponse)
.verifyComplete();
// 3. VERIFY
// Ensure we cleared the context
verify(memoryStoreConversationService).updateSession(argThat(s -> s.pantallaContexto() == null));
// Ensure we called the normal conversation manager
verify(conversationManagerService).manageConversation(request);
}
@Test
@DisplayName("manageConversation - Count 0 - MATCH: Should return QR Answer")
void manageConversation_Count0_Match_ShouldAnswer() {
// 1. SETUP: User typed "Ver Saldo" (Valid Option)
ExternalConvRequestDTO request = new ExternalConvRequestDTO("Ver Saldo", new UsuarioDTO(PHONE, "Nick"), "whatsapp", ConversationEntryType.CONVERSACION, null);
ConversationSessionDTO session = new ConversationSessionDTO(SESSION_ID, USER_ID, PHONE, Instant.now(), Instant.now(), "last", CONTEXTO);
when(memoryStoreConversationService.getSessionByTelefono(PHONE)).thenReturn(Mono.just(session));
// Count 0 (Last message was System)
ConversationMessageDTO sysMsg = new ConversationMessageDTO(MessageType.SYSTEM, Instant.now(), "Menu...", null, "whatsapp");
when(memoryStoreConversationService.getMessages(SESSION_ID)).thenReturn(Flux.just(sysMsg));
// Valid Option exists
QuestionDTO q1 = new QuestionDTO("Ver Saldo", "desc", "Tu saldo es 100 pesos");
QuickReplyDTO qrDto = new QuickReplyDTO("Header", "Body", "Btn", "Section", List.of(q1));
when(quickReplyContentService.getQuickReplies(CONTEXTO)).thenReturn(Mono.just(qrDto));
// Mocks for saving the conversation turn
mockPersistence();
when(memoryStoreConversationService.updateSession(any())).thenReturn(Mono.empty());
// 2. EXECUTE
StepVerifier.create(quickRepliesManagerService.manageConversation(request))
.assertNext(response -> {
// Expect the pre-defined answer from the JSON
assert response.queryResult().responseText().equals("Tu saldo es 100 pesos");
})
.verifyComplete();
// Verify we did NOT delegate
verify(conversationManagerService, never()).manageConversation(any());
}
@Test
@DisplayName("manageConversation - Count 1 - NO MATCH: Should save User Msg, Clear, and Delegate")
void manageConversation_Count1_NoMatch_ShouldDelegate() {
// 1. SETUP: User typed "Gracias" (Not an option)
ExternalConvRequestDTO request = new ExternalConvRequestDTO("Gracias", new UsuarioDTO(PHONE, "Nick"), "whatsapp", ConversationEntryType.CONVERSACION, null);
ConversationSessionDTO session = new ConversationSessionDTO(SESSION_ID, USER_ID, PHONE, Instant.now(), Instant.now(), "last", CONTEXTO);
when(memoryStoreConversationService.getSessionByTelefono(PHONE)).thenReturn(Mono.just(session));
// Mock History: System msg -> User msg (Invalid) -> Now this is the 2nd user msg (Count 1 logic)
// Wait, logic says count is messages AFTER last init.
// Sys (Init) -> User("Hola" - ignored previously) -> Current Request("Gracias")
ConversationMessageDTO sysMsg = new ConversationMessageDTO(MessageType.SYSTEM, Instant.now(), "Menu...", null, "whatsapp");
ConversationMessageDTO userMsg1 = new ConversationMessageDTO(MessageType.USER, Instant.now(), "Hola", null, "whatsapp");
when(memoryStoreConversationService.getMessages(SESSION_ID)).thenReturn(Flux.just(sysMsg, userMsg1));
QuestionDTO q1 = new QuestionDTO("Ver Saldo", "desc", "Tu saldo es 10");
QuickReplyDTO qrDto = new QuickReplyDTO("Header", "Body", "Btn", "Section", List.of(q1));
when(quickReplyContentService.getQuickReplies(CONTEXTO)).thenReturn(Mono.just(qrDto));
// Mock persistence for the user message (Manual save in the else block)
mockPersistence();
// Mock Delegation
DetectIntentResponseDTO delegatedResponse = new DetectIntentResponseDTO("df-response", new QueryResultDTO("De nada", null));
when(memoryStoreConversationService.updateSession(any())).thenReturn(Mono.empty());
when(conversationManagerService.manageConversation(request)).thenReturn(Mono.just(delegatedResponse));
// 2. EXECUTE
StepVerifier.create(quickRepliesManagerService.manageConversation(request))
.expectNext(delegatedResponse)
.verifyComplete();
// 3. VERIFY
// Ensure manual save was called (verifying flow of `persistConversationTurn`)
verify(memoryStoreConversationService, times(1)).saveMessage(eq(SESSION_ID), any());
verify(memoryStoreConversationService).updateSession(argThat(s -> s.pantallaContexto() == null));
verify(conversationManagerService).manageConversation(request);
}
// Helper to mock the complex save chain
private void mockPersistence() {
when(conversationEntryMapper.toConversationMessageDTO(any())).thenReturn(new ConversationMessageDTO(MessageType.USER, Instant.now(), "text", null, "wa"));
when(memoryStoreConversationService.saveSession(any())).thenReturn(Mono.empty());
when(memoryStoreConversationService.saveMessage(anyString(), any())).thenReturn(Mono.empty());
when(firestoreConversationService.saveSession(any())).thenReturn(Mono.empty());
when(firestoreConversationService.saveMessage(anyString(), any())).thenReturn(Mono.empty());
}
}