From 61a3a868cd99aabd5fb4d4c8e828deba4d125a37 Mon Sep 17 00:00:00 2001 From: Mark Sailes Date: Fri, 8 Oct 2021 11:56:44 +0100 Subject: [PATCH] new test-suite module for testing multi module use cases. --- pom.xml | 16 ++ .../logging/internal/LambdaLoggingAspect.java | 2 +- powertools-test-suite/pom.xml | 142 +++++++++++++++ .../testsuite/LoggingOrderTest.java | 162 ++++++++++++++++++ .../handler/LoggingOrderMessageHandler.java | 18 ++ .../TracingLoggingStreamMessageHandler.java | 23 +++ .../src/test/resources/log4j2.xml | 16 ++ 7 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 powertools-test-suite/pom.xml create mode 100644 powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java create mode 100644 powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java create mode 100644 powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java create mode 100644 powertools-test-suite/src/test/resources/log4j2.xml diff --git a/pom.xml b/pom.xml index 701a0af02..a3ee3423a 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ powertools-metrics powertools-parameters powertools-validation + powertools-test-suite @@ -88,6 +89,21 @@ powertools-core ${project.version} + + software.amazon.lambda + powertools-logging + ${project.version} + + + software.amazon.lambda + powertools-sqs + ${project.version} + + + software.amazon.lambda + powertools-tracing + ${project.version} + com.amazonaws aws-lambda-java-core diff --git a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java index 4d8ce15bb..34f3bf312 100644 --- a/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java +++ b/powertools-logging/src/main/java/software/amazon/lambda/powertools/logging/internal/LambdaLoggingAspect.java @@ -58,7 +58,7 @@ import static software.amazon.lambda.powertools.logging.LoggingUtils.objectMapper; @Aspect -@DeclarePrecedence("*, LambdaLoggingAspect") +@DeclarePrecedence("*, SqsLargeMessageAspect, LambdaLoggingAspect") public final class LambdaLoggingAspect { private static final Logger LOG = LogManager.getLogger(LambdaLoggingAspect.class); private static final Random SAMPLER = new Random(); diff --git a/powertools-test-suite/pom.xml b/powertools-test-suite/pom.xml new file mode 100644 index 000000000..2a2dfedaf --- /dev/null +++ b/powertools-test-suite/pom.xml @@ -0,0 +1,142 @@ + + + 4.0.0 + + powertools-test-suite + jar + + + powertools-parent + software.amazon.lambda + 1.7.3 + + + AWS Lambda Powertools Java library Test Suite + + A suite of tests for interactions between the various Powertools modules. + + https://aws.amazon.com/lambda/ + + GitHub Issues + https://github.com/awslabs/aws-lambda-powertools-java/issues + + + https://github.com/awslabs/aws-lambda-powertools-java.git + + + + AWS Lambda Powertools team + Amazon Web Services + https://aws.amazon.com/ + + + + + + ossrh + https://aws.oss.sonatype.org/content/repositories/snapshots + + + + + + software.amazon.lambda + powertools-core + + + com.amazonaws + aws-lambda-java-core + + + com.amazonaws + aws-lambda-java-events + + + software.amazon.lambda + powertools-logging + + + software.amazon.lambda + powertools-tracing + + + software.amazon.lambda + powertools-sqs + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.apache.commons + commons-lang3 + test + + + org.mockito + mockito-core + test + + + org.aspectj + aspectjweaver + test + + + org.assertj + assertj-core + test + + + org.skyscreamer + jsonassert + test + + + + + + + org.codehaus.mojo + aspectj-maven-plugin + 1.14.0 + + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.target} + + + software.amazon.lambda + powertools-logging + + + software.amazon.lambda + powertools-tracing + + + software.amazon.lambda + powertools-sqs + + + + + + + compile + + + + + + + \ No newline at end of file diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java new file mode 100644 index 000000000..aebcf0f52 --- /dev/null +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/LoggingOrderTest.java @@ -0,0 +1,162 @@ +package software.amazon.lambda.powertools.testsuite; + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Map; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.S3Object; +import com.amazonaws.xray.AWSXRay; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.ThreadContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import software.amazon.lambda.powertools.core.internal.LambdaHandlerProcessor; +import software.amazon.lambda.powertools.logging.internal.LambdaLoggingAspect; +import software.amazon.lambda.powertools.sqs.internal.SqsLargeMessageAspect; +import software.amazon.lambda.powertools.testsuite.handler.LoggingOrderMessageHandler; +import software.amazon.lambda.powertools.testsuite.handler.TracingLoggingStreamMessageHandler; + +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static org.apache.commons.lang3.reflect.FieldUtils.writeStaticField; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +public class LoggingOrderTest { + + private static final String BUCKET_NAME = "ms-extended-sqs-client"; + private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf"; + + @Mock + private Context context; + + @Mock + private AmazonS3 amazonS3; + + @BeforeEach + void setUp() throws IllegalAccessException, IOException, NoSuchMethodException, InvocationTargetException { + openMocks(this); + writeStaticField(SqsLargeMessageAspect.class, "amazonS3", amazonS3, true); + ThreadContext.clearAll(); + writeStaticField(LambdaHandlerProcessor.class, "IS_COLD_START", null, true); + setupContext(); + //Make sure file is cleaned up before running full stack logging regression + FileChannel.open(Paths.get("target/logfile.json"), StandardOpenOption.WRITE).truncate(0).close(); + resetLogLevel(Level.INFO); + AWSXRay.beginSegment(LoggingOrderTest.class.getName()); + } + + /** + * The SQSEvent payload will be altered by the @SqsLargeMessage annotation. Logging of the event should happen + * after the event has been altered + */ + @Test + public void testThatLoggingAnnotationActsLast() throws IOException { + S3Object s3Response = new S3Object(); + s3Response.setObjectContent(new ByteArrayInputStream("A big message".getBytes())); + + when(amazonS3.getObject(BUCKET_NAME, BUCKET_KEY)).thenReturn(s3Response); + SQSEvent sqsEvent = messageWithBody("[\"software.amazon.payloadoffloading.PayloadS3Pointer\",{\"s3BucketName\":\"" + BUCKET_NAME + "\",\"s3Key\":\"" + BUCKET_KEY + "\"}]"); + + LoggingOrderMessageHandler requestHandler = new LoggingOrderMessageHandler(); + requestHandler.handleRequest(sqsEvent, context); + + assertThat(Files.lines(Paths.get("target/logfile.json"))) + .hasSize(2) + .satisfies(line -> { + Map actual = parseToMap(line.get(0)); + + String message = actual.get("message").toString(); + + assertThat(message) + .contains("A big message"); + }); + } + + @Test + public void testLoggingAnnotationActsAfterTracingForStreamingHandler() throws IOException { + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + S3EventNotification s3EventNotification = s3EventNotification(); + + TracingLoggingStreamMessageHandler handler = new TracingLoggingStreamMessageHandler(); + handler.handleRequest(new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(s3EventNotification)), output, context); + + assertThat(new String(output.toByteArray(), StandardCharsets.UTF_8)) + .isNotEmpty(); + } + + private void setupContext() { + when(context.getFunctionName()).thenReturn("testFunction"); + when(context.getInvokedFunctionArn()).thenReturn("testArn"); + when(context.getFunctionVersion()).thenReturn("1"); + when(context.getMemoryLimitInMB()).thenReturn(10); + when(context.getAwsRequestId()).thenReturn("RequestId"); + } + + private void resetLogLevel(Level level) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + Method resetLogLevels = LambdaLoggingAspect.class.getDeclaredMethod("resetLogLevels", Level.class); + resetLogLevels.setAccessible(true); + resetLogLevels.invoke(null, level); + writeStaticField(LambdaLoggingAspect.class, "LEVEL_AT_INITIALISATION", level, true); + } + + private Map parseToMap(String stringAsJson) { + try { + return new ObjectMapper().readValue(stringAsJson, Map.class); + } catch (JsonProcessingException e) { + fail("Failed parsing logger line " + stringAsJson); + return emptyMap(); + } + } + + private S3EventNotification s3EventNotification() { + S3EventNotification.S3EventNotificationRecord record = new S3EventNotification.S3EventNotificationRecord("us-west-2", + "ObjectCreated:Put", + "aws:s3", + null, + "2.1", + new S3EventNotification.RequestParametersEntity("127.0.0.1"), + new S3EventNotification.ResponseElementsEntity("C3D13FE58DE4C810", "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"), + new S3EventNotification.S3Entity("testConfigRule", + new S3EventNotification.S3BucketEntity("mybucket", + new S3EventNotification.UserIdentityEntity("A3NL1KOZZKExample"), + "arn:aws:s3:::mybucket"), + new S3EventNotification.S3ObjectEntity("HappyFace.jpg", + 1024L, + "d41d8cd98f00b204e9800998ecf8427e", + "096fKKXTRTtl3on89fVO.nfljtsv6qko", + "0055AED6DCD90281E5"), + "1.0"), + new S3EventNotification.UserIdentityEntity("AIDAJDPLRKLG7UEXAMPLE") + ); + + return new S3EventNotification(singletonList(record)); + } + + private SQSEvent messageWithBody(String messageBody) { + SQSEvent.SQSMessage sqsMessage = new SQSEvent.SQSMessage(); + sqsMessage.setBody(messageBody); + SQSEvent sqsEvent = new SQSEvent(); + sqsEvent.setRecords(singletonList(sqsMessage)); + return sqsEvent; + } +} \ No newline at end of file diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java new file mode 100644 index 000000000..6a0c7248e --- /dev/null +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/LoggingOrderMessageHandler.java @@ -0,0 +1,18 @@ +package software.amazon.lambda.powertools.testsuite.handler; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.sqs.SqsLargeMessage; + +public class LoggingOrderMessageHandler implements RequestHandler { + + @Override + @SqsLargeMessage + @Logging(logEvent = true) + public String handleRequest(SQSEvent sqsEvent, Context context) { + System.out.println(sqsEvent.getRecords().get(0).getBody()); + return sqsEvent.getRecords().get(0).getBody(); + } +} diff --git a/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java new file mode 100644 index 000000000..d0f2b3ac5 --- /dev/null +++ b/powertools-test-suite/src/test/java/software/amazon/lambda/powertools/testsuite/handler/TracingLoggingStreamMessageHandler.java @@ -0,0 +1,23 @@ +package software.amazon.lambda.powertools.testsuite.handler; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Map; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import com.fasterxml.jackson.databind.ObjectMapper; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.tracing.Tracing; + +public class TracingLoggingStreamMessageHandler implements RequestStreamHandler { + + @Logging(logEvent = true) + @Tracing + @Override + public void handleRequest(InputStream input, OutputStream output, Context context) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(output, mapper.readValue(input, Map.class)); + } +} diff --git a/powertools-test-suite/src/test/resources/log4j2.xml b/powertools-test-suite/src/test/resources/log4j2.xml new file mode 100644 index 000000000..108e32b75 --- /dev/null +++ b/powertools-test-suite/src/test/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file