From b9fac11c901b2945fc6375cc028e419ed9998c4d Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Tue, 8 Feb 2022 15:27:20 +0900 Subject: [PATCH] Migrate AWS Lambda tests to Java (#5315) * Migrate AWS Lambda tests to Java * Only assert OTLP fields for links --- dependencyManagement/build.gradle.kts | 1 + .../v1_0/AwsLambdaSqsHandlerTest.groovy | 27 -- .../awslambda/v1_0/AwsLambdaTest.groovy | 31 -- .../v1_0/AwsLambdaSqsEventHandlerTest.java | 37 +++ .../awslambda/v1_0/AwsLambdaTest.java | 45 +++ .../v1_0/AwsLambdaSqsHandlerTest.groovy | 31 -- .../AwsLambdaSqsMessageHandlerTest.groovy | 102 ------ .../awslambda/v1_0/AwsLambdaTest.groovy | 35 -- ...TracingRequestApiGatewayWrapperTest.groovy | 232 ------------- ...RequestStreamWrapperPropagationTest.groovy | 135 -------- .../TracingRequestStreamWrapperTest.groovy | 119 ------- .../v1_0/TracingRequestWrapperTest.groovy | 160 --------- .../v1_0/TracingRequestWrapperTestBase.groovy | 38 --- .../v1_0/TracingSqsEventWrapperTest.groovy | 85 ----- .../v1_0/AwsLambdaApiGatewayWrapperTest.java | 308 ++++++++++++++++++ .../v1_0/AwsLambdaSqsEventHandlerTest.java | 40 +++ .../v1_0/AwsLambdaSqsEventWrapperTest.java | 112 +++++++ .../v1_0/AwsLambdaSqsMessageHandlerTest.java | 168 ++++++++++ ...ambdaStreamWrapperHttpPropagationTest.java | 162 +++++++++ .../v1_0/AwsLambdaStreamWrapperTest.java | 143 ++++++++ .../awslambda/v1_0/AwsLambdaTest.java | 41 +++ .../awslambda/v1_0/AwsLambdaWrapperTest.java | 219 +++++++++++++ .../aws-lambda-1.0/testing/build.gradle.kts | 3 + ...AbstractAwsLambdaRequestHandlerTest.groovy | 109 ------- .../AbstractAwsLambdaSqsHandlerTest.groovy | 112 ------- .../AbstractAwsLambdaSqsEventHandlerTest.java | 167 ++++++++++ .../awslambda/v1_0/AbstractAwsLambdaTest.java | 120 +++++++ .../junit/InstrumentationExtension.java | 16 + 28 files changed, 1582 insertions(+), 1216 deletions(-) delete mode 100644 instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy create mode 100644 instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java create mode 100644 instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.java delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestApiGatewayWrapperTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperPropagationTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTestBase.groovy delete mode 100644 instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventWrapperTest.groovy create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaApiGatewayWrapperTest.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventWrapperTest.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperHttpPropagationTest.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperTest.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaWrapperTest.java delete mode 100644 instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaRequestHandlerTest.groovy delete mode 100644 instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsHandlerTest.groovy create mode 100644 instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsEventHandlerTest.java create mode 100644 instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaTest.java diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index 72de2ef0f0..e1ec8df78f 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -110,6 +110,7 @@ val DEPENDENCIES = listOf( "com.google.code.findbugs:annotations:3.0.1u2", "com.google.code.findbugs:jsr305:3.0.2", "org.codehaus.groovy:groovy-all:${groovyVersion}", + "org.junit-pioneer:junit-pioneer:1.5.0", "org.objenesis:objenesis:3.2", "org.spockframework:spock-core:2.0-groovy-3.0", "org.spockframework:spock-junit4:2.0-groovy-3.0", diff --git a/instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy b/instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy deleted file mode 100644 index cbf6e443b5..0000000000 --- a/instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import com.amazonaws.services.lambda.runtime.events.SQSEvent -import io.opentelemetry.instrumentation.awslambda.v1_0.AbstractAwsLambdaSqsHandlerTest -import io.opentelemetry.instrumentation.test.AgentTestTrait - -class AwsLambdaSqsHandlerTest extends AbstractAwsLambdaSqsHandlerTest implements AgentTestTrait { - - static class TestRequestHandler implements RequestHandler { - @Override - Void handleRequest(SQSEvent input, Context context) { - return null - } - } - - @Override - RequestHandler handler() { - return new TestRequestHandler() - } -} diff --git a/instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy b/instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy deleted file mode 100644 index 6fc58385c8..0000000000 --- a/instrumentation/aws-lambda-1.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import io.opentelemetry.instrumentation.awslambda.v1_0.AbstractAwsLambdaRequestHandlerTest -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.javaagent.testing.common.AgentTestingExporterAccess - -class AwsLambdaTest extends AbstractAwsLambdaRequestHandlerTest implements AgentTestTrait { - - def cleanup() { - assert AgentTestingExporterAccess.forceFlushCalled() - } - - static class TestRequestHandler implements RequestHandler { - @Override - String handleRequest(String input, Context context) { - return doHandleRequest(input, context) - } - } - - @Override - RequestHandler handler() { - return new TestRequestHandler() - } -} diff --git a/instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java new file mode 100644 index 0000000000..649b246fb3 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.awslambda.v1_0; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.instrumentation.awslambda.v1_0.AbstractAwsLambdaSqsEventHandlerTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest { + + @RegisterExtension + public static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected RequestHandler handler() { + return new TestRequestHandler(); + } + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + private static final class TestRequestHandler implements RequestHandler { + @Override + public Void handleRequest(SQSEvent input, Context context) { + return null; + } + } +} diff --git a/instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.java b/instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.java new file mode 100644 index 0000000000..cf9d30e7a3 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaTest.java @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.awslambda.v1_0; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import io.opentelemetry.instrumentation.awslambda.v1_0.AbstractAwsLambdaTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class AwsLambdaTest extends AbstractAwsLambdaTest { + + @RegisterExtension + public static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected RequestHandler handler() { + return new TestRequestHandler(); + } + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + private static final class TestRequestHandler implements RequestHandler { + + @Override + public String handleRequest(String input, Context context) { + return doHandleRequest(input, context); + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy deleted file mode 100644 index 8caa2991ea..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsHandlerTest.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import com.amazonaws.services.lambda.runtime.events.SQSEvent -import io.opentelemetry.instrumentation.test.LibraryTestTrait -import io.opentelemetry.sdk.OpenTelemetrySdk - -class AwsLambdaSqsHandlerTest extends AbstractAwsLambdaSqsHandlerTest implements LibraryTestTrait { - - static class TestHandler extends TracingSqsEventHandler { - - TestHandler(OpenTelemetrySdk openTelemetrySdk) { - super(openTelemetrySdk) - } - - @Override - protected void handleEvent(SQSEvent event, Context context) { - } - } - - @Override - RequestHandler handler() { - return new TestHandler(testRunner().openTelemetrySdk) - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.groovy deleted file mode 100644 index 43d2440b4f..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.groovy +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.events.SQSEvent -import io.opentelemetry.instrumentation.test.LibraryInstrumentationSpecification -import io.opentelemetry.sdk.OpenTelemetrySdk -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER -import static io.opentelemetry.api.trace.SpanKind.SERVER - -class AwsLambdaSqsMessageHandlerTest extends LibraryInstrumentationSpecification { - - private static final String AWS_TRACE_HEADER1 = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1" - private static final String AWS_TRACE_HEADER2 = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad9;Sampled=1" - - static class TestHandler extends TracingSqsMessageHandler { - - TestHandler(OpenTelemetrySdk openTelemetrySdk) { - super(openTelemetrySdk) - } - - @Override - protected void handleMessage(SQSEvent.SQSMessage event, Context context) { - } - } - - def "messages with process spans"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - - def message1 = new SQSEvent.SQSMessage() - message1.setAttributes(["AWSTraceHeader": AWS_TRACE_HEADER1]) - message1.setMessageId("message1") - message1.setEventSource("queue1") - - def message2 = new SQSEvent.SQSMessage() - message2.setAttributes(["AWSTraceHeader": AWS_TRACE_HEADER2]) - message2.setMessageId("message2") - message2.setEventSource("queue1") - - def event = new SQSEvent() - event.setRecords([message1, message2]) - - new TestHandler(testRunner().openTelemetrySdk).handleRequest(event, context) - - then: - assertTraces(1) { - trace(0, 4) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - span(1) { - name("queue1 process") - kind CONSUMER - parentSpanId(span(0).spanId) - attributes { - "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" - "$SemanticAttributes.MESSAGING_OPERATION" "process" - } - hasLink("5759e988bd862e3fe1be46a994272793", "53995c3f42cd8ad8") - hasLink("5759e988bd862e3fe1be46a994272793", "53995c3f42cd8ad9") - } - span(2) { - name("queue1 process") - kind CONSUMER - parentSpanId(span(1).spanId) - attributes { - "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" - "$SemanticAttributes.MESSAGING_OPERATION" "process" - "$SemanticAttributes.MESSAGING_MESSAGE_ID" "message1" - "$SemanticAttributes.MESSAGING_DESTINATION" "queue1" - } - hasLink("5759e988bd862e3fe1be46a994272793", "53995c3f42cd8ad8") - } - span(3) { - name("queue1 process") - kind CONSUMER - parentSpanId(span(1).spanId) - attributes { - "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" - "$SemanticAttributes.MESSAGING_OPERATION" "process" - "$SemanticAttributes.MESSAGING_MESSAGE_ID" "message2" - "$SemanticAttributes.MESSAGING_DESTINATION" "queue1" - } - hasLink("5759e988bd862e3fe1be46a994272793", "53995c3f42cd8ad9") - } - } - } - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy deleted file mode 100644 index 6ff5755951..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.groovy +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import io.opentelemetry.instrumentation.test.LibraryTestTrait -import io.opentelemetry.sdk.OpenTelemetrySdk - -class AwsLambdaTest extends AbstractAwsLambdaRequestHandlerTest implements LibraryTestTrait { - - def cleanup() { - assert forceFlushCalled() - } - - static class TestRequestHandler extends TracingRequestHandler { - - TestRequestHandler(OpenTelemetrySdk openTelemetrySdk) { - super(openTelemetrySdk) - } - - @Override - protected String doHandleRequest(String input, Context context) { - return AbstractAwsLambdaRequestHandlerTest.doHandleRequest(input, context) - } - } - - @Override - RequestHandler handler() { - return new TestRequestHandler(testRunner().openTelemetrySdk) - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestApiGatewayWrapperTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestApiGatewayWrapperTest.groovy deleted file mode 100644 index 3bf5f17634..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestApiGatewayWrapperTest.groovy +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent -import com.google.common.collect.ImmutableMap -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes - -import static io.opentelemetry.api.trace.SpanKind.SERVER - -class TracingRequestApiGatewayWrapperTest extends TracingRequestWrapperTestBase { - - static class TestApiGatewayEventHandler implements RequestHandler { - - @Override - APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) { - if (input.getBody() == "hello") { - return new APIGatewayProxyResponseEvent() - .withStatusCode(200) - .withBody("world") - } else if (input.getBody() == "empty") { - return new APIGatewayProxyResponseEvent() - } - throw new IllegalStateException("bad request") - } - } - - static class TestApiGatewayStringHandler implements RequestHandler { - - @Override - String handleRequest(String input, Context context) { - if (input == "hello") { - return "world" - } - throw new IllegalStateException("bad request") - } - } - - static class TestApiGatewayIntegerHandler implements RequestHandler { - - @Override - String handleRequest(Integer input, Context context) { - if (input == 1) { - return "world" - } - throw new IllegalStateException("bad request") - } - } - - static class CustomType { - String key, value - } - - static class TestApiGatewayCustomTypeHandler implements RequestHandler { - - @Override - String handleRequest(CustomType input, Context context) { - if (input.key == "hello") { - return "Hello " + input.value - } - throw new IllegalStateException("bad request") - } - } - - def propagationHeaders() { - return Collections.singletonMap("traceparent", "00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01") - } - - def "event handler traced with trace propagation"() { - given: - setLambda(TestApiGatewayEventHandler.getName() + "::handleRequest", TracingRequestApiGatewayWrapper.metaClass.&invokeConstructor, TracingRequestApiGatewayWrapper.&map) - - def headers = ImmutableMap.builder() - .putAll(propagationHeaders()) - .put("User-Agent", "Test Client") - .put("host", "localhost:123") - .put("X-FORWARDED-PROTO", "http") - .build() - def input = new APIGatewayProxyRequestEvent() - .withHttpMethod("GET") - .withResource("/hello/{param}") - .withPath("/hello/world") - .withBody("hello") - .withQueryStringParamters(["a": "b", "c": "d"]) - .withHeaders(headers) - - when: - APIGatewayProxyResponseEvent result = wrapper.handleRequest(input, context) - - then: - result.getBody() == "world" - assertTraces(1) { - trace(0, 1) { - span(0) { - parentSpanId("0000000000000456") - traceId("4fd0b6131f19f39af59518d127b0cafe") - name("/hello/{param}") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - "$SemanticAttributes.FAAS_TRIGGER" "http" - "$SemanticAttributes.HTTP_METHOD" "GET" - "$SemanticAttributes.HTTP_USER_AGENT" "Test Client" - "$SemanticAttributes.HTTP_URL" "http://localhost:123/hello/world?a=b&c=d" - "$SemanticAttributes.HTTP_STATUS_CODE" 200 - } - } - } - } - } - - def "event handler test empty request & response"() { - given: - setLambda(TestApiGatewayEventHandler.getName() + "::handleRequest", TracingRequestApiGatewayWrapper.metaClass.&invokeConstructor, TracingRequestApiGatewayWrapper.&map) - - def input = new APIGatewayProxyRequestEvent() - .withBody("empty") - - when: - APIGatewayProxyResponseEvent result = wrapper.handleRequest(input, context) - - then: - result.body == null - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - "$SemanticAttributes.FAAS_TRIGGER" "http" - } - } - } - } - } - - def "string handler test request"() { - given: - setLambda(TestApiGatewayStringHandler.getName() + "::handleRequest", TracingRequestApiGatewayWrapper.metaClass.&invokeConstructor, TracingRequestApiGatewayWrapper.&map) - - def input = new APIGatewayProxyRequestEvent() - .withBody("\"hello\"") - - when: - APIGatewayProxyResponseEvent result = wrapper.handleRequest(input, context) - - then: - result.body == "\"world\"" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - "$SemanticAttributes.FAAS_TRIGGER" "http" - } - } - } - } - } - - def "integer handler test request"() { - given: - setLambda(TestApiGatewayIntegerHandler.getName() + "::handleRequest", TracingRequestApiGatewayWrapper.metaClass.&invokeConstructor, TracingRequestApiGatewayWrapper.&map) - - def input = new APIGatewayProxyRequestEvent() - .withBody("1") - - when: - APIGatewayProxyResponseEvent result = wrapper.handleRequest(input, context) - - then: - result.body == "\"world\"" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - "$SemanticAttributes.FAAS_TRIGGER" "http" - } - } - } - } - } - - def "custom type handler test request"() { - given: - setLambda(TestApiGatewayCustomTypeHandler.getName() + "::handleRequest", TracingRequestApiGatewayWrapper.metaClass.&invokeConstructor, TracingRequestApiGatewayWrapper.&map) - - def input = new APIGatewayProxyRequestEvent() - .withBody("{\"key\":\"hello\", \"value\":\"General Kenobi\"}") - - when: - APIGatewayProxyResponseEvent result = wrapper.handleRequest(input, context) - - then: - result.body == "\"Hello General Kenobi\"" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - "$SemanticAttributes.FAAS_TRIGGER" "http" - } - } - } - } - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperPropagationTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperPropagationTest.groovy deleted file mode 100644 index 4881f11d9e..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperPropagationTest.groovy +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestStreamHandler -import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.ObjectMapper -import io.opentelemetry.instrumentation.test.LibraryInstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import org.junit.Rule -import org.junit.contrib.java.lang.system.EnvironmentVariables -import spock.lang.Shared - -import java.nio.charset.Charset - -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -class TracingRequestStreamWrapperPropagationTest extends LibraryInstrumentationSpecification { - - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper() - - @Rule - public final EnvironmentVariables environmentVariables = new EnvironmentVariables() - - static class TestRequestHandler implements RequestStreamHandler { - - @Override - void handleRequest(InputStream input, OutputStream output, Context context) { - - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output)) - - JsonNode root = OBJECT_MAPPER.readTree(input) - String body = root.get("body").asText() - if (body == "hello") { - writer.write("world") - writer.flush() - writer.close() - } else { - throw new IllegalArgumentException("bad argument") - } - } - } - - @Shared - TracingRequestStreamWrapper wrapper - - def setup() { - environmentVariables.set(WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, "io.opentelemetry.instrumentation.awslambda.v1_0.TracingRequestStreamWrapperPropagationTest\$TestRequestHandler::handleRequest") - wrapper = new TracingRequestStreamWrapper(testRunner().openTelemetrySdk, WrappedLambda.fromConfiguration()) - } - - def cleanup() { - environmentVariables.clear(WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY) - } - - def "handler traced with trace propagation"() { - when: - String content = - "{" + - "\"headers\" : {" + - "\"traceparent\": \"00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01\"" + - "}," + - "\"body\" : \"hello\"" + - "}" - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - def input = new ByteArrayInputStream(content.getBytes(Charset.defaultCharset())) - def output = new ByteArrayOutputStream() - - wrapper.handleRequest(input, output, context) - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - parentSpanId("0000000000000456") - traceId("4fd0b6131f19f39af59518d127b0cafe") - name("my_function") - kind SERVER - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - - def "handler traced with exception and trace propagation"() { - when: - String content = - "{" + - "\"headers\" : {" + - "\"traceparent\": \"00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01\"" + - "}," + - "\"body\" : \"bye\"" + - "}" - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - def input = new ByteArrayInputStream(content.getBytes(Charset.defaultCharset())) - def output = new ByteArrayOutputStream() - - def thrown - try { - wrapper.handleRequest(input, output, context) - } catch (Throwable t) { - thrown = t - } - - then: - thrown != null - assertTraces(1) { - trace(0, 1) { - span(0) { - parentSpanId("0000000000000456") - traceId("4fd0b6131f19f39af59518d127b0cafe") - name("my_function") - kind SERVER - status ERROR - errorEvent(IllegalArgumentException, "bad argument") - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperTest.groovy deleted file mode 100644 index 632e6b8c34..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamWrapperTest.groovy +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestStreamHandler -import io.opentelemetry.instrumentation.test.LibraryInstrumentationSpecification -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import org.junit.Rule -import org.junit.contrib.java.lang.system.EnvironmentVariables -import spock.lang.Shared - -import java.nio.charset.Charset - -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -class TracingRequestStreamWrapperTest extends LibraryInstrumentationSpecification { - - @Rule - public final EnvironmentVariables environmentVariables = new EnvironmentVariables() - - static class TestRequestHandler implements RequestStreamHandler { - - @Override - void handleRequest(InputStream input, OutputStream output, Context context) { - - BufferedReader reader = new BufferedReader(new InputStreamReader(input)) - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output)) - String line = reader.readLine() - if (line == "hello") { - writer.write("world") - writer.flush() - writer.close() - } else { - throw new IllegalArgumentException("bad argument") - } - } - } - - @Shared - TracingRequestStreamWrapper wrapper - - def setup() { - environmentVariables.set(WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, "io.opentelemetry.instrumentation.awslambda.v1_0.TracingRequestStreamWrapperTest\$TestRequestHandler::handleRequest") - wrapper = new TracingRequestStreamWrapper(testRunner().openTelemetrySdk, WrappedLambda.fromConfiguration()) - } - - def cleanup() { - environmentVariables.clear(WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY) - } - - def "handler traced"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - context.getInvokedFunctionArn() >> "arn:aws:lambda:us-east-1:123456789:function:test" - def input = new ByteArrayInputStream("hello\n".getBytes(Charset.defaultCharset())) - def output = new ByteArrayOutputStream() - - wrapper.handleRequest(input, output, context) - - then: - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - - def "handler traced with exception"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - context.getInvokedFunctionArn() >> "arn:aws:lambda:us-east-1:123456789:function:test" - def input = new ByteArrayInputStream("bye".getBytes(Charset.defaultCharset())) - def output = new ByteArrayOutputStream() - - def thrown - try { - wrapper.handleRequest(input, output, context) - } catch (Throwable t) { - thrown = t - } - - then: - thrown != null - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - status ERROR - errorEvent(IllegalArgumentException, "bad argument") - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTest.groovy deleted file mode 100644 index ecd4d9aeec..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTest.groovy +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes - -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -class TracingRequestWrapperTest extends TracingRequestWrapperTestBase { - - static class TestRequestHandlerString implements RequestHandler { - - @Override - String handleRequest(String input, Context context) { - if (input == "hello") { - return "world" - } - throw new IllegalArgumentException("bad argument") - } - } - - static class TestRequestHandlerInteger implements RequestHandler { - - @Override - String handleRequest(Integer input, Context context) { - if (input == 1) { - return "world" - } - throw new IllegalArgumentException("bad argument") - } - } - - static class CustomType { - String key, value - } - - static class TestRequestHandlerCustomType implements RequestHandler { - - @Override - String handleRequest(CustomType input, Context context) { - if (input.key == "hello there") { - return input.value - } - throw new IllegalArgumentException("bad argument") - } - } - - def "handler string traced"() { - given: - setLambda(TestRequestHandlerString.getName() + "::handleRequest", TracingRequestWrapper.metaClass.&invokeConstructor, TracingRequestWrapper.&map) - - when: - def result = wrapper.handleRequest("hello", context) - - then: - result == "world" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - - def "handler with exception"() { - given: - setLambda(TestRequestHandlerString.getName() + "::handleRequest", TracingRequestWrapper.metaClass.&invokeConstructor, TracingRequestWrapper.&map) - - when: - def thrown - try { - wrapper.handleRequest("goodbye", context) - } catch (Throwable t) { - thrown = t - } - - then: - thrown != null - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - status ERROR - errorEvent(IllegalArgumentException, "bad argument") - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - - def "handler integer traced"() { - given: - setLambda(TestRequestHandlerInteger.getName() + "::handleRequest", TracingRequestWrapper.metaClass.&invokeConstructor, TracingRequestWrapper.&map) - - when: - def result = wrapper.handleRequest(1, context) - - then: - result == "world" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - - def "handler custom type traced"() { - given: - setLambda(TestRequestHandlerCustomType.getName() + "::handleRequest", TracingRequestWrapper.metaClass.&invokeConstructor, TracingRequestWrapper.&map) - - when: - CustomType ct = new CustomType() - ct.key = "hello there" - ct.value = "General Kenobi" - def result = wrapper.handleRequest(ct, context) - - then: - result == "General Kenobi" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTestBase.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTestBase.groovy deleted file mode 100644 index 70cb6840f8..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestWrapperTestBase.groovy +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import io.opentelemetry.instrumentation.test.LibraryInstrumentationSpecification -import org.junit.Rule -import org.junit.contrib.java.lang.system.EnvironmentVariables -import spock.lang.Shared - -import java.util.function.BiFunction - -class TracingRequestWrapperTestBase extends LibraryInstrumentationSpecification { - - @Rule - public final EnvironmentVariables environmentVariables = new EnvironmentVariables() - - @Shared - TracingRequestWrapperBase wrapper - - @Shared - Context context - - def setup() { - context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - context.getInvokedFunctionArn() >> "arn:aws:lambda:us-east-1:123456789:function:test" - } - - def setLambda(handler, Closure wrapperConstructor, BiFunction mapper) { - environmentVariables.set(WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, handler) - wrapper = wrapperConstructor.call(testRunner().openTelemetrySdk, WrappedLambda.fromConfiguration(), mapper) - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventWrapperTest.groovy b/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventWrapperTest.groovy deleted file mode 100644 index 16c423e6df..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventWrapperTest.groovy +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import com.amazonaws.services.lambda.runtime.events.SQSEvent -import io.opentelemetry.instrumentation.test.LibraryInstrumentationSpecification -import io.opentelemetry.semconv.resource.attributes.ResourceAttributes -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes -import org.junit.Rule -import org.junit.contrib.java.lang.system.EnvironmentVariables - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER -import static io.opentelemetry.api.trace.SpanKind.SERVER - -class TracingSqsEventWrapperTest extends LibraryInstrumentationSpecification { - - @Rule - public final EnvironmentVariables environmentVariables = new EnvironmentVariables() - - TracingSqsEventWrapper wrapper - Context context - - def setup() { - context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - context.getInvokedFunctionArn() >> "arn:aws:lambda:us-east-1:123456789:function:test" - } - - def setLambda(handler, Closure wrapperConstructor) { - environmentVariables.set(WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, handler) - wrapper = wrapperConstructor.call(testRunner().openTelemetrySdk, WrappedLambda.fromConfiguration()) - } - - static class TestRequestHandler implements RequestHandler { - - @Override - Void handleRequest(SQSEvent input, Context context) { - return null - } - - } - - - def "handler event traced"() { - given: - setLambda(TestRequestHandler.getName() + "::handleRequest", TracingSqsEventWrapper.metaClass.&invokeConstructor) - - when: - SQSEvent event = new SQSEvent() - SQSEvent.SQSMessage record = new SQSEvent.SQSMessage() - record.setEventSource("otel") - record.setAttributes(Collections.emptyMap()) - event.setRecords(Arrays.asList(record)) - wrapper.handleRequest(event, context) - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$ResourceAttributes.FAAS_ID.key" "arn:aws:lambda:us-east-1:123456789:function:test" - "$ResourceAttributes.CLOUD_ACCOUNT_ID.key" "123456789" - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - span(1) { - name("otel process") - kind CONSUMER - attributes { - "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" - "$SemanticAttributes.MESSAGING_OPERATION" "process" - } - } - } - } - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaApiGatewayWrapperTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaApiGatewayWrapperTest.java new file mode 100644 index 0000000000..26ab0b3c5c --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaApiGatewayWrapperTest.java @@ -0,0 +1,308 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.util.HashMap; +import java.util.Map; +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.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class AwsLambdaApiGatewayWrapperTest { + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerApiGateway::handleRequest") + void tracedWithHttpPropagation() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + + Map headers = new HashMap<>(); + headers.put("traceparent", "00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01"); + headers.put("User-Agent", "Test Client"); + headers.put("host", "localhost:123"); + headers.put("X-FORWARDED-PROTO", "http"); + Map query = new HashMap<>(); + query.put("a", "b"); + query.put("c", "d"); + APIGatewayProxyRequestEvent input = + new APIGatewayProxyRequestEvent() + .withHttpMethod("GET") + .withResource("/hello/{param}") + .withPath("/hello/world") + .withBody("hello") + .withQueryStringParameters(query) + .withHeaders(headers); + + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) wrapper.handleRequest(input, context); + + assertThat(result.getBody()).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("/hello/{param}") + .hasKind(SpanKind.SERVER) + .hasTraceId("4fd0b6131f19f39af59518d127b0cafe") + .hasParentSpanId("0000000000000456") + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"), + entry(SemanticAttributes.FAAS_TRIGGER, "http"), + entry(SemanticAttributes.HTTP_METHOD, "GET"), + entry(SemanticAttributes.HTTP_USER_AGENT, "Test Client"), + entry( + SemanticAttributes.HTTP_URL, + "http://localhost:123/hello/world?a=b&c=d"), + entry(SemanticAttributes.HTTP_STATUS_CODE, 200L))))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerApiGateway::handleRequest") + void handlerTraced_empty() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("empty"), context); + + assertThat(result.getBody()).isNull(); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"), + entry(SemanticAttributes.FAAS_TRIGGER, "http"))))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTraced_string() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("\"hello\""), context); + + assertThat(result.getBody()).isEqualTo("\"world\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"), + entry(SemanticAttributes.FAAS_TRIGGER, "http"))))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerInteger::handleRequest") + void handlerTraced_integer() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("1"), context); + + assertThat(result.getBody()).isEqualTo("\"world\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"), + entry(SemanticAttributes.FAAS_TRIGGER, "http"))))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerCustomType::handleRequest") + void handlerTraced_customType() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest( + new APIGatewayProxyRequestEvent() + .withBody("{\"key\":\"hello\", \"value\":\"General Kenobi\"}"), + context); + + assertThat(result.getBody()).isEqualTo("\"General Kenobi\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"), + entry(SemanticAttributes.FAAS_TRIGGER, "http"))))); + } + + public static class TestRequestHandlerApiGateway + implements RequestHandler { + + @Override + public APIGatewayProxyResponseEvent handleRequest( + APIGatewayProxyRequestEvent input, Context context) { + if (input.getBody().equals("hello")) { + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("world"); + } else if (input.getBody().equals("empty")) { + return new APIGatewayProxyResponseEvent(); + } + throw new IllegalStateException("bad request"); + } + } + + public static final class TestRequestHandlerString implements RequestHandler { + + @Override + public String handleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static final class TestRequestHandlerInteger implements RequestHandler { + + @Override + public String handleRequest(Integer input, Context context) { + if (input == 1) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static class CustomType { + public String key; + public String value; + } + + public static final class TestRequestHandlerCustomType + implements RequestHandler { + + @Override + public String handleRequest(CustomType input, Context context) { + if (input.key.equals("hello")) { + return input.value; + } + throw new IllegalArgumentException("bad argument"); + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java new file mode 100644 index 0000000000..c5b4980cfa --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventHandlerTest.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest { + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected RequestHandler handler() { + return new TestHandler(testing.getOpenTelemetrySdk()); + } + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + private static final class TestHandler extends TracingSqsEventHandler { + + TestHandler(OpenTelemetrySdk openTelemetrySdk) { + super(openTelemetrySdk); + } + + @Override + protected void handleEvent(SQSEvent event, Context context) {} + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventWrapperTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventWrapperTest.java new file mode 100644 index 0000000000..82ae6bc8b7 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsEventWrapperTest.java @@ -0,0 +1,112 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.lang.reflect.Constructor; +import java.util.Collections; +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.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +@SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaSqsEventWrapperTest$TestRequestHandler::handleRequest") +public class AwsLambdaSqsEventWrapperTest { + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + void eventTraced() { + SQSEvent event = new SQSEvent(); + SQSEvent.SQSMessage record = newMessage(); + record.setEventSource("otel"); + record.setAttributes(Collections.emptyMap()); + event.setRecords(Collections.singletonList(record)); + + TracingSqsEventWrapper wrapper = + new TracingSqsEventWrapper( + testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + wrapper.handleRequest(event, context); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))), + span -> + span.hasName("otel process") + .hasKind(SpanKind.CONSUMER) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"), + entry( + SemanticAttributes.MESSAGING_OPERATION, "process"))))); + } + + public static final class TestRequestHandler implements RequestHandler { + @Override + public Void handleRequest(SQSEvent input, Context context) { + return null; + } + } + + // Constructor private in early versions. + private static SQSEvent.SQSMessage newMessage() { + try { + Constructor ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor(); + return ctor.newInstance(); + } catch (Throwable t) { + throw new AssertionError(t); + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.java new file mode 100644 index 0000000000..8be0e32ca1 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaSqsMessageHandlerTest.java @@ -0,0 +1,168 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collections; +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.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class AwsLambdaSqsMessageHandlerTest { + + private static final String AWS_TRACE_HEADER1 = + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"; + private static final String AWS_TRACE_HEADER2 = + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad9;Sampled=1"; + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + void processSpans() { + SQSEvent.SQSMessage message1 = newMessage(); + message1.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER1)); + message1.setMessageId("message1"); + message1.setEventSource("queue1"); + + SQSEvent.SQSMessage message2 = newMessage(); + message2.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER2)); + message2.setMessageId("message2"); + message2.setEventSource("queue1"); + + SQSEvent event = new SQSEvent(); + event.setRecords(Arrays.asList(message1, message2)); + + new TestHandler(testing.getOpenTelemetrySdk()).handleRequest(event, context); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(0).getSpanId()) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"), + entry(SemanticAttributes.MESSAGING_OPERATION, "process"))) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad8", + TraceFlags.getSampled(), + TraceState.getDefault())), + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad9", + TraceFlags.getSampled(), + TraceState.getDefault()))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(1).getSpanId()) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"), + entry(SemanticAttributes.MESSAGING_OPERATION, "process"), + entry(SemanticAttributes.MESSAGING_MESSAGE_ID, "message1"), + entry(SemanticAttributes.MESSAGING_DESTINATION, "queue1"))) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad8", + TraceFlags.getSampled(), + TraceState.getDefault()))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(1).getSpanId()) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"), + entry(SemanticAttributes.MESSAGING_OPERATION, "process"), + entry(SemanticAttributes.MESSAGING_MESSAGE_ID, "message2"), + entry(SemanticAttributes.MESSAGING_DESTINATION, "queue1"))) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad9", + TraceFlags.getSampled(), + TraceState.getDefault()))))); + } + + // Constructor private in early versions. + private static SQSEvent.SQSMessage newMessage() { + try { + Constructor ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor(); + return ctor.newInstance(); + } catch (Throwable t) { + throw new AssertionError(t); + } + } + + private static final class TestHandler extends TracingSqsMessageHandler { + + TestHandler(OpenTelemetrySdk openTelemetrySdk) { + super(openTelemetrySdk); + } + + @Override + protected void handleMessage(SQSEvent.SQSMessage message, Context context) {} + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperHttpPropagationTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperHttpPropagationTest.java new file mode 100644 index 0000000000..da8188600e --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperHttpPropagationTest.java @@ -0,0 +1,162 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +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.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +@SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaStreamWrapperHttpPropagationTest$TestRequestHandler::handleRequest") +public class AwsLambdaStreamWrapperHttpPropagationTest { + + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + void handlerTraced() throws Exception { + String content = + "{" + + "\"headers\" : {" + + "\"traceparent\": \"00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01\"" + + "}," + + "\"body\" : \"hello\"" + + "}"; + InputStream input = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + OutputStream output = new ByteArrayOutputStream(); + + TracingRequestStreamWrapper wrapper = + new TracingRequestStreamWrapper( + testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + wrapper.handleRequest(input, output, context); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasTraceId("4fd0b6131f19f39af59518d127b0cafe") + .hasParentSpanId("0000000000000456") + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + @Test + void handlerTracedWithException() { + String content = + "{" + + "\"headers\" : {" + + "\"traceparent\": \"00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01\"" + + "}," + + "\"body\" : \"bye\"" + + "}"; + InputStream input = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + OutputStream output = new ByteArrayOutputStream(); + + TracingRequestStreamWrapper wrapper = + new TracingRequestStreamWrapper( + testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + + Throwable thrown = catchThrowable(() -> wrapper.handleRequest(input, output, context)); + assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasTraceId("4fd0b6131f19f39af59518d127b0cafe") + .hasParentSpanId("0000000000000456") + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + public static final class TestRequestHandler implements RequestStreamHandler { + + @Override + public void handleRequest(InputStream input, OutputStream output, Context context) + throws IOException { + JsonNode root = OBJECT_MAPPER.readTree(input); + String body = root.get("body").asText(); + BufferedWriter writer = + new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)); + if (body.equals("hello")) { + writer.write("world"); + writer.flush(); + writer.close(); + } else { + throw new IllegalArgumentException("bad argument"); + } + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperTest.java new file mode 100644 index 0000000000..a5cfa9cd9a --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaStreamWrapperTest.java @@ -0,0 +1,143 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +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.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +@SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaStreamWrapperTest$TestRequestHandler::handleRequest") +public class AwsLambdaStreamWrapperTest { + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + void handlerTraced() throws Exception { + InputStream input = new ByteArrayInputStream("hello\n".getBytes(StandardCharsets.UTF_8)); + OutputStream output = new ByteArrayOutputStream(); + + TracingRequestStreamWrapper wrapper = + new TracingRequestStreamWrapper( + testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + wrapper.handleRequest(input, output, context); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + @Test + void handlerTracedWithException() { + InputStream input = new ByteArrayInputStream("bye\n".getBytes(StandardCharsets.UTF_8)); + OutputStream output = new ByteArrayOutputStream(); + + TracingRequestStreamWrapper wrapper = + new TracingRequestStreamWrapper( + testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + + Throwable thrown = catchThrowable(() -> wrapper.handleRequest(input, output, context)); + assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + public static final class TestRequestHandler implements RequestStreamHandler { + + @Override + public void handleRequest(InputStream input, OutputStream output, Context context) + throws IOException { + BufferedReader reader = + new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); + BufferedWriter writer = + new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)); + String line = reader.readLine(); + if (line.equals("hello")) { + writer.write("world"); + writer.flush(); + writer.close(); + } else { + throw new IllegalArgumentException("bad argument"); + } + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.java new file mode 100644 index 0000000000..a008a11944 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTest.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class AwsLambdaTest extends AbstractAwsLambdaTest { + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected RequestHandler handler() { + return new TestRequestHandler(testing.getOpenTelemetrySdk()); + } + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + private static final class TestRequestHandler extends TracingRequestHandler { + + TestRequestHandler(OpenTelemetrySdk openTelemetrySdk) { + super(openTelemetrySdk); + } + + @Override + protected String doHandleRequest(String input, Context context) { + return AbstractAwsLambdaTest.doHandleRequest(input, context); + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaWrapperTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaWrapperTest.java new file mode 100644 index 0000000000..1f25568596 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaWrapperTest.java @@ -0,0 +1,219 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +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.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class AwsLambdaWrapperTest { + + @RegisterExtension + public static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTraced() { + TracingRequestWrapper wrapper = + new TracingRequestWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestWrapper::map); + Object result = wrapper.handleRequest("hello", context); + + assertThat(result).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTracedWithException() { + TracingRequestWrapper wrapper = + new TracingRequestWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestWrapper::map); + Throwable thrown = catchThrowable(() -> wrapper.handleRequest("goodbye", context)); + + assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaWrapperTest$TestRequestHandlerInteger::handleRequest") + void handlerTraced_integer() { + TracingRequestWrapper wrapper = + new TracingRequestWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestWrapper::map); + Object result = wrapper.handleRequest(1, context); + + assertThat(result).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaWrapperTest$TestRequestHandlerCustomType::handleRequest") + void handlerTraced_custom() { + TracingRequestWrapper wrapper = + new TracingRequestWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestWrapper::map); + CustomType ct = new CustomType(); + ct.key = "hello there"; + ct.value = "General Kenobi"; + Object result = wrapper.handleRequest(ct, context); + + assertThat(result).isEqualTo("General Kenobi"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + OpenTelemetryAssertions.assertThat(attrs) + .containsOnly( + entry( + ResourceAttributes.FAAS_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + entry(ResourceAttributes.CLOUD_ACCOUNT_ID, "123456789"), + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + public static final class TestRequestHandlerString implements RequestHandler { + + @Override + public String handleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static final class TestRequestHandlerInteger implements RequestHandler { + + @Override + public String handleRequest(Integer input, Context context) { + if (input == 1) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + private static class CustomType { + String key; + String value; + } + + public static final class TestRequestHandlerCustomType + implements RequestHandler { + + @Override + public String handleRequest(CustomType input, Context context) { + if (input.key.equals("hello there")) { + return input.value; + } + throw new IllegalArgumentException("bad argument"); + } + } +} diff --git a/instrumentation/aws-lambda-1.0/testing/build.gradle.kts b/instrumentation/aws-lambda-1.0/testing/build.gradle.kts index a8d8f61dfd..83dde24226 100644 --- a/instrumentation/aws-lambda-1.0/testing/build.gradle.kts +++ b/instrumentation/aws-lambda-1.0/testing/build.gradle.kts @@ -8,6 +8,9 @@ dependencies { api("com.amazonaws:aws-lambda-java-core:1.0.0") api("com.amazonaws:aws-lambda-java-events:2.2.1") + api("org.junit-pioneer:junit-pioneer") + api("org.mockito:mockito-junit-jupiter") + implementation("com.google.guava:guava") implementation("org.codehaus.groovy:groovy-all") diff --git a/instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaRequestHandlerTest.groovy b/instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaRequestHandlerTest.groovy deleted file mode 100644 index 1f56817307..0000000000 --- a/instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaRequestHandlerTest.groovy +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import com.github.stefanbirkner.systemlambda.SystemLambda -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes - -import static io.opentelemetry.api.trace.SpanKind.SERVER -import static io.opentelemetry.api.trace.StatusCode.ERROR - -abstract class AbstractAwsLambdaRequestHandlerTest extends InstrumentationSpecification { - - protected static String doHandleRequest(String input, Context context) { - if (input == "hello") { - return "world" - } - throw new IllegalArgumentException("bad argument") - } - - abstract RequestHandler handler() - - def "handler traced"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - - def result = handler().handleRequest("hello", context) - - then: - result == "world" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - - def "handler traced with exception"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - - def thrown - try { - handler().handleRequest("goodbye", context) - } catch (Throwable t) { - thrown = t - } - - then: - thrown != null - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - status ERROR - errorEvent(IllegalArgumentException, "bad argument") - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } - - def "handler links to lambda trace"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - - def result - SystemLambda.withEnvironmentVariable("_X_AMZN_TRACE_ID", "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=1") - .execute({ - result = handler().handleRequest("hello", context) - }) - - then: - result == "world" - assertTraces(1) { - trace(0, 1) { - span(0) { - name("my_function") - kind SERVER - parentSpanId("0000000000000456") - traceId("8a3c60f7d188f8fa79d48a391a778fa6") - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - } - } - } -} diff --git a/instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsHandlerTest.groovy b/instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsHandlerTest.groovy deleted file mode 100644 index d726a075a8..0000000000 --- a/instrumentation/aws-lambda-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsHandlerTest.groovy +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0 - -import com.amazonaws.services.lambda.runtime.Context -import com.amazonaws.services.lambda.runtime.RequestHandler -import com.amazonaws.services.lambda.runtime.events.SQSEvent -import io.opentelemetry.instrumentation.test.InstrumentationSpecification -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER -import static io.opentelemetry.api.trace.SpanKind.SERVER - -abstract class AbstractAwsLambdaSqsHandlerTest extends InstrumentationSpecification { - - private static final String AWS_TRACE_HEADER = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1" - - abstract RequestHandler handler() - - def "messages from same source"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - - def message1 = new SQSEvent.SQSMessage() - message1.setAttributes(["AWSTraceHeader": AWS_TRACE_HEADER]) - message1.setMessageId("message1") - message1.setEventSource("queue1") - - def message2 = new SQSEvent.SQSMessage() - message2.setAttributes([:]) - message2.setMessageId("message2") - message2.setEventSource("queue1") - - def event = new SQSEvent() - event.setRecords([message1, message2]) - - handler().handleRequest(event, context) - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - span(1) { - name("queue1 process") - kind CONSUMER - parentSpanId(span(0).spanId) - attributes { - "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" - "$SemanticAttributes.MESSAGING_OPERATION" "process" - } - hasLink("5759e988bd862e3fe1be46a994272793", "53995c3f42cd8ad8") - } - } - } - } - - def "messages from different source"() { - when: - def context = Mock(Context) - context.getFunctionName() >> "my_function" - context.getAwsRequestId() >> "1-22-333" - - def message1 = new SQSEvent.SQSMessage() - message1.setAttributes(["AWSTraceHeader": AWS_TRACE_HEADER]) - message1.setMessageId("message1") - message1.setEventSource("queue1") - - def message2 = new SQSEvent.SQSMessage() - message2.setAttributes([:]) - message2.setMessageId("message2") - message2.setEventSource("queue2") - - def event = new SQSEvent() - event.setRecords([message1, message2]) - - handler().handleRequest(event, context) - - then: - assertTraces(1) { - trace(0, 2) { - span(0) { - name("my_function") - kind SERVER - attributes { - "$SemanticAttributes.FAAS_EXECUTION" "1-22-333" - } - } - span(1) { - name("multiple_sources process") - kind CONSUMER - parentSpanId(span(0).spanId) - attributes { - "$SemanticAttributes.MESSAGING_SYSTEM" "AmazonSQS" - "$SemanticAttributes.MESSAGING_OPERATION" "process" - } - hasLink("5759e988bd862e3fe1be46a994272793", "53995c3f42cd8ad8") - } - } - } - } -} diff --git a/instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsEventHandlerTest.java new file mode 100644 index 0000000000..ac01c763b7 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaSqsEventHandlerTest.java @@ -0,0 +1,167 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collections; +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.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public abstract class AbstractAwsLambdaSqsEventHandlerTest { + + private static final String AWS_TRACE_HEADER = + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"; + + protected abstract RequestHandler handler(); + + protected abstract InstrumentationExtension testing(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + } + + @AfterEach + void tearDown() { + assertThat(testing().forceFlushCalled()).isTrue(); + } + + @Test + void sameSource() { + SQSEvent.SQSMessage message1 = newMessage(); + message1.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER)); + message1.setMessageId("message1"); + message1.setEventSource("queue1"); + + SQSEvent.SQSMessage message2 = newMessage(); + message2.setAttributes(Collections.emptyMap()); + message2.setMessageId("message2"); + message2.setEventSource("queue1"); + + SQSEvent event = new SQSEvent(); + event.setRecords(Arrays.asList(message1, message2)); + + handler().handleRequest(event, context); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(0).getSpanId()) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"), + entry( + SemanticAttributes.MESSAGING_OPERATION, "process"))) + .hasLinksSatisfying( + links -> + assertThat(links) + .singleElement() + .satisfies( + link -> { + assertThat(link.getSpanContext().getTraceId()) + .isEqualTo("5759e988bd862e3fe1be46a994272793"); + assertThat(link.getSpanContext().getSpanId()) + .isEqualTo("53995c3f42cd8ad8"); + })))); + } + + @Test + void differentSource() { + SQSEvent.SQSMessage message1 = newMessage(); + message1.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER)); + message1.setMessageId("message1"); + message1.setEventSource("queue1"); + + SQSEvent.SQSMessage message2 = newMessage(); + message2.setAttributes(Collections.emptyMap()); + message2.setMessageId("message2"); + message2.setEventSource("queue2"); + + SQSEvent event = new SQSEvent(); + event.setRecords(Arrays.asList(message1, message2)); + + handler().handleRequest(event, context); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.FAAS_EXECUTION, "1-22-333"))), + span -> + span.hasName("multiple_sources process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(0).getSpanId()) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"), + entry( + SemanticAttributes.MESSAGING_OPERATION, "process"))) + .hasLinksSatisfying( + links -> + assertThat(links) + .singleElement() + .satisfies( + link -> { + assertThat(link.getSpanContext().getTraceId()) + .isEqualTo("5759e988bd862e3fe1be46a994272793"); + assertThat(link.getSpanContext().getSpanId()) + .isEqualTo("53995c3f42cd8ad8"); + })))); + } + + // Constructor private in early versions. + private static SQSEvent.SQSMessage newMessage() { + try { + Constructor ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor(); + ctor.setAccessible(true); + return ctor.newInstance(); + } catch (Throwable t) { + throw new AssertionError(t); + } + } +} diff --git a/instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaTest.java b/instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaTest.java new file mode 100644 index 0000000000..4b3ae206a7 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AbstractAwsLambdaTest.java @@ -0,0 +1,120 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +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.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public abstract class AbstractAwsLambdaTest { + + protected static String doHandleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + + protected abstract RequestHandler handler(); + + protected abstract InstrumentationExtension testing(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + } + + @AfterEach + void tearDown() { + assertThat(testing().forceFlushCalled()).isTrue(); + } + + @Test + void handlerTraced() { + String result = handler().handleRequest("hello", context); + assertThat(result).isEqualTo("world"); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry( + SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + @Test + void handlerTracedWithException() { + Throwable thrown = catchThrowable(() -> handler().handleRequest("goodbye", context)); + assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry( + SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } + + @Test + @SetEnvironmentVariable( + key = "_X_AMZN_TRACE_ID", + value = "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=1") + void handlerLinksToInfrastructureTrace() { + String result = handler().handleRequest("hello", context); + assertThat(result).isEqualTo("world"); + + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasTraceId("8a3c60f7d188f8fa79d48a391a778fa6") + .hasParentSpanId("0000000000000456") + .hasAttributesSatisfying( + attrs -> + assertThat(attrs) + .containsOnly( + entry( + SemanticAttributes.FAAS_EXECUTION, "1-22-333"))))); + } +} diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java index b0b4749846..b7e9a354eb 100644 --- a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/InstrumentationExtension.java @@ -11,9 +11,11 @@ import static org.awaitility.Awaitility.await; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.context.ContextStorage; import io.opentelemetry.instrumentation.testing.InstrumentationTestRunner; +import io.opentelemetry.instrumentation.testing.LibraryTestRunner; import io.opentelemetry.instrumentation.testing.util.ContextStorageCloser; import io.opentelemetry.instrumentation.testing.util.ThrowingRunnable; import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier; +import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.metrics.data.MetricData; import io.opentelemetry.sdk.testing.assertj.TraceAssert; import io.opentelemetry.sdk.trace.data.SpanData; @@ -108,6 +110,7 @@ public abstract class InstrumentationExtension } @SafeVarargs + @SuppressWarnings("varargs") public final void waitAndAssertTraces(Consumer... assertions) { testRunner.waitAndAssertTraces(assertions); } @@ -166,6 +169,19 @@ public abstract class InstrumentationExtension return testRunner.runWithServerSpan(spanName, callback); } + /** Returns whether forceFlush was called. */ + public boolean forceFlushCalled() { + return testRunner.forceFlushCalled(); + } + + /** Returns the {@link OpenTelemetrySdk} initialied for library tests. */ + public OpenTelemetrySdk getOpenTelemetrySdk() { + if (testRunner instanceof LibraryTestRunner) { + return ((LibraryTestRunner) testRunner).getOpenTelemetrySdk(); + } + throw new IllegalStateException("Can only be called from library instrumentation tests."); + } + protected InstrumentationTestRunner getTestRunner() { return testRunner; }