diff --git a/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java b/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java index b1fbca9..2b61f69 100644 --- a/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java +++ b/src/test/java/io/github/sashirestela/cleverclient/http/HttpProcessorTest.java @@ -1,30 +1,18 @@ package io.github.sashirestela.cleverclient.http; -import io.github.sashirestela.cleverclient.client.JavaHttpClientAdapter; import io.github.sashirestela.cleverclient.support.CleverClientException; -import io.github.sashirestela.cleverclient.support.Configurator; +import io.github.sashirestela.cleverclient.test.TestSupport; +import io.github.sashirestela.cleverclient.test.TestSupport.SyncType; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; -import java.net.http.HttpClient; -import java.net.http.HttpHeaders; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.nio.file.Paths; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.stream.Stream; @@ -32,55 +20,43 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@SuppressWarnings("unchecked") -@Order(2) -class HttpProcessorTest { - - HttpProcessor httpProcessor; - HttpClient httpClient = mock(HttpClient.class); - HttpResponse httpResponse = mock(HttpResponse.class); - HttpResponse> httpResponseStream = mock(HttpResponse.class); - HttpResponse httpResponseBinary = mock(HttpResponse.class); - HttpRequest httpRequest = mock(HttpRequest.class); - HttpHeaders httpHeaders = mock(HttpHeaders.class); + +interface HttpProcessorTest { + + HttpProcessor getHttpProcessor(); + + void setMocksForString(SyncType syncType, String result) throws IOException, InterruptedException; + + void setMocksForBinary(SyncType syncType, InputStream result) throws IOException, InterruptedException; + + void setMocksForStream(SyncType syncType, Stream result) throws IOException, InterruptedException; + + void setMocksForException() throws IOException, InterruptedException; + + void setMocksForStringWithError(String result) throws IOException, URISyntaxException; + + void setMocksForBinaryWithError(InputStream result) throws IOException, URISyntaxException; + + void setMocksForStreamWithError(Stream result) throws IOException, URISyntaxException; @BeforeAll static void setup() { - Configurator.builder() - .endOfStream("END") - .build(); - } - - @BeforeEach - void init() { - httpProcessor = HttpProcessor.builder() - .baseUrl("https://api.demo") - .headers(List.of()) - .clientAdapter(new JavaHttpClientAdapter(httpClient)) - .build(); + TestSupport.setupConfigurator(); } @Test - void shouldThownExceptionWhenCallingMethodReturnTypeIsUnsupported() { - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldThownExceptionWhenCallingMethodReturnTypeIsUnsupported() { + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); Exception exception = assertThrows(CleverClientException.class, () -> service.unsupportedMethod()); assertTrue(exception.getMessage().contains("Unsupported return type")); } @Test - void shouldReturnAStringSyncWhenMethodReturnTypeIsAString() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(httpResponse); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldReturnAStringSyncWhenMethodReturnTypeIsAString() throws IOException, InterruptedException { + setMocksForString(SyncType.SYNC, "{\"id\":100,\"description\":\"Description\",\"active\":true}"); + + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var actualDemo = service.getDemoPlain(100); var expectedDemo = "{\"id\":100,\"description\":\"Description\",\"active\":true}"; @@ -88,24 +64,18 @@ void shouldReturnAStringSyncWhenMethodReturnTypeIsAString() throws IOException, } @Test - void shouldThrownExceptionWhenMethodReturnTypeIsAString() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenThrow(new InterruptedException("The operation was interrupted")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldThrownExceptionWhenMethodReturnTypeIsAString() throws IOException, InterruptedException { + setMocksForException(); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); assertThrows(CleverClientException.class, () -> service.getDemoPlain(100)); } @Test - void shouldshouldReturnABinarySyncWhenMethodReturnTypeIsABinary() throws IOException, InterruptedException { + default void shouldshouldReturnABinarySyncWhenMethodReturnTypeIsABinary() throws IOException, InterruptedException { InputStream binaryData = new FileInputStream(new File("src/test/resources/image.png")); - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) - .thenReturn(httpResponseBinary); - when(httpResponseBinary.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponseBinary.headers()).thenReturn(HttpHeaders.of(Map.of(), (t, s) -> true)); - when(httpResponseBinary.body()).thenReturn(binaryData); + setMocksForBinary(SyncType.SYNC, binaryData); - var service = httpProcessor.createProxy(ITest.SyncService.class); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var actualDemo = service.getDemoBinary(100); var expectedDemo = binaryData; @@ -113,23 +83,17 @@ void shouldshouldReturnABinarySyncWhenMethodReturnTypeIsABinary() throws IOExcep } @Test - void shouldThrownExceptionWhenMethodReturnTypeIsABinary() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) - .thenThrow(new InterruptedException("The operation was interrupted")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldThrownExceptionWhenMethodReturnTypeIsABinary() throws IOException, InterruptedException { + setMocksForException(); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); assertThrows(CleverClientException.class, () -> service.getDemoBinary(100)); } @Test - void shouldReturnAnObjectSyncWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(httpResponse); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldReturnAnObjectSyncWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { + setMocksForString(SyncType.SYNC, "{\"id\":100,\"description\":\"Description\",\"active\":true}"); + + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var actualDemo = service.getDemo(100); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -137,24 +101,18 @@ void shouldReturnAnObjectSyncWhenMethodReturnTypeIsAnObject() throws IOException } @Test - void shouldThrownExceptionWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenThrow(new InterruptedException("The operation was interrupted")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldThrownExceptionWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { + setMocksForException(); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); assertThrows(CleverClientException.class, () -> service.getDemo(100)); } @Test - void shouldReturnAGenericSyncWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(httpResponse); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()) - .thenReturn("{\"id\":1,\"listDemo\":[{\"id\":100,\"description\":\"Description\",\"active\":true}]}"); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldReturnAGenericSyncWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { + setMocksForString(SyncType.SYNC, + "{\"id\":1,\"listDemo\":[{\"id\":100,\"description\":\"Description\",\"active\":true}]}"); + + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var actualGenericDemo = service.getGenericDemo(1); var actualDemo = actualGenericDemo.getListDemo().get(0); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -163,23 +121,17 @@ void shouldReturnAGenericSyncWhenMethodReturnTypeIsAnObject() throws IOException } @Test - void shouldThrownExceptionWhenMethodReturnTypeIsAGenericObject() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenThrow(new InterruptedException("The operation was interrupted")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldThrownExceptionWhenMethodReturnTypeIsAGenericObject() throws IOException, InterruptedException { + setMocksForException(); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); assertThrows(CleverClientException.class, () -> service.getGenericDemo(1)); } @Test - void shouldReturnAListSyncWhenMethodReturnTypeIsAList() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(httpResponse); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()).thenReturn("[{\"id\":100,\"description\":\"Description\",\"active\":true}]"); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldReturnAListSyncWhenMethodReturnTypeIsAList() throws IOException, InterruptedException { + setMocksForString(SyncType.SYNC, "[{\"id\":100,\"description\":\"Description\",\"active\":true}]"); + + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var actualListDemo = service.getDemos(); var actualDemo = actualListDemo.get(0); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -188,24 +140,18 @@ void shouldReturnAListSyncWhenMethodReturnTypeIsAList() throws IOException, Inte } @Test - void shouldThrownExceptionWhenMethodReturnTypeIsAList() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenThrow(new InterruptedException("The operation was interrupted")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldThrownExceptionWhenMethodReturnTypeIsAList() throws IOException, InterruptedException { + setMocksForException(); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); assertThrows(CleverClientException.class, () -> service.getDemos()); } @Test - void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStream() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) - .thenReturn(httpResponseStream); - when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponseStream.body()) - .thenReturn(Stream.of("data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStream() throws IOException, InterruptedException { + setMocksForStream(SyncType.SYNC, + Stream.of("data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); + + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var actualStreamDemo = service.getDemoStream(new ITest.RequestDemo("Descr", null)); var actualDemo = actualStreamDemo.findFirst().get(); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -214,26 +160,19 @@ void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStream() throws IOException, } @Test - void shouldThrownExceptionWhenMethodReturnTypeIsAStream() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) - .thenThrow(new InterruptedException("The operation was interrupted")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldThrownExceptionWhenMethodReturnTypeIsAStream() throws IOException, InterruptedException { + setMocksForException(); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var requestDemo = new ITest.RequestDemo("Descr", null); assertThrows(CleverClientException.class, () -> service.getDemoStream(requestDemo)); } @Test - void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStreamEvent() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) - .thenReturn(httpResponseStream); - when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponseStream.body()) - .thenReturn(Stream.of("event: created", - "data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStreamEvent() throws IOException, InterruptedException { + setMocksForStream(SyncType.SYNC, + Stream.of("event: created", "data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); + + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var actualStreamObject = service.getStreamEvent(new ITest.RequestDemo("Descr", null)); var actualObject = actualStreamObject.findFirst().get().getData(); var expectedObject = new ITest.Demo(100, "Description", true); @@ -242,24 +181,18 @@ void shouldReturnAStreamSyncWhenMethodReturnTypeIsAStreamEvent() throws IOExcept } @Test - void shouldThrownExceptionWhenMethodReturnTypeIsAStreamObject() throws IOException, InterruptedException { - when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) - .thenThrow(new InterruptedException("The operation was interrupted")); - - var service = httpProcessor.createProxy(ITest.SyncService.class); + default void shouldThrownExceptionWhenMethodReturnTypeIsAStreamObject() throws IOException, InterruptedException { + setMocksForException(); + var service = getHttpProcessor().createProxy(ITest.SyncService.class); var requestDemo = new ITest.RequestDemo("Descr", null); assertThrows(CleverClientException.class, () -> service.getStreamEvent(requestDemo)); } @Test - void shouldReturnAStringAsyncWhenMethodReturnTypeIsAString() { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponse)); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); - - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldReturnAStringAsyncWhenMethodReturnTypeIsAString() throws IOException, InterruptedException { + setMocksForString(SyncType.ASYNC, "{\"id\":100,\"description\":\"Description\",\"active\":true}"); + + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualDemo = service.getDemoPlain(100).join(); var expectedDemo = "{\"id\":100,\"description\":\"Description\",\"active\":true}"; @@ -267,15 +200,11 @@ void shouldReturnAStringAsyncWhenMethodReturnTypeIsAString() { } @Test - void shouldReturnABinaryAsyncWhenMethodReturnTypeIsABinary() throws FileNotFoundException { + default void shouldReturnABinaryAsyncWhenMethodReturnTypeIsABinary() throws IOException, InterruptedException { InputStream binaryData = new FileInputStream(new File("src/test/resources/image.png")); - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponseBinary)); - when(httpResponseBinary.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponseBinary.headers()).thenReturn(HttpHeaders.of(Map.of(), (t, s) -> true)); - when(httpResponseBinary.body()).thenReturn(binaryData); + setMocksForBinary(SyncType.ASYNC, binaryData); - var service = httpProcessor.createProxy(ITest.AsyncService.class); + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualDemo = service.getDemoBinary(100).join(); var expectedDemo = binaryData; @@ -283,14 +212,10 @@ void shouldReturnABinaryAsyncWhenMethodReturnTypeIsABinary() throws FileNotFound } @Test - void shouldReturnAnObjectAsyncWhenMethodReturnTypeIsAnObject() { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponse)); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); - - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldReturnAnObjectAsyncWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { + setMocksForString(SyncType.ASYNC, "{\"id\":100,\"description\":\"Description\",\"active\":true}"); + + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualDemo = service.getDemo(100).join(); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -298,15 +223,11 @@ void shouldReturnAnObjectAsyncWhenMethodReturnTypeIsAnObject() { } @Test - void shouldReturnAGenericAsyncWhenMethodReturnTypeIsAnObject() { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponse)); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()) - .thenReturn("{\"id\":1,\"listDemo\":[{\"id\":100,\"description\":\"Description\",\"active\":true}]}"); - - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldReturnAGenericAsyncWhenMethodReturnTypeIsAnObject() throws IOException, InterruptedException { + setMocksForString(SyncType.ASYNC, + "{\"id\":1,\"listDemo\":[{\"id\":100,\"description\":\"Description\",\"active\":true}]}"); + + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualGenericDemo = service.getGenericDemo(1).join(); var actualDemo = actualGenericDemo.getListDemo().get(0); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -315,14 +236,10 @@ void shouldReturnAGenericAsyncWhenMethodReturnTypeIsAnObject() { } @Test - void shouldReturnAListAsyncWhenMethodReturnTypeIsAList() { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponse)); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()).thenReturn("[{\"id\":100,\"description\":\"Description\",\"active\":true}]"); - - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldReturnAListAsyncWhenMethodReturnTypeIsAList() throws IOException, InterruptedException { + setMocksForString(SyncType.ASYNC, "[{\"id\":100,\"description\":\"Description\",\"active\":true}]"); + + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualListDemo = service.getDemos().join(); var actualDemo = actualListDemo.get(0); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -331,15 +248,11 @@ void shouldReturnAListAsyncWhenMethodReturnTypeIsAList() { } @Test - void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStream() { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponseStream)); - when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponseStream.body()) - .thenReturn(Stream.of("data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); - - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStream() throws IOException, InterruptedException { + setMocksForStream(SyncType.ASYNC, + Stream.of("data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); + + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualStreamDemo = service.getDemoStream(new ITest.RequestDemo("Descr", null)).join(); var actualDemo = actualStreamDemo.findFirst().get(); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -348,16 +261,11 @@ void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStream() { } @Test - void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStreamEvent() { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponseStream)); - when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponseStream.body()) - .thenReturn(Stream.of("event: created", - "data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); - - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStreamEvent() throws IOException, InterruptedException { + setMocksForStream(SyncType.ASYNC, + Stream.of("event: created", "data: {\"id\":100,\"description\":\"Description\",\"active\":true}")); + + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualStreamObject = service.getStreamEvent(new ITest.RequestDemo("Descr", null)).join(); var actualObject = actualStreamObject.findFirst().get().getData(); var expectedObject = new ITest.Demo(100, "Description", true); @@ -366,14 +274,10 @@ void shouldReturnAStreamAsyncWhenMethodReturnTypeIsAStreamEvent() { } @Test - void shouldReturnAnObjectWhenMethodIsAnnotatedWithMultipart() { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponse)); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); - when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t,s) -> true)); - when(httpResponse.body()).thenReturn("{\"id\":100,\"description\":\"Description\",\"active\":true}"); - - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldReturnAnObjectWhenMethodIsAnnotatedWithMultipart() throws IOException, InterruptedException { + setMocksForString(SyncType.ASYNC, "{\"id\":100,\"description\":\"Description\",\"active\":true}"); + + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualDemo = service.getFile(new ITest.RequestDemo("Descr", Paths.get("src/test/resources/image.png"))) .join(); var expectedDemo = new ITest.Demo(100, "Description", true); @@ -382,20 +286,12 @@ void shouldReturnAnObjectWhenMethodIsAnnotatedWithMultipart() { } @Test - void shouldThrownExceptionWhenCallingNoStreamingMethodAndServerRespondsWithError() throws URISyntaxException { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponse)); - when(httpHeaders.map()).thenReturn(Map.of()); - when(httpRequest.method()).thenReturn("GET"); - when(httpRequest.uri()).thenReturn(new URI("https://api.com")); - when(httpRequest.headers()).thenReturn(httpHeaders); - when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); - when(httpResponse.body()).thenReturn( + default void shouldThrownExceptionWhenCallingNoStreamingMethodAndServerRespondsWithError() + throws IOException, URISyntaxException { + setMocksForStringWithError( "{\"error\": {\"message\": \"The resource does not exist\", \"type\": \"T\", \"param\": \"P\", \"code\": \"C\"}}"); - when(httpResponse.headers()).thenReturn(httpHeaders); - when(httpResponse.request()).thenReturn(httpRequest); - var service = httpProcessor.createProxy(ITest.AsyncService.class); + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var futureService = service.getDemo(100); Exception exception = assertThrows(CompletionException.class, () -> futureService.join()); @@ -406,21 +302,14 @@ void shouldThrownExceptionWhenCallingNoStreamingMethodAndServerRespondsWithError } @Test - void shouldThrownExceptionWhenCallingStreamingMethodAndServerRespondsWithError() throws URISyntaxException { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponseStream)); - when(httpHeaders.map()).thenReturn(Map.of()); - when(httpRequest.method()).thenReturn("GET"); - when(httpRequest.uri()).thenReturn(new URI("https://api.com")); - when(httpRequest.headers()).thenReturn(httpHeaders); - when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); - when(httpResponseStream.body()).thenReturn(Stream.of( - "{\"error\": {\"message\": \"The resource does not exist\", \"type\": \"T\", \"param\": \"P\", \"code\": \"C\"}}")); - when(httpResponseStream.headers()).thenReturn(httpHeaders); - when(httpResponseStream.request()).thenReturn(httpRequest); + default void shouldThrownExceptionWhenCallingBinaryMethodAndServerRespondsWithError() + throws IOException, URISyntaxException { + setMocksForBinaryWithError(new ByteArrayInputStream( + "{\"error\": {\"message\": \"The resource does not exist\", \"type\": \"T\", \"param\": \"P\", \"code\": \"C\"}}" + .getBytes())); - var service = httpProcessor.createProxy(ITest.AsyncService.class); - var futureService = service.getDemoStream(new ITest.RequestDemo("Descr", null)); + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); + var futureService = service.getDemoBinary(100); Exception exception = assertThrows(CompletionException.class, () -> futureService.join()); CleverClientException nestedException = (CleverClientException) exception.getCause(); @@ -430,22 +319,13 @@ void shouldThrownExceptionWhenCallingStreamingMethodAndServerRespondsWithError() } @Test - void shouldThrownExceptionWhenCallingBinaryMethodAndServerRespondsWithError() throws URISyntaxException { - when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) - .thenReturn(CompletableFuture.completedFuture(httpResponseBinary)); - when(httpHeaders.map()).thenReturn(Map.of()); - when(httpRequest.method()).thenReturn("GET"); - when(httpRequest.uri()).thenReturn(new URI("https://api.com")); - when(httpRequest.headers()).thenReturn(httpHeaders); - when(httpResponseBinary.statusCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); - when(httpResponseBinary.body()).thenReturn(new ByteArrayInputStream( - "{\"error\": {\"message\": \"The resource does not exist\", \"type\": \"T\", \"param\": \"P\", \"code\": \"C\"}}" - .getBytes())); - when(httpResponseBinary.headers()).thenReturn(httpHeaders); - when(httpResponseBinary.request()).thenReturn(httpRequest); + default void shouldThrownExceptionWhenCallingStreamingMethodAndServerRespondsWithError() + throws IOException, URISyntaxException { + setMocksForStreamWithError(Stream.of( + "{\"error\": {\"message\": \"The resource does not exist\", \"type\": \"T\", \"param\": \"P\", \"code\": \"C\"}}")); - var service = httpProcessor.createProxy(ITest.AsyncService.class); - var futureService = service.getDemoBinary(100); + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); + var futureService = service.getDemoStream(new ITest.RequestDemo("Descr", null)); Exception exception = assertThrows(CompletionException.class, () -> futureService.join()); CleverClientException nestedException = (CleverClientException) exception.getCause(); @@ -455,8 +335,8 @@ void shouldThrownExceptionWhenCallingBinaryMethodAndServerRespondsWithError() th } @Test - void shouldExecuteDefaultMethodWhenItIsCalled() { - var service = httpProcessor.createProxy(ITest.AsyncService.class); + default void shouldExecuteDefaultMethodWhenItIsCalled() { + var service = getHttpProcessor().createProxy(ITest.AsyncService.class); var actualValue = service.defaultMethod("Test"); var expectedValue = "Hello Test"; diff --git a/src/test/java/io/github/sashirestela/cleverclient/http/JavaHttpProcessorTest.java b/src/test/java/io/github/sashirestela/cleverclient/http/JavaHttpProcessorTest.java new file mode 100644 index 0000000..40078b5 --- /dev/null +++ b/src/test/java/io/github/sashirestela/cleverclient/http/JavaHttpProcessorTest.java @@ -0,0 +1,149 @@ +package io.github.sashirestela.cleverclient.http; + +import io.github.sashirestela.cleverclient.client.JavaHttpClientAdapter; +import io.github.sashirestela.cleverclient.test.TestSupport.SyncType; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@SuppressWarnings("unchecked") +class JavaHttpProcessorTest implements HttpProcessorTest { + + HttpProcessor httpProcessor; + HttpClient httpClient = mock(HttpClient.class); + HttpResponse httpResponse = mock(HttpResponse.class); + HttpResponse> httpResponseStream = mock(HttpResponse.class); + HttpResponse httpResponseBinary = mock(HttpResponse.class); + HttpRequest httpRequest = mock(HttpRequest.class); + HttpHeaders httpHeaders = mock(HttpHeaders.class); + + JavaHttpProcessorTest() { + httpClient = mock(HttpClient.class); + httpResponse = mock(HttpResponse.class); + httpResponseStream = mock(HttpResponse.class); + httpResponseBinary = mock(HttpResponse.class); + httpRequest = mock(HttpRequest.class); + httpHeaders = mock(HttpHeaders.class); + } + + @Override + public HttpProcessor getHttpProcessor() { + httpProcessor = HttpProcessor.builder() + .baseUrl("https://api.demo") + .headers(List.of()) + .clientAdapter(new JavaHttpClientAdapter(httpClient)) + .build(); + return httpProcessor; + } + + @Override + public void setMocksForString(SyncType syncType, String result) throws IOException, InterruptedException { + if (syncType == SyncType.SYNC) { + when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) + .thenReturn(httpResponse); + } else { + when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) + .thenReturn(CompletableFuture.completedFuture(httpResponse)); + } + when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponse.headers()).thenReturn(HttpHeaders.of(Map.of(), (t, s) -> true)); + when(httpResponse.body()).thenReturn(result); + } + + @Override + public void setMocksForBinary(SyncType syncType, InputStream result) throws IOException, InterruptedException { + if (syncType == SyncType.SYNC) { + when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) + .thenReturn(httpResponseBinary); + } else { + when(httpClient.sendAsync(any(HttpRequest.class), + any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) + .thenReturn(CompletableFuture.completedFuture(httpResponseBinary)); + } + when(httpResponseBinary.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseBinary.headers()).thenReturn(HttpHeaders.of(Map.of(), (t, s) -> true)); + when(httpResponseBinary.body()).thenReturn(result); + } + + @Override + public void setMocksForStream(SyncType syncType, Stream result) throws IOException, InterruptedException { + if (syncType == SyncType.SYNC) { + when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) + .thenReturn(httpResponseStream); + } else { + when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) + .thenReturn(CompletableFuture.completedFuture(httpResponseStream)); + } + when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_OK); + when(httpResponseStream.headers()).thenReturn(HttpHeaders.of(Map.of(), (t, s) -> true)); + when(httpResponseStream.body()).thenReturn(result); + } + + @Override + public void setMocksForException() throws IOException, InterruptedException { + when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) + .thenThrow(new InterruptedException("The operation was interrupted")); + when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) + .thenThrow(new InterruptedException("The operation was interrupted")); + when(httpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) + .thenThrow(new InterruptedException("The operation was interrupted")); + } + + @Override + public void setMocksForStringWithError(String result) throws IOException, URISyntaxException { + when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofString().getClass()))) + .thenReturn(CompletableFuture.completedFuture(httpResponse)); + when(httpHeaders.map()).thenReturn(Map.of()); + when(httpRequest.method()).thenReturn("GET"); + when(httpRequest.uri()).thenReturn(new URI("https://api.com")); + when(httpRequest.headers()).thenReturn(httpHeaders); + when(httpResponse.statusCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); + when(httpResponse.body()).thenReturn(result); + when(httpResponse.headers()).thenReturn(httpHeaders); + when(httpResponse.request()).thenReturn(httpRequest); + } + + @Override + public void setMocksForBinaryWithError(InputStream result) throws IOException, URISyntaxException { + when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofInputStream().getClass()))) + .thenReturn(CompletableFuture.completedFuture(httpResponseBinary)); + when(httpHeaders.map()).thenReturn(Map.of()); + when(httpRequest.method()).thenReturn("GET"); + when(httpRequest.uri()).thenReturn(new URI("https://api.com")); + when(httpRequest.headers()).thenReturn(httpHeaders); + when(httpResponseBinary.statusCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); + when(httpResponseBinary.body()).thenReturn(result); + when(httpResponseBinary.headers()).thenReturn(httpHeaders); + when(httpResponseBinary.request()).thenReturn(httpRequest); + } + + @Override + public void setMocksForStreamWithError(Stream result) throws IOException, URISyntaxException { + when(httpClient.sendAsync(any(HttpRequest.class), any(HttpResponse.BodyHandlers.ofLines().getClass()))) + .thenReturn(CompletableFuture.completedFuture(httpResponseStream)); + when(httpHeaders.map()).thenReturn(Map.of()); + when(httpRequest.method()).thenReturn("GET"); + when(httpRequest.uri()).thenReturn(new URI("https://api.com")); + when(httpRequest.headers()).thenReturn(httpHeaders); + when(httpResponseStream.statusCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); + when(httpResponseStream.body()).thenReturn(result); + when(httpResponseStream.headers()).thenReturn(httpHeaders); + when(httpResponseStream.request()).thenReturn(httpRequest); + } + +} diff --git a/src/test/java/io/github/sashirestela/cleverclient/http/OkHttpProcessorTest.java b/src/test/java/io/github/sashirestela/cleverclient/http/OkHttpProcessorTest.java new file mode 100644 index 0000000..0f4bd03 --- /dev/null +++ b/src/test/java/io/github/sashirestela/cleverclient/http/OkHttpProcessorTest.java @@ -0,0 +1,182 @@ +package io.github.sashirestela.cleverclient.http; + +import io.github.sashirestela.cleverclient.client.OkHttpClientAdapter; +import io.github.sashirestela.cleverclient.test.TestSupport.SyncType; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class OkHttpProcessorTest implements HttpProcessorTest { + + HttpProcessor httpProcessor; + OkHttpClient okHttpClient; + Response okHttpResponse; + ResponseBody responseBody; + Request okHttpRequest; + HttpUrl httpUrl; + Call call; + + OkHttpProcessorTest() { + okHttpClient = mock(OkHttpClient.class); + okHttpResponse = mock(Response.class); + responseBody = mock(ResponseBody.class); + call = mock(Call.class); + okHttpRequest = mock(Request.class); + httpUrl = mock(HttpUrl.class); + } + + @Override + public HttpProcessor getHttpProcessor() { + httpProcessor = HttpProcessor.builder() + .baseUrl("https://api.demo") + .headers(List.of()) + .clientAdapter(new OkHttpClientAdapter(okHttpClient)) + .build(); + return httpProcessor; + } + + @Override + public void setMocksForString(SyncType syncType, String result) throws IOException { + when(okHttpClient.newCall(any(okhttp3.Request.class))).thenReturn(call); + + if (syncType == SyncType.SYNC) { + when(call.execute()).thenReturn(okHttpResponse); + } else { + doAnswer(invocation -> { + Callback callback = invocation.getArgument(0); + callback.onResponse(call, okHttpResponse); + return null; + }).when(call).enqueue(any(Callback.class)); + } + + when(okHttpResponse.code()).thenReturn(HttpURLConnection.HTTP_OK); + when(okHttpResponse.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.body()).thenReturn(responseBody); + when(responseBody.string()).thenReturn(result); + } + + @Override + public void setMocksForBinary(SyncType syncType, InputStream result) throws IOException { + when(okHttpClient.newCall(any(okhttp3.Request.class))).thenReturn(call); + + if (syncType == SyncType.SYNC) { + when(call.execute()).thenReturn(okHttpResponse); + } else { + doAnswer(invocation -> { + Callback callback = invocation.getArgument(0); + callback.onResponse(call, okHttpResponse); + return null; + }).when(call).enqueue(any(Callback.class)); + } + + when(okHttpResponse.code()).thenReturn(HttpURLConnection.HTTP_OK); + when(okHttpResponse.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.body()).thenReturn(responseBody); + when(responseBody.byteStream()).thenReturn(result); + } + + @Override + public void setMocksForStream(SyncType syncType, Stream result) throws IOException { + when(okHttpClient.newCall(any(okhttp3.Request.class))).thenReturn(call); + + if (syncType == SyncType.SYNC) { + when(call.execute()).thenReturn(okHttpResponse); + } else { + doAnswer(invocation -> { + Callback callback = invocation.getArgument(0); + callback.onResponse(call, okHttpResponse); + return null; + }).when(call).enqueue(any(Callback.class)); + } + + when(okHttpResponse.code()).thenReturn(HttpURLConnection.HTTP_OK); + when(okHttpResponse.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.body()).thenReturn(responseBody); + when(responseBody.byteStream()).thenReturn( + new ByteArrayInputStream(result.collect(Collectors.joining("\n")).getBytes(StandardCharsets.UTF_8))); + } + + @Override + public void setMocksForException() throws IOException { + when(okHttpClient.newCall(any(okhttp3.Request.class))).thenReturn(call); + when(call.execute()).thenThrow(new IOException("The operation was interrupted")); + } + + @Override + public void setMocksForStringWithError(String result) throws IOException { + when(okHttpClient.newCall(any(okhttp3.Request.class))).thenReturn(call); + doAnswer(invocation -> { + Callback callback = invocation.getArgument(0); + callback.onResponse(call, okHttpResponse); + return null; + }).when(call).enqueue(any(Callback.class)); + when(httpUrl.toString()).thenReturn("https://api.com"); + when(okHttpRequest.method()).thenReturn("GET"); + when(okHttpRequest.url()).thenReturn(httpUrl); + when(okHttpRequest.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.code()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); + when(okHttpResponse.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.body()).thenReturn(responseBody); + when(responseBody.string()).thenReturn(result); + when(okHttpResponse.request()).thenReturn(okHttpRequest); + } + + @Override + public void setMocksForBinaryWithError(InputStream result) throws IOException { + when(okHttpClient.newCall(any(okhttp3.Request.class))).thenReturn(call); + doAnswer(invocation -> { + Callback callback = invocation.getArgument(0); + callback.onResponse(call, okHttpResponse); + return null; + }).when(call).enqueue(any(Callback.class)); + when(httpUrl.toString()).thenReturn("https://api.com"); + when(okHttpRequest.method()).thenReturn("GET"); + when(okHttpRequest.url()).thenReturn(httpUrl); + when(okHttpRequest.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.code()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); + when(okHttpResponse.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.body()).thenReturn(responseBody); + when(responseBody.byteStream()).thenReturn(result); + when(okHttpResponse.request()).thenReturn(okHttpRequest); + } + + @Override + public void setMocksForStreamWithError(Stream result) throws IOException { + when(okHttpClient.newCall(any(okhttp3.Request.class))).thenReturn(call); + doAnswer(invocation -> { + Callback callback = invocation.getArgument(0); + callback.onResponse(call, okHttpResponse); + return null; + }).when(call).enqueue(any(Callback.class)); + when(httpUrl.toString()).thenReturn("https://api.com"); + when(okHttpRequest.method()).thenReturn("GET"); + when(okHttpRequest.url()).thenReturn(httpUrl); + when(okHttpRequest.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.code()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); + when(okHttpResponse.headers()).thenReturn(okhttp3.Headers.of(Map.of())); + when(okHttpResponse.body()).thenReturn(responseBody); + when(responseBody.byteStream()).thenReturn( + new ByteArrayInputStream(result.collect(Collectors.joining("\n")).getBytes(StandardCharsets.UTF_8))); + when(okHttpResponse.request()).thenReturn(okHttpRequest); + } + +}