diff --git a/aws-lambda-java-runtime-interface-client/pom.xml b/aws-lambda-java-runtime-interface-client/pom.xml index 69692378..df42879b 100644 --- a/aws-lambda-java-runtime-interface-client/pom.xml +++ b/aws-lambda-java-runtime-interface-client/pom.xml @@ -87,6 +87,12 @@ 4.11.0 test + + com.squareup.okhttp3 + mockwebserver + 4.12.0 + test + diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImplTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImplTest.java new file mode 100644 index 00000000..1b6f3136 --- /dev/null +++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImplTest.java @@ -0,0 +1,382 @@ +/* +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0 +*/ + +package com.amazonaws.services.lambda.runtime.api.client.runtimeapi; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.ErrorRequest; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.StackElement; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayErrorCause; +import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.dto.XRayException; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.fail; +import okhttp3.HttpUrl; +import static java.net.HttpURLConnection.HTTP_ACCEPTED; +import static java.net.HttpURLConnection.HTTP_OK; +import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; +import okhttp3.mockwebserver.MockWebServer; + +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.RecordedRequest; + +@DisabledOnOs(OS.MAC) +public class LambdaRuntimeApiClientImplTest { + + MockWebServer mockWebServer; + LambdaRuntimeApiClientImpl lambdaRuntimeApiClientImpl; + + String[] errorStackStrace = { "item0", "item1", "item2" }; + ErrorRequest errorRequest = new ErrorRequest("testErrorMessage", "testErrorType", errorStackStrace); + + String requestId = "1234"; + + @BeforeEach + void setUp() { + mockWebServer = new MockWebServer(); + String hostnamePort = getHostnamePort(); + lambdaRuntimeApiClientImpl = new LambdaRuntimeApiClientImpl(hostnamePort); + } + + @Test + public void reportInitErrorTest() { + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_ACCEPTED); + mockWebServer.enqueue(mockResponse); + + LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType); + lambdaRuntimeApiClientImpl.reportInitError(lambdaError); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + HttpUrl actualUrl = recordedRequest.getRequestUrl(); + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/init/error"; + assertEquals(expectedUrl, actualUrl.toString()); + + String userAgentHeader = recordedRequest.getHeader("User-Agent"); + assertTrue(userAgentHeader.startsWith("aws-lambda-java/")); + + String lambdaRuntimeErrorTypeHeader = recordedRequest.getHeader("Lambda-Runtime-Function-Error-Type"); + assertEquals("Runtime.AfterRestoreError", lambdaRuntimeErrorTypeHeader); + + String actualBody = recordedRequest.getBody().readUtf8(); + assertEquals("{\"errorMessage\":\"testErrorMessage\",\"errorType\":\"testErrorType\",\"stackTrace\":[\"item0\",\"item1\",\"item2\"]}", actualBody); + } catch(Exception e) { + fail(); + } + } + + @Test + public void reportInitErrorTestWrongStatusCode() { + int errorStatusCode = HTTP_INTERNAL_ERROR; + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(errorStatusCode); + mockWebServer.enqueue(mockResponse); + + LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType); + lambdaRuntimeApiClientImpl.reportInitError(lambdaError); + fail(); + } catch(LambdaRuntimeClientException e) { + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/init/error"; + String expectedMessage = expectedUrl + " Response code: '" + errorStatusCode + "'."; + assertEquals(expectedMessage, e.getLocalizedMessage()); + } catch(Exception e) { + fail(); + } + } + + @Test + public void reportRestoreErrorTest() { + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_ACCEPTED); + mockWebServer.enqueue(mockResponse); + + LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType); + lambdaRuntimeApiClientImpl.reportRestoreError(lambdaError); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + HttpUrl actualUrl = recordedRequest.getRequestUrl(); + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/restore/error"; + assertEquals(expectedUrl, actualUrl.toString()); + + String userAgentHeader = recordedRequest.getHeader("User-Agent"); + assertTrue(userAgentHeader.startsWith("aws-lambda-java/")); + + String lambdaRuntimeErrorTypeHeader = recordedRequest.getHeader("Lambda-Runtime-Function-Error-Type"); + assertEquals("Runtime.AfterRestoreError", lambdaRuntimeErrorTypeHeader); + + String actualBody = recordedRequest.getBody().readUtf8(); + assertEquals("{\"errorMessage\":\"testErrorMessage\",\"errorType\":\"testErrorType\",\"stackTrace\":[\"item0\",\"item1\",\"item2\"]}", actualBody); + } catch(Exception e) { + fail(); + } + } + + @Test + public void reportRestoreErrorTestWrongStatusCode() { + int errorStatusCode = HTTP_INTERNAL_ERROR; + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(errorStatusCode); + mockWebServer.enqueue(mockResponse); + + LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType); + lambdaRuntimeApiClientImpl.reportRestoreError(lambdaError); + fail(); + } catch(LambdaRuntimeClientException e) { + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/restore/error"; + String expectedMessage = expectedUrl + " Response code: '" + errorStatusCode + "'."; + assertEquals(expectedMessage, e.getLocalizedMessage()); + } catch(Exception e) { + fail(); + } + } + + @Test + public void reportInvocationErrorTest() { + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_ACCEPTED); + mockWebServer.enqueue(mockResponse); + + LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType); + lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + HttpUrl actualUrl = recordedRequest.getRequestUrl(); + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/invocation/1234/error"; + assertEquals(expectedUrl, actualUrl.toString()); + + String userAgentHeader = recordedRequest.getHeader("User-Agent"); + assertTrue(userAgentHeader.startsWith("aws-lambda-java/")); + + String lambdaRuntimeErrorTypeHeader = recordedRequest.getHeader("Lambda-Runtime-Function-Error-Type"); + assertEquals("Runtime.AfterRestoreError", lambdaRuntimeErrorTypeHeader); + + String actualBody = recordedRequest.getBody().readUtf8(); + assertEquals("{\"errorMessage\":\"testErrorMessage\",\"errorType\":\"testErrorType\",\"stackTrace\":[\"item0\",\"item1\",\"item2\"]}", actualBody); + } catch(Exception e) { + fail(); + } + } + + @Test + public void reportInvocationErrorTestWrongStatusCode() { + int errorStatusCode = HTTP_INTERNAL_ERROR; + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(errorStatusCode); + mockWebServer.enqueue(mockResponse); + + LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType); + lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError); + fail(); + } catch(LambdaRuntimeClientException e) { + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/invocation/1234/error"; + String expectedMessage = expectedUrl + " Response code: '" + errorStatusCode + "'."; + assertEquals(expectedMessage, e.getLocalizedMessage()); + } catch(Exception e) { + fail(); + } + } + + @Test + public void reportLambdaErrorWithXRayTest() { + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_ACCEPTED); + mockWebServer.enqueue(mockResponse); + + String workingDirectory = "my-test-directory"; + List paths = new ArrayList(); + paths.add("path-0"); + paths.add("path-1"); + paths.add("path-2"); + + List stackElements0 = new ArrayList<>(); + stackElements0.add(new StackElement("label0", "path0", 0)); + stackElements0.add(new StackElement("label1", "path1", 1)); + stackElements0.add(new StackElement("label2", "path2", 2)); + XRayException xRayException0 = new XRayException("my-test-message0", "my-test-type0", stackElements0); + + List stackElements1 = new ArrayList<>(); + stackElements1.add(new StackElement("label10", "path10", 0)); + stackElements1.add(new StackElement("label11", "path11", 11)); + stackElements1.add(new StackElement("label12", "path12", 12)); + XRayException xRayException1 = new XRayException("my-test-message1", "my-test-type0", stackElements1); + + List exceptions = new ArrayList<>(); + exceptions.add(xRayException0); + exceptions.add(xRayException1); + + XRayErrorCause xRayErrorCause = new XRayErrorCause(workingDirectory, exceptions, paths); + LambdaError lambdaError = new LambdaError(errorRequest, xRayErrorCause, rapidErrorType); + lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + String xrayErrorCauseHeader = recordedRequest.getHeader("Lambda-Runtime-Function-XRay-Error-Cause"); + assertEquals("{\"working_directory\":\"my-test-directory\",\"exceptions\":[{\"message\":\"my-test-message0\",\"type\":\"my-test-type0\",\"stack\":[{\"label\":\"label0\"," + + "\"path\":\"path0\",\"line\":0},{\"label\":\"label1\",\"path\":\"path1\",\"line\":1},{\"label\":\"label2\",\"path\":\"path2\",\"line\":2}]},{\"message\":\"my-test-message1\"," + + "\"type\":\"my-test-type0\",\"stack\":[{\"label\":\"label10\",\"path\":\"path10\",\"line\":0},{\"label\":\"label11\",\"path\":\"path11\",\"line\":11},{\"label\":\"label12\"," + + "\"path\":\"path12\",\"line\":12}]}],\"paths\":[\"path-0\",\"path-1\",\"path-2\"]}", xrayErrorCauseHeader); + } catch(Exception e) { + fail(); + } + } + + @Test + public void reportInvocationSuccessTest() { + try { + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_ACCEPTED); + mockWebServer.enqueue(mockResponse); + + String response = "{\"msg\":\"test\"}"; + lambdaRuntimeApiClientImpl.reportInvocationSuccess(requestId, response.getBytes()); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + HttpUrl actualUrl = recordedRequest.getRequestUrl(); + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/invocation/1234/response"; + assertEquals(expectedUrl, actualUrl.toString()); + + String actualBody = recordedRequest.getBody().readUtf8(); + assertEquals("{\"msg\":\"test\"}", actualBody); + } catch(Exception e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void restoreNextTest() { + try { + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_OK); + mockWebServer.enqueue(mockResponse); + + lambdaRuntimeApiClientImpl.restoreNext(); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + HttpUrl actualUrl = recordedRequest.getRequestUrl(); + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/restore/next"; + assertEquals(expectedUrl, actualUrl.toString()); + + String actualBody = recordedRequest.getBody().readUtf8(); + assertEquals("", actualBody); + } catch(Exception e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void restoreNextWrongStatusCodeTest() { + int errorStatusCode = HTTP_INTERNAL_ERROR; + try { + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(errorStatusCode); + mockWebServer.enqueue(mockResponse); + + lambdaRuntimeApiClientImpl.restoreNext(); + fail(); + } catch(LambdaRuntimeClientException e) { + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/restore/next"; + String expectedMessage = expectedUrl + " Response code: '" + errorStatusCode + "'."; + assertEquals(expectedMessage, e.getLocalizedMessage()); + } catch(Exception e) { + fail(); + } + } + + @Test + public void nextTest() { + try { + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_ACCEPTED); + mockResponse.setHeader("lambda-runtime-aws-request-id", "1234567890"); + mockResponse.setHeader("Content-Type", "application/json"); + mockWebServer.enqueue(mockResponse); + + lambdaRuntimeApiClientImpl.nextInvocation(); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + HttpUrl actualUrl = recordedRequest.getRequestUrl(); + String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/invocation/next"; + assertEquals(expectedUrl, actualUrl.toString()); + + String actualBody = recordedRequest.getBody().readUtf8(); + assertEquals("", actualBody); + } catch(Exception e) { + fail(); + } + } + + + @Test + public void createUrlMalformedTest() { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType); + RuntimeException thrown = assertThrows(RuntimeException.class, ()->{ + lambdaRuntimeApiClientImpl.reportLambdaError("invalidurl", lambdaError, 100); + }); + assertTrue(thrown.getLocalizedMessage().contains("java.net.MalformedURLException")); + } + + @Test + public void lambdaReportErrorXRayHeaderTooLongTest() { + try { + RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError; + + MockResponse mockResponse = new MockResponse(); + mockResponse.setResponseCode(HTTP_ACCEPTED); + mockWebServer.enqueue(mockResponse); + + String workingDirectory = "my-test-directory"; + List paths = new ArrayList(); + paths.add("path-0"); + + List stackElements = new ArrayList<>(); + stackElements.add(new StackElement("label0", "path0", 0)); + XRayException xRayException = new XRayException("my-test-message0", "my-test-type0", stackElements); + + List exceptions = new ArrayList<>(); + exceptions.add(xRayException); + + XRayErrorCause xRayErrorCause = new XRayErrorCause(workingDirectory, exceptions, paths); + LambdaError lambdaError = new LambdaError(errorRequest, xRayErrorCause, rapidErrorType); + lambdaRuntimeApiClientImpl.reportLambdaError("http://" + getHostnamePort(), lambdaError, 10); + RecordedRequest recordedRequest = mockWebServer.takeRequest(); + + String xrayErrorCauseHeader = recordedRequest.getHeader("Lambda-Runtime-Function-XRay-Error-Cause"); + assertNull(xrayErrorCauseHeader); + } catch(Exception e) { + fail(); + } + } + + private String getHostnamePort() { + return mockWebServer.getHostName() + ":" + mockWebServer.getPort(); + } +} \ No newline at end of file