From 1714ce8d74ecbd3e946fc31a42d3f4852e861a23 Mon Sep 17 00:00:00 2001 From: Artur Ciocanu Date: Sun, 16 Feb 2025 16:17:13 +0200 Subject: [PATCH] Adjusted the Dapr HTTP tests --- .../main/java/io/dapr/client/DaprHttp.java | 7 +- .../java/io/dapr/client/DaprHttpTest.java | 609 +++++++++++------- .../java/io/dapr/client/MockHttpResponse.java | 5 + 3 files changed, 375 insertions(+), 246 deletions(-) diff --git a/sdk/src/main/java/io/dapr/client/DaprHttp.java b/sdk/src/main/java/io/dapr/client/DaprHttp.java index 124dc0e10..7cbaad3bc 100644 --- a/sdk/src/main/java/io/dapr/client/DaprHttp.java +++ b/sdk/src/main/java/io/dapr/client/DaprHttp.java @@ -129,6 +129,11 @@ public int getStatusCode() { */ private static final byte[] EMPTY_BYTES = new byte[0]; + /** + * Empty Body Publisher + */ + private static final HttpRequest.BodyPublisher EMPTY_BODY_PUBLISHER = HttpRequest.BodyPublishers.noBody(); + /** * Endpoint used to communicate to Dapr's HTTP endpoint. */ @@ -289,7 +294,7 @@ private CompletableFuture doInvokeApi( requestBuilder.DELETE(); } else if (HttpMethods.HEAD.name().equals(method)) { // HTTP HEAD is not exposed as a normal method - requestBuilder.method(HttpMethods.HEAD.name(), null); + requestBuilder.method(HttpMethods.HEAD.name(), EMPTY_BODY_PUBLISHER); } else { requestBuilder.method(method, body); } diff --git a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java index 7d0762209..8c0883a4a 100644 --- a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java @@ -28,20 +28,17 @@ import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; import java.io.IOException; -import java.net.HttpURLConnection; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import static io.dapr.utils.TestUtils.formatIpAddress; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -50,21 +47,21 @@ @ExtendWith(SystemStubsExtension.class) public class DaprHttpTest { - @SystemStub - public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); - - private static final String STATE_PATH = DaprHttp.API_VERSION + "/state"; - + private static final int HTTP_OK = 200; + private static final int HTTP_SERVER_ERROR = 500; private static final String EXPECTED_RESULT = "{\"data\":\"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"}"; - + + @SystemStub + private final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + private String sidecarIp; private String daprTokenApi; private HttpClient httpClient; - private ObjectSerializer serializer = new ObjectSerializer(); + private final ObjectSerializer serializer = new ObjectSerializer(); @BeforeEach public void setUp() { @@ -76,7 +73,7 @@ public void setUp() { @Test public void invokeApi_daprApiToken_present() throws IOException { byte[] content = serializer.serialize(EXPECTED_RESULT); - MockHttpResponse mockHttpResponse = new MockHttpResponse(content, 200); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); @@ -106,238 +103,360 @@ public void invokeApi_daprApiToken_present() throws IOException { assertEquals("xyz", request.headers().firstValue(Headers.DAPR_API_TOKEN).get()); } -// @Test -// public void invokeApi_daprApiToken_absent() throws IOException { -// mockInterceptor.addRule() -// .post("http://" + sidecarIp + ":3500/v1.0/state") -// .not() -// .hasHeader(Headers.DAPR_API_TOKEN) -// .respond(serializer.serialize(EXPECTED_RESULT)); -// assertNull(Properties.API_TOKEN.get()); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("POST", "v1.0/state".split("/"), null, (byte[]) null, null, Context.empty()); -// DaprHttp.Response response = mono.block(); -// String body = serializer.deserialize(response.getBody(), String.class); -// assertEquals(EXPECTED_RESULT, body); -// } -// -// @Test -// public void invokeMethod() throws IOException { -// Map headers = new HashMap<>(); -// headers.put("content-type", "text/html"); -// headers.put("header1", "value1"); -// mockInterceptor.addRule() -// .post("http://" + sidecarIp + ":3500/v1.0/state") -// .respond(serializer.serialize(EXPECTED_RESULT)); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("POST", "v1.0/state".split("/"), null, (byte[]) null, headers, Context.empty()); -// DaprHttp.Response response = mono.block(); -// String body = serializer.deserialize(response.getBody(), String.class); -// assertEquals(EXPECTED_RESULT, body); -// } -// -// @Test -// public void invokeMethodIPv6() throws IOException { -// sidecarIp = formatIpAddress("2001:db8:3333:4444:5555:6666:7777:8888"); -// Map headers = new HashMap<>(); -// headers.put("content-type", "text/html"); -// headers.put("header1", "value1"); -// mockInterceptor.addRule() -// .post("http://" + sidecarIp + ":3500/v1.0/state") -// .respond(serializer.serialize(EXPECTED_RESULT)); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("POST", "v1.0/state".split("/"), null, (byte[]) null, headers, Context.empty()); -// DaprHttp.Response response = mono.block(); -// String body = serializer.deserialize(response.getBody(), String.class); -// assertEquals(EXPECTED_RESULT, body); -// } -// -// @Test -// public void invokePostMethod() throws IOException { -// mockInterceptor.addRule() -// .post("http://" + sidecarIp + ":3500/v1.0/state") -// .respond(serializer.serialize(EXPECTED_RESULT)) -// .addHeader("Header", "Value"); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("POST", "v1.0/state".split("/"), null, "", null, Context.empty()); -// DaprHttp.Response response = mono.block(); -// String body = serializer.deserialize(response.getBody(), String.class); -// assertEquals(EXPECTED_RESULT, body); -// } -// -// @Test -// public void invokeDeleteMethod() throws IOException { -// mockInterceptor.addRule() -// .delete("http://" + sidecarIp + ":3500/v1.0/state") -// .respond(serializer.serialize(EXPECTED_RESULT)); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("DELETE", "v1.0/state".split("/"), null, (String) null, null, Context.empty()); -// DaprHttp.Response response = mono.block(); -// String body = serializer.deserialize(response.getBody(), String.class); -// assertEquals(EXPECTED_RESULT, body); -// } -// -// @Test -// public void invokeHEADMethod() throws IOException { -// mockInterceptor.addRule().head("http://127.0.0.1:3500/v1.0/state").respond(HttpURLConnection.HTTP_OK); -// DaprHttp daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("HEAD", "v1.0/state".split("/"), null, (String) null, null, Context.empty()); -// DaprHttp.Response response = mono.block(); -// assertEquals(HttpURLConnection.HTTP_OK, response.getStatusCode()); -// } -// -// @Test -// public void invokeGetMethod() throws IOException { -// mockInterceptor.addRule() -// .get("http://" + sidecarIp + ":3500/v1.0/get") -// .respond(serializer.serialize(EXPECTED_RESULT)); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = daprHttp.invokeApi("GET", "v1.0/get".split("/"), null, null, Context.empty()); -// DaprHttp.Response response = mono.block(); -// String body = serializer.deserialize(response.getBody(), String.class); -// assertEquals(EXPECTED_RESULT, body); -// } -// -// @Test -// public void invokeMethodWithHeaders() throws IOException { -// Map headers = new HashMap<>(); -// headers.put("header", "value"); -// headers.put("header1", "value1"); -// Map> urlParameters = new HashMap<>(); -// urlParameters.put("orderId", Collections.singletonList("41")); -// mockInterceptor.addRule() -// .get("http://" + sidecarIp + ":3500/v1.0/state/order?orderId=41") -// .respond(serializer.serialize(EXPECTED_RESULT)); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("GET", "v1.0/state/order".split("/"), urlParameters, headers, Context.empty()); -// DaprHttp.Response response = mono.block(); -// String body = serializer.deserialize(response.getBody(), String.class); -// assertEquals(EXPECTED_RESULT, body); -// } -// -// @Test -// public void invokePostMethodRuntime() throws IOException { -// mockInterceptor.addRule() -// .post("http://" + sidecarIp + ":3500/v1.0/state") -// .respond(500); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = -// daprHttp.invokeApi("POST", "v1.0/state".split("/"), null, null, Context.empty()); -// StepVerifier.create(mono).expectError(RuntimeException.class).verify(); -// } -// -// @Test -// public void invokePostDaprError() throws IOException { -// mockInterceptor.addRule() -// .post("http://" + sidecarIp + ":3500/v1.0/state") -// .respond(500, ResponseBody.create(MediaType.parse("text"), -// "{\"errorCode\":null,\"message\":null}")); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = daprHttp.invokeApi("POST", "v1.0/state".split("/"), null, null, Context.empty()); -// StepVerifier.create(mono).expectError(RuntimeException.class).verify(); -// } -// -// @Test -// public void invokePostMethodUnknownError() throws IOException { -// mockInterceptor.addRule() -// .post("http://" + sidecarIp + ":3500/v1.0/state") -// .respond(500, ResponseBody.create(MediaType.parse("application/json"), -// "{\"errorCode\":\"null\",\"message\":\"null\"}")); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono mono = daprHttp.invokeApi("POST", "v1.0/state".split("/"), null, null, Context.empty()); -// StepVerifier.create(mono).expectError(RuntimeException.class).verify(); -// } -// -// @Test -// public void validateExceptionParsing() { -// final String payload = "{" + -// "\"errorCode\":\"ERR_PUBSUB_NOT_FOUND\"," + -// "\"message\":\"pubsub abc is not found\"," + -// "\"details\":[" + -// "{" + -// "\"@type\":\"type.googleapis.com/google.rpc.ErrorInfo\"," + -// "\"domain\":\"dapr.io\"," + -// "\"metadata\":{}," + -// "\"reason\":\"DAPR_PUBSUB_NOT_FOUND\"" + -// "}]}"; -// mockInterceptor.addRule() -// .post("http://127.0.0.1:3500/v1.0/pubsub/publish") -// .respond(500, ResponseBody.create(MediaType.parse("application/json"), -// payload)); -// DaprHttp daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3500, daprTokenApi, okHttpClient); -// Mono mono = daprHttp.invokeApi("POST", "v1.0/pubsub/publish".split("/"), null, null, Context.empty()); -// StepVerifier.create(mono).expectErrorMatches(e -> { -// assertEquals(DaprException.class, e.getClass()); -// DaprException daprException = (DaprException)e; -// assertEquals("ERR_PUBSUB_NOT_FOUND", daprException.getErrorCode()); -// assertEquals("DAPR_PUBSUB_NOT_FOUND", -// daprException.getErrorDetails() -// .get(DaprErrorDetails.ErrorDetailType.ERROR_INFO, "reason", TypeRef.STRING)); -// return true; -// }).verify(); -// } -// -// /** -// * The purpose of this test is to show that it doesn't matter when the client is called, the actual coll to DAPR -// * will be done when the output Mono response call the Mono.block method. -// * Like for instanche if you call getState, withouth blocking for the response, and then call delete for the same state -// * you just retrived but block for the delete response, when later you block for the response of the getState, you will -// * not found the state. -// *

This test will execute the following flow:

-// *
    -// *
  1. Exeucte client getState for Key=key1
  2. -// *
  3. Block for result to the the state
  4. -// *
  5. Assert the Returned State is the expected to key1
  6. -// *
  7. Execute client getState for Key=key2
  8. -// *
  9. Execute client deleteState for Key=key2
  10. -// *
  11. Block for deleteState call.
  12. -// *
  13. Block for getState for Key=key2 and Assert they 2 was not found.
  14. -// *
-// * -// * @throws IOException - Test will fail if any unexpected exception is being thrown -// */ -// @Test() -// public void testCallbackCalledAtTheExpectedTimeTest() throws IOException { -// String deletedStateKey = "deletedKey"; -// String existingState = "existingState"; -// String urlDeleteState = STATE_PATH + "/" + deletedStateKey; -// String urlExistingState = STATE_PATH + "/" + existingState; -// mockInterceptor.addRule() -// .get("http://" + sidecarIp + ":3500/" + urlDeleteState) -// .respond(200, ResponseBody.create(MediaType.parse("application/json"), -// deletedStateKey)); -// mockInterceptor.addRule() -// .delete("http://" + sidecarIp + ":3500/" + urlDeleteState) -// .respond(204); -// mockInterceptor.addRule() -// .get("http://" + sidecarIp + ":3500/" + urlExistingState) -// .respond(200, ResponseBody.create(MediaType.parse("application/json"), -// serializer.serialize(existingState))); -// DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, okHttpClient); -// Mono response = daprHttp.invokeApi("GET", urlExistingState.split("/"), null, null, Context.empty()); -// assertEquals(existingState, serializer.deserialize(response.block().getBody(), String.class)); -// Mono responseDeleted = daprHttp.invokeApi("GET", urlDeleteState.split("/"), null, null, Context.empty()); -// Mono responseDeleteKey = -// daprHttp.invokeApi("DELETE", urlDeleteState.split("/"), null, null, Context.empty()); -// assertNull(serializer.deserialize(responseDeleteKey.block().getBody(), String.class)); -// mockInterceptor.reset(); -// mockInterceptor.addRule() -// .get("http://" + sidecarIp + ":3500/" + urlDeleteState) -// .respond(404, ResponseBody.create(MediaType.parse("application/json"), -// "{\"errorCode\":\"404\",\"message\":\"State Not Found\"}")); -// try { -// responseDeleted.block(); -// fail("Expected DaprException"); -// } catch (Exception ex) { -// assertEquals(DaprException.class, ex.getClass()); -// } -// } + @Test + public void invokeApi_daprApiToken_absent() throws IOException { + byte[] content = serializer.serialize(EXPECTED_RESULT); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + assertNull(Properties.API_TOKEN.get()); + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/state".split("/"), + null, + (byte[]) null, + null, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals(EXPECTED_RESULT, body); + assertEquals("POST", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state", request.uri().toString()); + assertFalse(request.headers().map().containsKey(Headers.DAPR_API_TOKEN)); + } + + @Test + public void invokeMethod() throws IOException { + Map headers = Map.of( + "content-type", "text/html", + "header1", "value1" + ); + byte[] content = serializer.serialize(EXPECTED_RESULT); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/state".split("/"), + null, + (byte[]) null, + headers, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals(EXPECTED_RESULT, body); + assertEquals("POST", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state", request.uri().toString()); + assertEquals("text/html", request.headers().firstValue("content-type").get()); + assertEquals("value1", request.headers().firstValue("header1").get()); + } + + @Test + public void invokeMethodIPv6() throws IOException { + sidecarIp = formatIpAddress("2001:db8:3333:4444:5555:6666:7777:8888"); + Map headers = Map.of( + "content-type", "text/html", + "header1", "value1" + ); + byte[] content = serializer.serialize(EXPECTED_RESULT); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/state".split("/"), + null, + (byte[]) null, + headers, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals(EXPECTED_RESULT, body); + assertEquals("POST", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state", request.uri().toString()); + assertEquals("text/html", request.headers().firstValue("content-type").get()); + assertEquals("value1", request.headers().firstValue("header1").get()); + } + + @Test + public void invokePostMethod() throws IOException { + byte[] content = serializer.serialize(EXPECTED_RESULT); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/state".split("/"), + null, + "", + null, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals(EXPECTED_RESULT, body); + assertEquals("POST", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state", request.uri().toString()); + } + + @Test + public void invokeDeleteMethod() throws IOException { + byte[] content = serializer.serialize(EXPECTED_RESULT); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "DELETE", + "v1.0/state".split("/"), + null, + (String) null, + null, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals(EXPECTED_RESULT, body); + assertEquals("DELETE", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state", request.uri().toString()); + } + + @Test + public void invokeHeadMethod() { + MockHttpResponse mockHttpResponse = new MockHttpResponse(HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "HEAD", + "v1.0/state".split("/"), + null, + (String) null, + null, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals("HEAD", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state", request.uri().toString()); + assertEquals(HTTP_OK, response.getStatusCode()); + } + + @Test + public void invokeGetMethod() throws IOException { + byte[] content = serializer.serialize(EXPECTED_RESULT); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "GET", + "v1.0/state".split("/"), + null, + null, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals(EXPECTED_RESULT, body); + assertEquals("GET", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state", request.uri().toString()); + } + + @Test + public void invokeMethodWithHeaders() throws IOException { + Map headers = Map.of( + "header", "value", + "header1", "value1" + ); + Map> urlParameters = Map.of( + "orderId", List.of("41") + ); + byte[] content = serializer.serialize(EXPECTED_RESULT); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_OK); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(HttpRequest.class); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "GET", + "v1.0/state/order".split("/"), + urlParameters, + headers, + Context.empty() + ); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + + verify(httpClient).sendAsync(requestCaptor.capture(), any()); + + HttpRequest request = requestCaptor.getValue(); + + assertEquals(EXPECTED_RESULT, body); + assertEquals("GET", request.method()); + assertEquals("http://" + sidecarIp + ":3500/v1.0/state/order?orderId=41", request.uri().toString()); + assertEquals("value", request.headers().firstValue("header").get()); + assertEquals("value1", request.headers().firstValue("header1").get()); + } + + @Test + public void invokePostMethodRuntime() { + MockHttpResponse mockHttpResponse = new MockHttpResponse(HTTP_SERVER_ERROR); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/state".split("/"), + null, + null, + Context.empty()); + + StepVerifier.create(mono).expectError(RuntimeException.class).verify(); + } + + @Test + public void invokePostDaprError() { + byte[] content = "{\"errorCode\":null,\"message\":null}".getBytes(); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_SERVER_ERROR); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/state".split("/"), + null, + null, + Context.empty() + ); + + StepVerifier.create(mono).expectError(RuntimeException.class).verify(); + } + + @Test + public void invokePostMethodUnknownError() { + byte[] content = "{\"errorCode\":null,\"message\":null}".getBytes(); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_SERVER_ERROR); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(sidecarIp, 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/state".split("/"), + null, + null, + Context.empty() + ); + + StepVerifier.create(mono).expectError(RuntimeException.class).verify(); + } + + @Test + public void validateExceptionParsing() { + String payload = "{" + + "\"errorCode\":\"ERR_PUBSUB_NOT_FOUND\"," + + "\"message\":\"pubsub abc is not found\"," + + "\"details\":[" + + "{" + + "\"@type\":\"type.googleapis.com/google.rpc.ErrorInfo\"," + + "\"domain\":\"dapr.io\"," + + "\"metadata\":{}," + + "\"reason\":\"DAPR_PUBSUB_NOT_FOUND\"" + + "}]}"; + byte[] content = payload.getBytes(); + MockHttpResponse mockHttpResponse = new MockHttpResponse(content, HTTP_SERVER_ERROR); + CompletableFuture> mockResponse = CompletableFuture.completedFuture(mockHttpResponse); + + when(httpClient.sendAsync(any(), any())).thenReturn(mockResponse); + + DaprHttp daprHttp = new DaprHttp(Properties.SIDECAR_IP.get(), 3500, daprTokenApi, httpClient); + Mono mono = daprHttp.invokeApi( + "POST", + "v1.0/pubsub/publish".split("/"), + null, + null, + Context.empty() + ); + + StepVerifier.create(mono).expectErrorMatches(e -> { + assertEquals(DaprException.class, e.getClass()); + + DaprException daprException = (DaprException)e; + assertEquals("ERR_PUBSUB_NOT_FOUND", daprException.getErrorCode()); + assertEquals("DAPR_PUBSUB_NOT_FOUND", + daprException.getErrorDetails() + .get(DaprErrorDetails.ErrorDetailType.ERROR_INFO, "reason", TypeRef.STRING)); + return true; + }).verify(); + } } diff --git a/sdk/src/test/java/io/dapr/client/MockHttpResponse.java b/sdk/src/test/java/io/dapr/client/MockHttpResponse.java index b9b19fdcc..de818b813 100644 --- a/sdk/src/test/java/io/dapr/client/MockHttpResponse.java +++ b/sdk/src/test/java/io/dapr/client/MockHttpResponse.java @@ -16,6 +16,11 @@ public class MockHttpResponse implements HttpResponse { private final byte[] body; private final int statusCode; + public MockHttpResponse(int statusCode) { + this.body = null; + this.statusCode = statusCode; + } + public MockHttpResponse(byte[] body, int statusCode) { this.body = body; this.statusCode = statusCode;