.
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user