package com.example.service.unit_testing; import com.example.dto.dialogflow.base.DetectIntentRequestDTO; import com.example.dto.dialogflow.base.DetectIntentResponseDTO; import com.example.exception.DialogflowClientException; import com.example.mapper.conversation.DialogflowRequestMapper; import com.example.mapper.conversation.DialogflowResponseMapper; import com.example.service.base.DialogflowClientService; import com.google.api.gax.rpc.ApiException; import com.google.api.gax.rpc.StatusCode; import com.google.cloud.dialogflow.cx.v3.DetectIntentRequest; import com.google.cloud.dialogflow.cx.v3.DetectIntentResponse; import com.google.cloud.dialogflow.cx.v3.SessionsClient; import com.google.cloud.dialogflow.cx.v3.SessionsSettings; import io.grpc.Status; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import reactor.test.StepVerifier; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class DialogflowClientServiceTest { private static final String PROJECT_ID = "test-project"; private static final String LOCATION = "us-central1"; private static final String AGENT_ID = "test-agent"; private static final String SESSION_ID = "test-session-123"; @Mock private DialogflowRequestMapper mockRequestMapper; @Mock private DialogflowResponseMapper mockResponseMapper; @Mock private SessionsClient mockSessionsClient; private MockedStatic mockedStaticSessionsClient; private DialogflowClientService dialogflowClientService; @BeforeEach void setUp() throws IOException { mockedStaticSessionsClient = Mockito.mockStatic(SessionsClient.class); mockedStaticSessionsClient.when(() -> SessionsClient.create(any(SessionsSettings.class))) .thenReturn(mockSessionsClient); dialogflowClientService = new DialogflowClientService( PROJECT_ID, LOCATION, AGENT_ID, mockRequestMapper, mockResponseMapper ); } @AfterEach void tearDown() { mockedStaticSessionsClient.close(); } @Test void constructor_shouldInitializeClientSuccessfully() { assertNotNull(dialogflowClientService); mockedStaticSessionsClient.verify(() -> SessionsClient.create(any(SessionsSettings.class))); } @Test void closeSessionsClient_shouldCloseClient() { dialogflowClientService.closeSessionsClient(); verify(mockSessionsClient, times(1)).close(); } @Test void detectIntent_whenSuccess_shouldReturnMappedResponse() { // Arrange DetectIntentRequestDTO requestDTO = mock(DetectIntentRequestDTO.class); DetectIntentRequest.Builder requestBuilder = DetectIntentRequest.newBuilder(); DetectIntentRequest finalRequest = DetectIntentRequest.newBuilder() .setSession(String.format("projects/%s/locations/%s/agents/%s/sessions/%s", PROJECT_ID, LOCATION, AGENT_ID, SESSION_ID)) .build(); DetectIntentResponse dfResponse = DetectIntentResponse.newBuilder().build(); DetectIntentResponseDTO expectedResponseDTO = mock(DetectIntentResponseDTO.class); when(mockRequestMapper.mapToDetectIntentRequestBuilder(requestDTO)).thenReturn(requestBuilder); when(mockSessionsClient.detectIntent(any(DetectIntentRequest.class))).thenReturn(dfResponse); when(mockResponseMapper.mapFromDialogflowResponse(dfResponse, SESSION_ID)).thenReturn(expectedResponseDTO); // Act & Assert StepVerifier.create(dialogflowClientService.detectIntent(SESSION_ID, requestDTO)) .expectNext(expectedResponseDTO) .verifyComplete(); verify(mockSessionsClient).detectIntent(finalRequest); verify(mockResponseMapper).mapFromDialogflowResponse(dfResponse, SESSION_ID); } @Test void detectIntent_whenRequestMapperFails_shouldReturnError() { DetectIntentRequestDTO requestDTO = mock(DetectIntentRequestDTO.class); when(mockRequestMapper.mapToDetectIntentRequestBuilder(requestDTO)) .thenThrow(new IllegalArgumentException("Invalid mapping")); StepVerifier.create(dialogflowClientService.detectIntent(SESSION_ID, requestDTO)) .expectError(IllegalArgumentException.class) .verify(); verify(mockSessionsClient, never()).detectIntent(any(DetectIntentRequest.class)); } @Test void detectIntent_whenDialogflowApiThrowsApiException_shouldReturnDialogflowClientException() { DetectIntentRequestDTO requestDTO = mock(DetectIntentRequestDTO.class); DetectIntentRequest.Builder requestBuilder = DetectIntentRequest.newBuilder(); ApiException apiException = new ApiException( "API Error", null, new StatusCode() { @Override public Code getCode() { return Code.UNAVAILABLE; } @Override public Object getTransportCode() { return Status.Code.UNAVAILABLE; } }, false ); when(mockRequestMapper.mapToDetectIntentRequestBuilder(requestDTO)).thenReturn(requestBuilder); when(mockSessionsClient.detectIntent(any(DetectIntentRequest.class))).thenThrow(apiException); StepVerifier.create(dialogflowClientService.detectIntent(SESSION_ID, requestDTO)) .expectError(DialogflowClientException.class) .verify(); } @Test void detectIntent_withNullSessionId_shouldThrowNullPointerException() { DetectIntentRequestDTO requestDTO = mock(DetectIntentRequestDTO.class); assertThrows(NullPointerException.class, () -> { dialogflowClientService.detectIntent(null, requestDTO); }); } @Test void detectIntent_withNullRequest_shouldThrowNullPointerException() { assertThrows(NullPointerException.class, () -> { dialogflowClientService.detectIntent(SESSION_ID, null); }); } }