diff --git a/.gitignore b/.gitignore index b5d289a8b..5a277e5d6 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ experimental/aws-lambda-java-profiler/integration_tests/helloworld/bin .vscode .kiro build +mise.toml diff --git a/aws-lambda-java-serialization/mise.toml b/aws-lambda-java-serialization/mise.toml deleted file mode 100644 index 854b93b56..000000000 --- a/aws-lambda-java-serialization/mise.toml +++ /dev/null @@ -1,2 +0,0 @@ -[tools] -java = "corretto-8" diff --git a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateModule.java b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateModule.java index 8a6954e34..acc8bde2a 100644 --- a/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateModule.java +++ b/aws-lambda-java-serialization/src/main/java/com/amazonaws/services/lambda/runtime/serialization/events/modules/DateModule.java @@ -15,10 +15,17 @@ import com.fasterxml.jackson.databind.module.SimpleModule; /** - * The AWS API represents a date as a double, which specifies the fractional - * number of seconds since the epoch. Java's Date, however, represents a date as - * a long, which specifies the number of milliseconds since the epoch. This - * class is used to translate between these two formats. + * The AWS API represents a date as a double (fractional seconds since epoch). + * Java's Date uses a long (milliseconds since epoch). This module translates + * between the two formats. + * + *

+ * Round-trip caveats: The serializer always writes via + * {@link JsonGenerator#writeNumber(double)}, so integer epochs + * (e.g. {@code 1428537600}) round-trip as decimal ({@code 1.4285376E9}). + * Sub-millisecond precision is lost because {@link java.util.Date} + * has milliseconds precision. + *

* * This class is copied from LambdaEventBridgeservice * com.amazon.aws.lambda.stream.ddb.DateModule diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtils.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtils.java new file mode 100644 index 000000000..f9f4e1eb6 --- /dev/null +++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtils.java @@ -0,0 +1,110 @@ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.Iterator; +import java.util.List; +import java.util.TreeSet; +import java.util.regex.Pattern; + +import org.joda.time.DateTime; + +/** + * Utility methods for working with shaded Jackson {@link JsonNode} trees. + * + *

+ * Package-private — not part of the public API. + *

+ */ +class JsonNodeUtils { + + private static final Pattern ISO_DATE_REGEX = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T.+"); + + private JsonNodeUtils() { + } + + /** + * Recursively removes all fields whose value is {@code null} from the + * tree. This mirrors the serializer's {@code Include.NON_NULL} behaviour + * so that explicit nulls in the fixture don't cause false-positive diffs. + */ + static JsonNode stripNulls(JsonNode node) { + if (node.isObject()) { + ObjectNode obj = (ObjectNode) node; + Iterator fieldNames = obj.fieldNames(); + while (fieldNames.hasNext()) { + String field = fieldNames.next(); + if (obj.get(field).isNull()) { + fieldNames.remove(); + } else { + stripNulls(obj.get(field)); + } + } + } else if (node.isArray()) { + for (JsonNode element : node) { + stripNulls(element); + } + } + return node; + } + + /** + * Recursively walks both trees and collects human-readable diff lines. + */ + static void diffNodes(String path, JsonNode expected, JsonNode actual, List diffs) { + if (expected.equals(actual)) + return; + + // Compares two datetime strings by parsed instant, because DateTimeModule + // normalizes the format on serialization (e.g. "+0000" → "Z", "Z" → ".000Z") + if (areSameDateTime(expected.textValue(), actual.textValue())) { + return; + } + + if (expected.isObject() && actual.isObject()) { + TreeSet allKeys = new TreeSet<>(); + expected.fieldNames().forEachRemaining(allKeys::add); + actual.fieldNames().forEachRemaining(allKeys::add); + for (String key : allKeys) { + diffChild(path + "." + key, expected.get(key), actual.get(key), diffs); + } + } else if (expected.isArray() && actual.isArray()) { + for (int i = 0; i < Math.max(expected.size(), actual.size()); i++) { + diffChild(path + "[" + i + "]", expected.get(i), actual.get(i), diffs); + } + } else { + diffs.add("CHANGED " + path + " : " + summarize(expected) + " -> " + summarize(actual)); + } + } + + /** + * Compares two strings by parsed instant when both look like ISO-8601 dates, + * because DateTimeModule normalizes format on serialization + * (e.g. "+0000" → "Z", "Z" → ".000Z"). + */ + private static boolean areSameDateTime(String expected, String actual) { + if (expected == null || actual == null + || !ISO_DATE_REGEX.matcher(expected).matches() + || !ISO_DATE_REGEX.matcher(actual).matches()) { + return false; + } + return DateTime.parse(expected).equals(DateTime.parse(actual)); + } + + private static void diffChild(String path, JsonNode expected, JsonNode actual, List diffs) { + if (expected == null) + diffs.add("ADDED " + path + " = " + summarize(actual)); + else if (actual == null) + diffs.add("MISSING " + path + " (was " + summarize(expected) + ")"); + else + diffNodes(path, expected, actual, diffs); + } + + private static String summarize(JsonNode node) { + if (node == null) { + return ""; + } + String text = node.toString(); + return text.length() > 80 ? text.substring(0, 77) + "..." : text; + } +} diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssert.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssert.java new file mode 100644 index 000000000..e189b5e2b --- /dev/null +++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssert.java @@ -0,0 +1,147 @@ +package com.amazonaws.services.lambda.runtime.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; +import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.ArrayList; +import java.util.List; + +/** + * Framework-agnostic assertion utilities for verifying Lambda event + * serialization. + * + *

+ * When opentest4j is on the classpath (e.g. JUnit 5.x / JUnit Platform), + * assertion failures are reported as + * {@code org.opentest4j.AssertionFailedError} + * which enables rich diff support in IDEs. Otherwise, falls back to plain + * {@link AssertionError}. + *

+ * + *

+ * This class is intentionally package-private to support updates to + * the aws-lambda-java-events and aws-lambda-java-serialization packages. + * Consider making it public if there's a real request for it. + *

+ */ +class LambdaEventAssert { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + /** + * Round-trip using the registered {@link LambdaEventSerializers} path + * (Jackson + mixins + DateModule + DateTimeModule + naming strategies). + * + *

+ * The check performs two consecutive round-trips + * (JSON → POJO → JSON → POJO → JSON) and compares the + * original JSON tree against the final output tree. A single structural + * comparison catches both: + *

+ *
    + *
  • Fields silently dropped during deserialization
  • + *
  • Non-idempotent serialization (output changes across round-trips)
  • + *
+ * + * @param fileName classpath resource name (must end with {@code .json}) + * @param targetClass the event class to deserialize into + * @throws AssertionError if the original and final JSON trees differ + */ + public static void assertSerializationRoundTrip(String fileName, Class targetClass) { + PojoSerializer serializer = LambdaEventSerializers.serializerFor(targetClass, + ClassLoader.getSystemClassLoader()); + + if (!fileName.endsWith(".json")) { + throw new IllegalArgumentException("File " + fileName + " must have json extension"); + } + + byte[] originalBytes; + try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) { + if (stream == null) { + throw new IllegalArgumentException("Could not load resource '" + fileName + "' from classpath"); + } + originalBytes = toBytes(stream); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read resource " + fileName, e); + } + + // Two round-trips: original → POJO → JSON → POJO → JSON + // We are doing 2 passes so we can check instability problems + // like UnstablePojo in LambdaEventAssertTest + ByteArrayOutputStream firstOutput = roundTrip(new ByteArrayInputStream(originalBytes), serializer); + ByteArrayOutputStream secondOutput = roundTrip( + new ByteArrayInputStream(firstOutput.toByteArray()), serializer); + + // Compare original tree against final tree. + // Strip explicit nulls from the original because the serializer is + // configured with Include.NON_NULL — null fields are intentionally + // omitted and that is not a data-loss bug. + try { + JsonNode originalTree = JsonNodeUtils.stripNulls(MAPPER.readTree(originalBytes)); + JsonNode finalTree = MAPPER.readTree(secondOutput.toByteArray()); + + if (!originalTree.equals(finalTree)) { + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", originalTree, finalTree, diffs); + + if (!diffs.isEmpty()) { + StringBuilder msg = new StringBuilder(); + msg.append("Serialization round-trip failure for ") + .append(targetClass.getSimpleName()) + .append(" (").append(diffs.size()).append(" difference(s)):\n"); + for (String diff : diffs) { + msg.append(" ").append(diff).append('\n'); + } + + String expected = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(originalTree); + String actual = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(finalTree); + throw buildAssertionError(msg.toString(), expected, actual); + } + } + } catch (IOException e) { + throw new UncheckedIOException("Failed to parse JSON for tree comparison", e); + } + } + + private static ByteArrayOutputStream roundTrip(InputStream stream, PojoSerializer serializer) { + T event = serializer.fromJson(stream); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + serializer.toJson(event, outputStream); + return outputStream; + } + + private static byte[] toBytes(InputStream stream) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + byte[] chunk = new byte[4096]; + int n; + while ((n = stream.read(chunk)) != -1) { + buffer.write(chunk, 0, n); + } + return buffer.toByteArray(); + } + + /** + * Tries to create an opentest4j AssertionFailedError for rich IDE diff + * support. Falls back to plain AssertionError if opentest4j is not on + * the classpath. + */ + private static AssertionError buildAssertionError(String message, String expected, String actual) { + try { + // opentest4j is provided by JUnit Platform (5.x) and enables + // IDE diff viewers to show expected vs actual side-by-side. + Class cls = Class.forName("org.opentest4j.AssertionFailedError"); + return (AssertionError) cls + .getConstructor(String.class, Object.class, Object.class) + .newInstance(message, expected, actual); + } catch (ReflectiveOperationException e) { + return new AssertionError(message + "\nExpected:\n" + expected + "\nActual:\n" + actual); + } + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java index 752b84e27..43030bbca 100644 --- a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/EventLoaderTest.java @@ -81,6 +81,12 @@ public void testLoadAPIGatewayV2CustomAuthorizerEvent() { assertThat(event).isNotNull(); assertThat(event.getRequestContext().getHttp().getMethod()).isEqualTo("POST"); + // getTime() converts the raw string "12/Mar/2020:19:03:58 +0000" into a DateTime object; + // Jackson then serializes it as ISO-8601 "2020-03-12T19:03:58.000Z" + assertThat(event.getRequestContext().getTime().toInstant().getMillis()) + .isEqualTo(DateTime.parse("2020-03-12T19:03:58.000Z").toInstant().getMillis()); + // getTimeEpoch() converts the raw long into an Instant; + // Jackson then serializes it as a decimal seconds value assertThat(event.getRequestContext().getTimeEpoch()).isEqualTo(Instant.ofEpochMilli(1583348638390L)); } @@ -136,6 +142,9 @@ public void testLoadLexEvent() { assertThat(event.getCurrentIntent().getName()).isEqualTo("BookHotel"); assertThat(event.getCurrentIntent().getSlots()).hasSize(4); assertThat(event.getBot().getName()).isEqualTo("BookTrip"); + // Jackson leniently coerces the JSON number for "Nights" into a String + // because slots is typed as Map + assertThat(event.getCurrentIntent().getSlots().get("Nights")).isInstanceOf(String.class); } @Test @@ -159,6 +168,10 @@ public void testLoadMSKFirehoseEvent() { assertThat(event.getRecords().get(0).getKafkaRecordValue().array()).asString().isEqualTo("{\"Name\":\"Hello World\"}"); assertThat(event.getRecords().get(0).getApproximateArrivalTimestamp()).asString().isEqualTo("1716369573887"); assertThat(event.getRecords().get(0).getMskRecordMetadata()).asString().isEqualTo("{offset=0, partitionId=1, approximateArrivalTimestamp=1716369573887}"); + // Jackson leniently coerces the JSON number in mskRecordMetadata into a String + // because the map is typed as Map + Map metadata = event.getRecords().get(0).getMskRecordMetadata(); + assertThat(metadata.get("approximateArrivalTimestamp")).isInstanceOf(String.class); } @Test @@ -408,6 +421,8 @@ public void testLoadRabbitMQEvent() { .returns("AIDACKCEVSQ6C2EXAMPLE", from(RabbitMQEvent.BasicProperties::getUserId)) .returns(80, from(RabbitMQEvent.BasicProperties::getBodySize)) .returns("Jan 1, 1970, 12:33:41 AM", from(RabbitMQEvent.BasicProperties::getTimestamp)); + // Jackson leniently coerces the JSON string "60000" for expiration into int + // because the model field is typed as int Map headers = basicProperties.getHeaders(); assertThat(headers).hasSize(3); diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtilsTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtilsTest.java new file mode 100644 index 000000000..ec5798dca --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/JsonNodeUtilsTest.java @@ -0,0 +1,155 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.JsonNode; +import com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class JsonNodeUtilsTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + // --- stripNulls --- + + @Test + void stripNulls_removesTopLevelNulls() throws Exception { + JsonNode node = MAPPER.readTree("{\"a\":1,\"b\":null,\"c\":\"hello\"}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"a\":1,\"c\":\"hello\"}"), result); + } + + @Test + void stripNulls_removesNestedNulls() throws Exception { + JsonNode node = MAPPER.readTree("{\"outer\":{\"keep\":true,\"drop\":null}}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"outer\":{\"keep\":true}}"), result); + } + + @Test + void stripNulls_leavesArrayElementsIntact() throws Exception { + // Nulls inside arrays are kept (they're positional) + JsonNode node = MAPPER.readTree("{\"arr\":[1,null,3]}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"arr\":[1,null,3]}"), result); + } + + @Test + void stripNulls_removesNullsInsideArrayObjects() throws Exception { + JsonNode node = MAPPER.readTree("[{\"a\":1,\"b\":null},{\"c\":null}]"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("[{\"a\":1},{}]"), result); + } + + @Test + void stripNulls_noOpOnCleanTree() throws Exception { + JsonNode node = MAPPER.readTree("{\"a\":1,\"b\":\"two\"}"); + JsonNode result = JsonNodeUtils.stripNulls(node); + assertEquals(MAPPER.readTree("{\"a\":1,\"b\":\"two\"}"), result); + } + + // --- diffNodes --- + + @Test + void diffNodes_identicalTrees_noDiffs() throws Exception { + JsonNode a = MAPPER.readTree("{\"x\":1,\"y\":\"hello\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", a, a.deepCopy(), diffs); + assertTrue(diffs.isEmpty()); + } + + @Test + void diffNodes_changedValue() throws Exception { + JsonNode expected = MAPPER.readTree("{\"x\":1}"); + JsonNode actual = MAPPER.readTree("{\"x\":2}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).startsWith("CHANGED .x")); + } + + @Test + void diffNodes_missingField() throws Exception { + JsonNode expected = MAPPER.readTree("{\"a\":1,\"b\":2}"); + JsonNode actual = MAPPER.readTree("{\"a\":1}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains("MISSING") && diffs.get(0).contains(".b"), "got: " + diffs.get(0)); + } + + @Test + void diffNodes_addedField() throws Exception { + JsonNode expected = MAPPER.readTree("{\"a\":1}"); + JsonNode actual = MAPPER.readTree("{\"a\":1,\"b\":2}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size(), "diffs: " + diffs); + assertTrue(diffs.get(0).contains("ADDED") && diffs.get(0).contains(".b"), "got: " + diffs.get(0)); + } + + @Test + void diffNodes_nestedObjectDiff() throws Exception { + JsonNode expected = MAPPER.readTree("{\"outer\":{\"inner\":\"old\"}}"); + JsonNode actual = MAPPER.readTree("{\"outer\":{\"inner\":\"new\"}}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains(".outer.inner")); + } + + @Test + void diffNodes_arrayElementDiff() throws Exception { + JsonNode expected = MAPPER.readTree("{\"arr\":[1,2,3]}"); + JsonNode actual = MAPPER.readTree("{\"arr\":[1,99,3]}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains("[1]")); + } + + @Test + void diffNodes_arrayLengthMismatch() throws Exception { + JsonNode expected = MAPPER.readTree("{\"arr\":[1,2]}"); + JsonNode actual = MAPPER.readTree("{\"arr\":[1,2,3]}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).contains("ADDED"), "got: " + diffs.get(0)); + } + + // --- areSameDateTime (tested indirectly via diffNodes) --- + + @Test + void diffNodes_equivalentDateTimes_noDiff() throws Exception { + // "+0000" vs "Z" — same instant, different format + JsonNode expected = MAPPER.readTree("{\"t\":\"2020-03-12T19:03:58.000+0000\"}"); + JsonNode actual = MAPPER.readTree("{\"t\":\"2020-03-12T19:03:58.000Z\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertTrue(diffs.isEmpty(), "Expected no diffs for equivalent datetimes, got: " + diffs); + } + + @Test + void diffNodes_differentDateTimes_hasDiff() throws Exception { + JsonNode expected = MAPPER.readTree("{\"t\":\"2020-03-12T19:03:58.000Z\"}"); + JsonNode actual = MAPPER.readTree("{\"t\":\"2021-01-01T00:00:00.000Z\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + } + + @Test + void diffNodes_nonDateStrings_notTreatedAsDates() throws Exception { + JsonNode expected = MAPPER.readTree("{\"s\":\"hello\"}"); + JsonNode actual = MAPPER.readTree("{\"s\":\"world\"}"); + List diffs = new ArrayList<>(); + JsonNodeUtils.diffNodes("", expected, actual, diffs); + assertEquals(1, diffs.size()); + assertTrue(diffs.get(0).startsWith("CHANGED .s")); + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssertTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssertTest.java new file mode 100644 index 000000000..63067f8b2 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssertTest.java @@ -0,0 +1,71 @@ +package com.amazonaws.services.lambda.runtime.tests; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class LambdaEventAssertTest { + /** + * Demonstrates the completeness check: the fixture has a field + * ({@code unknownField}) that {@code PartialPojo} does not capture, + * so it gets silently dropped during deserialization. + */ + @Test + void shouldFailWhenFieldIsDropped() { + AssertionError error = assertThrows(AssertionError.class, + () -> LambdaEventAssert.assertSerializationRoundTrip( + "partial_pojo.json", PartialPojo.class)); + + assertTrue(error.getMessage().contains("PartialPojo"), + "Error message should name the failing class"); + } + + /** + * Demonstrates the stability check: the getter mutates state on each + * call, so the first and second round-trips produce different JSON. + */ + @Test + void shouldFailWhenSerializationIsUnstable() { + AssertionError error = assertThrows(AssertionError.class, + () -> LambdaEventAssert.assertSerializationRoundTrip( + "unstable_pojo.json", UnstablePojo.class)); + + assertTrue(error.getMessage().contains("UnstablePojo"), + "Error message should name the failing class"); + } + + /** POJO that only captures {@code name}, silently dropping any other fields. */ + public static class PartialPojo { + private String name; + + public PartialPojo() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + /** + * POJO with a getter that appends a suffix, making serialization + * non-idempotent. + */ + public static class UnstablePojo { + private String name; + + public UnstablePojo() { + } + + public String getName() { + return name == null ? null : name + "_x"; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/ResponseEventSerializationRoundTripTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/ResponseEventSerializationRoundTripTest.java new file mode 100644 index 000000000..e700b0d01 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/ResponseEventSerializationRoundTripTest.java @@ -0,0 +1,59 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.services.lambda.runtime.events.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +/** + * Verifies serialization round-trip fidelity for Lambda response event types. + * + *

Response events are POJOs that Lambda functions return to the RIC, which + * serializes them to JSON via {@code EventHandlerLoader.getSerializer()}. + * None of these types are registered in {@code SUPPORTED_EVENTS}, so they + * go through the bare Jackson path ({@code JacksonFactory.getInstance() + * .getSerializer(type)}).

+ * + *

Although the RIC only calls {@code toJson()} on response types (never + * {@code fromJson()}), the round-trip test is a stricter check: if a response + * type survives JSON → POJO → JSON → POJO → JSON, it + * certainly survives the production POJO → JSON path.

+ * + * @see SerializationRoundTripTest for registered input events + * @see UnregisteredEventSerializationRoundTripTest for unregistered input events + */ +@SuppressWarnings("deprecation") // APIGatewayV2ProxyResponseEvent is deprecated +public class ResponseEventSerializationRoundTripTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("passingCases") + void roundTrip(String displayName, String fixture, Class eventClass) { + LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass); + } + + private static Stream passingCases() { + return Stream.of( + // API Gateway responses + args(APIGatewayProxyResponseEvent.class, "response/apigw_proxy_response.json"), + args(APIGatewayV2HTTPResponse.class, "response/apigw_v2_http_response.json"), + args(APIGatewayV2WebSocketResponse.class, "response/apigw_v2_websocket_response.json"), + args(APIGatewayV2ProxyResponseEvent.class, "response/apigw_v2_websocket_response.json"), + // ALB response + args(ApplicationLoadBalancerResponseEvent.class, "response/alb_response.json"), + // S3 Batch response + args(S3BatchResponse.class, "response/s3_batch_response.json"), + // SQS Batch response + args(SQSBatchResponse.class, "response/sqs_batch_response.json"), + // Simple IAM Policy response (HTTP API) + args(SimpleIAMPolicyResponse.class, "response/simple_iam_policy_response.json"), + // MSK Firehose response + args(MSKFirehoseResponse.class, "response/msk_firehose_response.json")); + } + + private static Arguments args(Class clazz, String fixture) { + return Arguments.of(clazz.getSimpleName(), fixture, clazz); + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/SerializationRoundTripTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/SerializationRoundTripTest.java new file mode 100644 index 000000000..3eab8fec0 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/SerializationRoundTripTest.java @@ -0,0 +1,108 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.services.lambda.runtime.events.*; +import com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Verifies serialization round-trip fidelity for events that are registered in + * {@code LambdaEventSerializers.SUPPORTED_EVENTS}. + * + *

Registered events go through the full customized serialization path in the + * Runtime Interface Client (RIC): {@code EventHandlerLoader.getSerializer()} + * detects them via {@code isLambdaSupportedEvent()} and delegates to + * {@code LambdaEventSerializers.serializerFor()}, which applies Jackson mixins, + * {@code DateModule}/{@code DateTimeModule}, and naming strategies.

+ * + *

Each case feeds a JSON fixture through + * {@link LambdaEventAssert#assertSerializationRoundTrip} which performs two + * consecutive round-trips and compares the original JSON tree against the + * final output.

+ * + * @see UnregisteredEventSerializationRoundTripTest for events not in SUPPORTED_EVENTS + */ +public class SerializationRoundTripTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("passingCases") + void roundTrip(String displayName, String fixture, Class eventClass) { + LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass); + } + + @ParameterizedTest(name = "{0} (known failure)") + @MethodSource("knownFailureCases") + void roundTripKnownFailures(String displayName, String fixture, Class eventClass) { + assertThrows(Throwable.class, + () -> LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass), + displayName + " was expected to fail but passed — move it to passingCases()"); + } + + private static Stream passingCases() { + return Stream.of( + args(CloudFormationCustomResourceEvent.class, "cloudformation_event.json"), + args(CloudWatchLogsEvent.class, "cloudwatchlogs_event.json"), + args(CodeCommitEvent.class, "codecommit_event.json"), + args(ConfigEvent.class, "config_event.json"), + args(DynamodbEvent.class, "ddb/dynamo_event_roundtrip.json"), + args(KinesisEvent.class, "kinesis/kinesis_event_roundtrip.json"), + args(KinesisFirehoseEvent.class, "firehose_event.json"), + args(LambdaDestinationEvent.class, "lambda_destination_event.json"), + args(ScheduledEvent.class, "cloudwatch_event.json"), + args(SecretsManagerRotationEvent.class, "secrets_rotation_event.json"), + args(SNSEvent.class, "sns_event.json"), + args(LexEvent.class, "lex_event_roundtrip.json"), + args(ConnectEvent.class, "connect_event.json"), + args(SQSEvent.class, "sqs/sqs_event_nobody.json"), + args(APIGatewayProxyRequestEvent.class, "apigw_rest_event.json"), + args(CloudFrontEvent.class, "cloudfront_event.json"), + args(S3Event.class, "s3_event.json"), + args(S3EventNotification.class, "s3_event.json"), + args(APIGatewayV2HTTPEvent.class, "apigw_http_event.json"), + args(APIGatewayCustomAuthorizerEvent.class, "apigw_auth.json"), + args(ApplicationLoadBalancerRequestEvent.class, "elb_event.json"), + args(CloudWatchCompositeAlarmEvent.class, "cloudwatch_composite_alarm.json"), + args(CloudWatchMetricAlarmEvent.class, "cloudwatch_metric_alarm.json"), + args(CognitoUserPoolPreTokenGenerationEventV2.class, "cognito_user_pool_pre_token_generation_event_v2.json"), + args(KafkaEvent.class, "kafka_event_roundtrip.json"), + args(MSKFirehoseEvent.class, "msk_firehose_event_roundtrip.json"), + args(RabbitMQEvent.class, "rabbitmq_event_roundtrip.json"), + args(S3BatchEventV2.class, "s3_batch_event_v2.json"), + args(IoTButtonEvent.class, "iot_button_event.json"), + args(CognitoEvent.class, "cognito_sync_event.json"), + args(DynamodbTimeWindowEvent.class, "ddb/dynamo_time_window_event.json"), + args(KinesisTimeWindowEvent.class, "kinesis/kinesis_time_window_event.json")); + } + + private static Stream knownFailureCases() { + return Stream.of( + // APIGatewayV2CustomAuthorizerEvent has two serialization issues: + // 1. getTime() parses the raw string "12/Mar/2020:19:03:58 +0000" into a + // DateTime via dd/MMM/yyyy formatter. Jackson serializes as ISO-8601, but + // the formatter cannot parse ISO-8601 back on the second round-trip. + // The time field is effectively mandatory (getTime() throws NPE if null), + // and the date format change is inherent to how the serialization works. + // 2. getTimeEpoch() converts long to Instant; Jackson serializes as decimal + // seconds (e.g. 1583348638.390000000) instead of the original long. + // Both transformations are lossy; coercion captured in EventLoaderTest. + args(APIGatewayV2CustomAuthorizerEvent.class, "apigw_auth_v2.json"), + // ActiveMQEvent has one serialization issue: + // Destination.physicalName (camelCase) vs JSON "physicalname" (lowercase) — + // ACCEPT_CASE_INSENSITIVE_PROPERTIES is disabled in JacksonFactory so the + // field is silently dropped during deserialization. + // Fix: create an ActiveMQEventMixin with a DestinationMixin that maps + // @JsonProperty("physicalname") to getPhysicalName()/setPhysicalName(), + // then register it in LambdaEventSerializers MIXIN_MAP and NESTED_CLASS_MAP. + args(ActiveMQEvent.class, "mq_event.json")); + } + + private static Arguments args(Class clazz, String fixture) { + return Arguments.of(clazz.getSimpleName(), fixture, clazz); + } +} diff --git a/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/UnregisteredEventSerializationRoundTripTest.java b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/UnregisteredEventSerializationRoundTripTest.java new file mode 100644 index 000000000..12ad818e4 --- /dev/null +++ b/aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/UnregisteredEventSerializationRoundTripTest.java @@ -0,0 +1,90 @@ +/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */ +package com.amazonaws.services.lambda.runtime.tests; + +import com.amazonaws.services.lambda.runtime.events.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Verifies serialization round-trip fidelity for events that are NOT registered + * in {@code LambdaEventSerializers.SUPPORTED_EVENTS}. + * + *

In the Runtime Interface Client (RIC), when a handler's event type is not + * in {@code SUPPORTED_EVENTS}, {@code EventHandlerLoader.getSerializer()} falls + * through to {@code JacksonFactory.getInstance().getSerializer(type)} — a bare + * {@code PojoSerializer} backed by Jackson without any mixins or naming + * strategies. However, {@code LambdaEventSerializers.serializerFor()} (used by + * this test) unconditionally registers {@code DateModule} and + * {@code DateTimeModule}, so Joda/java.time types are still handled. For most + * unregistered events this makes no practical difference because they don't + * contain Joda DateTime fields.

+ * + * @see SerializationRoundTripTest for events registered in SUPPORTED_EVENTS + */ +@SuppressWarnings("deprecation") // APIGatewayV2ProxyRequestEvent is deprecated +public class UnregisteredEventSerializationRoundTripTest { + + @ParameterizedTest(name = "{0}") + @MethodSource("passingCases") + void roundTrip(String displayName, String fixture, Class eventClass) { + LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass); + } + + @ParameterizedTest(name = "{0} (known failure)") + @MethodSource("knownFailureCases") + void roundTripKnownFailures(String displayName, String fixture, Class eventClass) { + assertThrows(Throwable.class, + () -> LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass), + displayName + " was expected to fail but passed — move it to passingCases()"); + } + + private static Stream passingCases() { + return Stream.of( + // S3 Batch + args(S3BatchEvent.class, "s3_batch_event.json"), + // AppSync + args(AppSyncLambdaAuthorizerEvent.class, "appsync_authorizer_event.json"), + args(AppSyncLambdaAuthorizerResponse.class, "appsync_authorizer_response.json"), + // TimeWindow response + args(TimeWindowEventResponse.class, "time_window_event_response.json"), + // Cognito UserPool triggers + args(CognitoUserPoolPreSignUpEvent.class, "cognito/cognito_userpool_presignup.json"), + args(CognitoUserPoolPostConfirmationEvent.class, "cognito/cognito_userpool_postconfirmation.json"), + args(CognitoUserPoolPreAuthenticationEvent.class, "cognito/cognito_userpool_preauthentication.json"), + args(CognitoUserPoolPostAuthenticationEvent.class, "cognito/cognito_userpool_postauthentication.json"), + args(CognitoUserPoolDefineAuthChallengeEvent.class, "cognito/cognito_userpool_define_auth_challenge.json"), + args(CognitoUserPoolCreateAuthChallengeEvent.class, "cognito/cognito_userpool_create_auth_challenge.json"), + args(CognitoUserPoolVerifyAuthChallengeResponseEvent.class, "cognito/cognito_userpool_verify_auth_challenge.json"), + args(CognitoUserPoolMigrateUserEvent.class, "cognito/cognito_userpool_migrate_user.json"), + args(CognitoUserPoolCustomMessageEvent.class, "cognito/cognito_userpool_custom_message.json"), + args(CognitoUserPoolPreTokenGenerationEvent.class, "cognito/cognito_userpool_pre_token_generation.json"), + // Kinesis Analytics + args(KinesisAnalyticsFirehoseInputPreprocessingEvent.class, "kinesis/kinesis_analytics_firehose_input_preprocessing.json"), + args(KinesisAnalyticsStreamsInputPreprocessingEvent.class, "kinesis/kinesis_analytics_streams_input_preprocessing.json"), + args(KinesisAnalyticsInputPreprocessingResponse.class, "kinesis/kinesis_analytics_input_preprocessing_response.json"), + args(KinesisAnalyticsOutputDeliveryEvent.class, "kinesis/kinesis_analytics_output_delivery.json"), + args(KinesisAnalyticsOutputDeliveryResponse.class, "kinesis/kinesis_analytics_output_delivery_response.json"), + // API Gateway V2 WebSocket + args(APIGatewayV2WebSocketEvent.class, "apigw_websocket_event.json"), + args(APIGatewayV2ProxyRequestEvent.class, "apigw_websocket_event.json")); + } + + private static Stream knownFailureCases() { + return Stream.of( + // S3ObjectLambdaEvent: Lombok generates getXAmzRequestId() for field + // "xAmzRequestId". With USE_STD_BEAN_NAMING, Jackson derives the property + // name as "XAmzRequestId" (capital X), so the original "xAmzRequestId" key + // is silently dropped during deserialization. + // Fix: add @JsonProperty("xAmzRequestId") on the field or getter. + args(S3ObjectLambdaEvent.class, "s3_object_lambda_event.json")); + } + + private static Arguments args(Class clazz, String fixture) { + return Arguments.of(clazz.getSimpleName(), fixture, clazz); + } +} diff --git a/aws-lambda-java-tests/src/test/resources/apigw_http_event.json b/aws-lambda-java-tests/src/test/resources/apigw_http_event.json index 88f4e5b4b..91954656c 100644 --- a/aws-lambda-java-tests/src/test/resources/apigw_http_event.json +++ b/aws-lambda-java-tests/src/test/resources/apigw_http_event.json @@ -18,18 +18,6 @@ "requestContext": { "accountId": "123456789012", "apiId": "api-id", - "authentication": { - "clientCert": { - "clientCertPem": "CERT_CONTENT", - "subjectDN": "www.example.com", - "issuerDN": "Example issuer", - "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", - "validity": { - "notBefore": "May 28 12:30:02 2019 GMT", - "notAfter": "Aug 5 09:36:04 2021 GMT" - } - } - }, "authorizer": { "jwt": { "claims": { diff --git a/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json b/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json index 28f10c221..a139ccbe8 100644 --- a/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json +++ b/aws-lambda-java-tests/src/test/resources/apigw_rest_event.json @@ -14,19 +14,22 @@ "Header2": [ "value1", "value2" + ], + "Header3": [ + "value1,value2" ] }, "queryStringParameters": { "parameter1": "value1", - "parameter2": "value" + "parameter2": "value2" }, "multiValueQueryStringParameters": { "parameter1": [ - "value1", - "value2" + "value1" ], "parameter2": [ - "value" + "value1", + "value2" ] }, "requestContext": { @@ -52,17 +55,7 @@ "sourceIp": "IP", "user": null, "userAgent": "user-agent", - "userArn": null, - "clientCert": { - "clientCertPem": "CERT_CONTENT", - "subjectDN": "www.example.com", - "issuerDN": "Example issuer", - "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", - "validity": { - "notBefore": "May 28 12:30:02 2019 GMT", - "notAfter": "Aug 5 09:36:04 2021 GMT" - } - } + "userArn": null }, "path": "/my/path", "protocol": "HTTP/1.1", @@ -76,5 +69,5 @@ "pathParameters": null, "stageVariables": null, "body": "Hello from Lambda!", - "isBase64Encoded": true + "isBase64Encoded": false } \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/apigw_websocket_event.json b/aws-lambda-java-tests/src/test/resources/apigw_websocket_event.json new file mode 100644 index 000000000..a47fc3a94 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/apigw_websocket_event.json @@ -0,0 +1,88 @@ +{ + "resource": "/", + "path": "/", + "httpMethod": "GET", + "headers": { + "Host": "abcdef1234.execute-api.us-east-1.amazonaws.com", + "Sec-WebSocket-Extensions": "permessage-deflate", + "Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==", + "Sec-WebSocket-Version": "13", + "X-Forwarded-For": "192.0.2.1", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "multiValueHeaders": { + "Host": [ + "abcdef1234.execute-api.us-east-1.amazonaws.com" + ], + "Sec-WebSocket-Extensions": [ + "permessage-deflate" + ], + "Sec-WebSocket-Key": [ + "dGhlIHNhbXBsZSBub25jZQ==" + ], + "Sec-WebSocket-Version": [ + "13" + ], + "X-Forwarded-For": [ + "192.0.2.1" + ], + "X-Forwarded-Port": [ + "443" + ], + "X-Forwarded-Proto": [ + "https" + ] + }, + "queryStringParameters": { + "param1": "value1" + }, + "multiValueQueryStringParameters": { + "param1": [ + "value1" + ] + }, + "pathParameters": { + "proxy": "path/to/resource" + }, + "stageVariables": { + "stageVar1": "value1" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "abcdef", + "stage": "prod", + "requestId": "abc-def-ghi", + "identity": { + "cognitoIdentityPoolId": "us-east-1:id-pool", + "accountId": "123456789012", + "cognitoIdentityId": "us-east-1:identity-id", + "caller": "caller-id", + "apiKey": "api-key-id", + "sourceIp": "192.0.2.1", + "cognitoAuthenticationType": "authenticated", + "cognitoAuthenticationProvider": "provider", + "userArn": "arn:aws:iam::123456789012:user/testuser", + "userAgent": "Mozilla/5.0", + "user": "testuser", + "accessKey": "AKIAIOSFODNN7EXAMPLE" + }, + "resourcePath": "/", + "httpMethod": "GET", + "apiId": "abcdef1234", + "connectedAt": 1583348638390, + "connectionId": "abc123=", + "domainName": "abcdef1234.execute-api.us-east-1.amazonaws.com", + "eventType": "CONNECT", + "extendedRequestId": "abc123=", + "integrationLatency": "100", + "messageDirection": "IN", + "messageId": "msg-001", + "requestTime": "09/Apr/2020:18:03:58 +0000", + "requestTimeEpoch": 1583348638390, + "routeKey": "$connect", + "status": "200" + }, + "body": "request body", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/appsync_authorizer_event.json b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_event.json new file mode 100644 index 000000000..494a500f3 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_event.json @@ -0,0 +1,15 @@ +{ + "authorizationToken": "BE9DC5E3-D410-4733-AF76-70178092E681", + "requestContext": { + "apiId": "giy7kumfmvcqvbedntjwjvagii", + "accountId": "254688921111", + "requestId": "b80ed838-14c6-4500-b4c3-b694c7bef086", + "queryDocument": "mutation MyNewTask($desc: String!) {\n createTask(description: $desc) {\n id\n }\n}\n", + "operationName": "MyNewTask", + "variables": {} + }, + "requestHeaders": { + "host": "giy7kumfmvcqvbedntjwjvagii.appsync-api.us-east-1.amazonaws.com", + "content-type": "application/json" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/appsync_authorizer_response.json b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_response.json new file mode 100644 index 000000000..1216ad51d --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/appsync_authorizer_response.json @@ -0,0 +1,11 @@ +{ + "isAuthorized": true, + "resolverContext": { + "name": "Foo Man", + "balance": "100" + }, + "deniedFields": [ + "Mutation.createEvent" + ], + "ttlOverride": 15 +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cloudfront_event.json b/aws-lambda-java-tests/src/test/resources/cloudfront_event.json index 7485310e9..bf4625d06 100644 --- a/aws-lambda-java-tests/src/test/resources/cloudfront_event.json +++ b/aws-lambda-java-tests/src/test/resources/cloudfront_event.json @@ -7,7 +7,6 @@ }, "request": { "uri": "/test", - "querystring": "auth=test&foo=bar", "method": "GET", "clientIp": "2001:cdba::3257:9652", "headers": { diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_create_auth_challenge.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_create_auth_challenge.json new file mode 100644 index 000000000..495b41475 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_create_auth_challenge.json @@ -0,0 +1,37 @@ +{ + "version": "1", + "triggerSource": "CreateAuthChallenge_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "challengeName": "CUSTOM_CHALLENGE", + "session": [ + { + "challengeName": "PASSWORD_VERIFIER", + "challengeResult": true, + "challengeMetadata": "metadata1" + } + ], + "userNotFound": false + }, + "response": { + "publicChallengeParameters": { + "captchaUrl": "url/123.jpg" + }, + "privateChallengeParameters": { + "answer": "5" + }, + "challengeMetadata": "CAPTCHA_CHALLENGE" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_custom_message.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_custom_message.json new file mode 100644 index 000000000..aa7a53a83 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_custom_message.json @@ -0,0 +1,28 @@ +{ + "version": "1", + "triggerSource": "CustomMessage_SignUp", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com", + "phone_number_verified": "true", + "email_verified": "true" + }, + "clientMetadata": { + "meta1": "value1" + }, + "codeParameter": "####", + "usernameParameter": "testuser" + }, + "response": { + "smsMessage": "Your code is ####", + "emailMessage": "Your code is ####", + "emailSubject": "Welcome" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_define_auth_challenge.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_define_auth_challenge.json new file mode 100644 index 000000000..e320b71d1 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_define_auth_challenge.json @@ -0,0 +1,32 @@ +{ + "version": "1", + "triggerSource": "DefineAuthChallenge_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "session": [ + { + "challengeName": "PASSWORD_VERIFIER", + "challengeResult": true, + "challengeMetadata": "metadata1" + } + ], + "userNotFound": false + }, + "response": { + "challengeName": "CUSTOM_CHALLENGE", + "issueTokens": false, + "failAuthentication": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_migrate_user.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_migrate_user.json new file mode 100644 index 000000000..2897ae063 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_migrate_user.json @@ -0,0 +1,35 @@ +{ + "version": "1", + "triggerSource": "UserMigration_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "validationData": { + "key1": "val1" + }, + "clientMetadata": { + "meta1": "value1" + }, + "userName": "testuser", + "password": "test-password" + }, + "response": { + "userAttributes": { + "email": "user@example.com" + }, + "finalUserStatus": "CONFIRMED", + "messageAction": "SUPPRESS", + "desiredDeliveryMediums": [ + "EMAIL" + ], + "forceAliasCreation": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postauthentication.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postauthentication.json new file mode 100644 index 000000000..f41084d54 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postauthentication.json @@ -0,0 +1,20 @@ +{ + "version": "1", + "triggerSource": "PostAuthentication_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "newDeviceUsed": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postconfirmation.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postconfirmation.json new file mode 100644 index 000000000..ecf63c7d3 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_postconfirmation.json @@ -0,0 +1,20 @@ +{ + "version": "1", + "triggerSource": "PostConfirmation_ConfirmSignUp", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com", + "email_verified": "true" + }, + "clientMetadata": { + "meta1": "value1" + } + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_pre_token_generation.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_pre_token_generation.json new file mode 100644 index 000000000..f81ffb902 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_pre_token_generation.json @@ -0,0 +1,48 @@ +{ + "version": "1", + "triggerSource": "TokenGeneration_HostedAuth", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "groupConfiguration": { + "groupsToOverride": [ + "group1", + "group2" + ], + "iamRolesToOverride": [ + "arn:aws:iam::123456789012:role/role1" + ], + "preferredRole": "arn:aws:iam::123456789012:role/role1" + } + }, + "response": { + "claimsOverrideDetails": { + "claimsToAddOrOverride": { + "custom:myattr": "myvalue" + }, + "claimsToSuppress": [ + "email" + ], + "groupOverrideDetails": { + "groupsToOverride": [ + "group1" + ], + "iamRolesToOverride": [ + "arn:aws:iam::123456789012:role/role1" + ], + "preferredRole": "arn:aws:iam::123456789012:role/role1" + } + } + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_preauthentication.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_preauthentication.json new file mode 100644 index 000000000..1402c4684 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_preauthentication.json @@ -0,0 +1,20 @@ +{ + "version": "1", + "triggerSource": "PreAuthentication_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "validationData": { + "key1": "val1" + }, + "userNotFound": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_presignup.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_presignup.json new file mode 100644 index 000000000..0d1f0936a --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_presignup.json @@ -0,0 +1,27 @@ +{ + "version": "1", + "triggerSource": "PreSignUp_SignUp", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "validationData": { + "key1": "val1" + }, + "clientMetadata": { + "meta1": "value1" + } + }, + "response": { + "autoConfirmUser": false, + "autoVerifyPhone": false, + "autoVerifyEmail": false + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_verify_auth_challenge.json b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_verify_auth_challenge.json new file mode 100644 index 000000000..ef14c4ddf --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito/cognito_userpool_verify_auth_challenge.json @@ -0,0 +1,27 @@ +{ + "version": "1", + "triggerSource": "VerifyAuthChallengeResponse_Authentication", + "region": "us-east-1", + "userPoolId": "us-east-1_uPoolId", + "userName": "testuser", + "callerContext": { + "awsSdkVersion": "2.0.0", + "clientId": "abcdefg1234567" + }, + "request": { + "userAttributes": { + "email": "user@example.com" + }, + "clientMetadata": { + "meta1": "value1" + }, + "privateChallengeParameters": { + "answer": "5" + }, + "challengeAnswer": "5", + "userNotFound": false + }, + "response": { + "answerCorrect": true + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/cognito_sync_event.json b/aws-lambda-java-tests/src/test/resources/cognito_sync_event.json new file mode 100644 index 000000000..6edf1c246 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/cognito_sync_event.json @@ -0,0 +1,20 @@ +{ + "version": 2, + "eventType": "SyncTrigger", + "region": "us-east-1", + "identityPoolId": "us-east-1:example-identity-pool-id", + "identityId": "us-east-1:example-identity-id", + "datasetName": "SampleDataset", + "datasetRecords": { + "SampleKey1": { + "oldValue": "oldValue1", + "newValue": "newValue1", + "op": "replace" + }, + "SampleKey2": { + "oldValue": "oldValue2", + "newValue": "newValue2", + "op": "replace" + } + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/connect_event.json b/aws-lambda-java-tests/src/test/resources/connect_event.json index b71bf6692..4ce17a657 100644 --- a/aws-lambda-java-tests/src/test/resources/connect_event.json +++ b/aws-lambda-java-tests/src/test/resources/connect_event.json @@ -12,15 +12,6 @@ "InitialContactId": "6ca32fbd-8f92-46af-92a5-6b0f970f0efe", "InitiationMethod": "API", "InstanceARN": "arn:aws:connect:eu-central-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa", - "MediaStreams": { - "Customer": { - "Audio": { - "StartFragmentNumber": "91343852333181432392682062622220590765191907586", - "StartTimestamp": "1565781909613", - "StreamARN": "arn:aws:kinesisvideo:eu-central-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806" - } - } - }, "PreviousContactId": "4ca32fbd-8f92-46af-92a5-6b0f970f0efe", "Queue": { "Name": "SampleQueue", diff --git a/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event_roundtrip.json new file mode 100644 index 000000000..10d963c3c --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_event_roundtrip.json @@ -0,0 +1,97 @@ +{ + "Records": [ + { + "eventID": "c4ca4238a0b923820dcc509a6f75849b", + "eventName": "INSERT", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1.4285376E9, + "SequenceNumber": "4421584500000000017450439091", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "userIdentity": { + "principalId": "dynamodb.amazonaws.com", + "type": "Service" + } + }, + { + "eventID": "c81e728d9d4c2f636f067f89cc14862c", + "eventName": "MODIFY", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1.635734407123E9, + "SequenceNumber": "4421584500000000017450439092", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "eccbc87e4b5ce2fe28308fd9f2a7baf3", + "eventName": "REMOVE", + "eventVersion": "1.1", + "eventSource": "aws:dynamodb", + "awsRegion": "eu-central-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "ApproximateCreationDateTime": 1.4285376E9, + "SequenceNumber": "4421584500000000017450439093", + "SizeBytes": 38, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:eu-central-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/ddb/dynamo_time_window_event.json b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_time_window_event.json new file mode 100644 index 000000000..d931acb80 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/ddb/dynamo_time_window_event.json @@ -0,0 +1,75 @@ +{ + "Records": [ + { + "eventID": "1", + "eventName": "INSERT", + "eventVersion": "1.0", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "SequenceNumber": "111", + "SizeBytes": 26, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + }, + { + "eventID": "2", + "eventName": "MODIFY", + "eventVersion": "1.0", + "eventSource": "aws:dynamodb", + "awsRegion": "us-east-1", + "dynamodb": { + "Keys": { + "Id": { + "N": "101" + } + }, + "NewImage": { + "Message": { + "S": "This item has changed" + }, + "Id": { + "N": "101" + } + }, + "OldImage": { + "Message": { + "S": "New item!" + }, + "Id": { + "N": "101" + } + }, + "SequenceNumber": "222", + "SizeBytes": 59, + "StreamViewType": "NEW_AND_OLD_IMAGES" + }, + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899" + } + ], + "window": { + "start": "2020-07-30T17:00:00Z", + "end": "2020-07-30T17:05:00Z" + }, + "state": { + "1": "state1" + }, + "shardId": "shard123456789", + "eventSourceARN": "arn:aws:dynamodb:us-east-1:123456789012:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899", + "isFinalInvokeForWindow": false, + "isWindowTerminatedEarly": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/iot_button_event.json b/aws-lambda-java-tests/src/test/resources/iot_button_event.json new file mode 100644 index 000000000..8dc82826b --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/iot_button_event.json @@ -0,0 +1,5 @@ +{ + "serialNumber": "G030JF055364XVRB", + "clickType": "SINGLE", + "batteryVoltage": "2000mV" +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kafka_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/kafka_event_roundtrip.json new file mode 100644 index 000000000..d9f682e5f --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kafka_event_roundtrip.json @@ -0,0 +1,22 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-east-1:123456789012:cluster/vpc-3432434/4834-3547-3455-9872-7929", + "bootstrapServers": "b-2.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092,b-1.demo-cluster-1.a1bcde.c1.kafka.us-east-1.amazonaws.com:9092", + "records": { + "mytopic-01": [ + { + "topic": "mytopic", + "partition": 0, + "offset": 15, + "timestamp": 1596480920837, + "timestampType": "CREATE_TIME", + "value": "SGVsbG8gZnJvbSBLYWZrYSAhIQ==", + "headers": [ + { + "headerKey": "aGVhZGVyVmFsdWU=" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_firehose_input_preprocessing.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_firehose_input_preprocessing.json new file mode 100644 index 000000000..8c6cfe514 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_firehose_input_preprocessing.json @@ -0,0 +1,14 @@ +{ + "invocationId": "invocationIdExample", + "applicationArn": "arn:aws:kinesisanalytics:us-east-1:123456789012:application/my-app", + "streamArn": "arn:aws:firehose:us-east-1:123456789012:deliverystream/my-stream", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "kinesisFirehoseRecordMetadata": { + "approximateArrivalTimestamp": 1583348638390 + }, + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_input_preprocessing_response.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_input_preprocessing_response.json new file mode 100644 index 000000000..f5be190ec --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_input_preprocessing_response.json @@ -0,0 +1,9 @@ +{ + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "result": "Ok", + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery.json new file mode 100644 index 000000000..573b6baba --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery.json @@ -0,0 +1,13 @@ +{ + "invocationId": "invocationIdExample", + "applicationArn": "arn:aws:kinesisanalytics:us-east-1:123456789012:application/my-app", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "lambdaDeliveryRecordMetadata": { + "retryHint": 0 + }, + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery_response.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery_response.json new file mode 100644 index 000000000..56ddaa194 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_output_delivery_response.json @@ -0,0 +1,8 @@ +{ + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "result": "Ok" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_streams_input_preprocessing.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_streams_input_preprocessing.json new file mode 100644 index 000000000..4ae8f3705 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_analytics_streams_input_preprocessing.json @@ -0,0 +1,17 @@ +{ + "invocationId": "invocationIdExample", + "applicationArn": "arn:aws:kinesisanalytics:us-east-1:123456789012:application/my-app", + "streamArn": "arn:aws:kinesis:us-east-1:123456789012:stream/my-stream", + "records": [ + { + "recordId": "49546986683135544286507457936321625675700192471156785154", + "kinesisStreamRecordMetadata": { + "sequenceNumber": "49546986683135544286507457936321625675700192471156785154", + "partitionKey": "partKey", + "shardId": "shardId-000000000000", + "approximateArrivalTimestamp": 1583348638390 + }, + "data": "SGVsbG8gV29ybGQ=" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_event_roundtrip.json new file mode 100644 index 000000000..e2081ef2b --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_event_roundtrip.json @@ -0,0 +1,21 @@ +{ + "Records": [ + { + "kinesis": { + "partitionKey": "partitionKey-03", + "kinesisSchemaVersion": "1.0", + "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0IDEyMy4=", + "sequenceNumber": "49545115243490985018280067714973144582180062593244200961", + "approximateArrivalTimestamp": 1.4285376E9, + "encryptionType": "NONE" + }, + "eventSource": "aws:kinesis", + "eventID": "shardId-000000000000:49545115243490985018280067714973144582180062593244200961", + "invokeIdentityArn": "arn:aws:iam::EXAMPLE", + "eventVersion": "1.0", + "eventName": "aws:kinesis:record", + "eventSourceARN": "arn:aws:kinesis:EXAMPLE", + "awsRegion": "eu-central-1" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_time_window_event.json b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_time_window_event.json new file mode 100644 index 000000000..2d6283c58 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/kinesis/kinesis_time_window_event.json @@ -0,0 +1,32 @@ +{ + "Records": [ + { + "kinesis": { + "kinesisSchemaVersion": "1.0", + "partitionKey": "1", + "sequenceNumber": "49590338271490256608559692538361571095921575989136588898", + "data": "SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==", + "approximateArrivalTimestamp": 1.607497475E9 + }, + "eventSource": "aws:kinesis", + "eventVersion": "1.0", + "eventID": "shardId-000000000006:49590338271490256608559692538361571095921575989136588898", + "eventName": "aws:kinesis:record", + "invokeIdentityArn": "arn:aws:iam::123456789012:role/lambda-kinesis-role", + "awsRegion": "us-east-1", + "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/lambda-stream" + } + ], + "window": { + "start": "2020-12-09T07:04:00Z", + "end": "2020-12-09T07:06:00Z" + }, + "state": { + "1": "282", + "2": "715" + }, + "shardId": "shardId-000000000006", + "eventSourceARN": "arn:aws:kinesis:us-east-1:123456789012:stream/lambda-stream", + "isFinalInvokeForWindow": false, + "isWindowTerminatedEarly": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/lex_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/lex_event_roundtrip.json new file mode 100644 index 000000000..be065eb3f --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/lex_event_roundtrip.json @@ -0,0 +1,24 @@ +{ + "messageVersion": "1.0", + "invocationSource": "DialogCodeHook", + "userId": "John", + "sessionAttributes": { + "key": "value" + }, + "bot": { + "name": "BookTrip", + "alias": "$LATEST", + "version": "$LATEST" + }, + "outputDialogMode": "Text", + "currentIntent": { + "name": "BookHotel", + "slots": { + "Location": "Chicago", + "CheckInDate": "2030-11-08", + "Nights": "4", + "RoomType": "queen" + }, + "confirmationStatus": "None" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/mq_event.json b/aws-lambda-java-tests/src/test/resources/mq_event.json index 6505a22d4..8b12af72e 100644 --- a/aws-lambda-java-tests/src/test/resources/mq_event.json +++ b/aws-lambda-java-tests/src/test/resources/mq_event.json @@ -5,15 +5,17 @@ { "messageID": "ID:b-9bcfa592-423a-4942-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1", "messageType": "jms/text-message", - "data": "QUJDOkFBQUE=", - "connectionId": "myJMSCoID", + "timestamp": 1598827811958, + "deliveryMode": 0, "redelivered": false, + "expiration": 0, + "priority": 0, + "data": "QUJDOkFBQUE=", + "brokerInTime": 1598827811958, + "brokerOutTime": 1598827811959, "destination": { "physicalname": "testQueue" }, - "timestamp": 1598827811958, - "brokerInTime": 1598827811958, - "brokerOutTime": 1598827811959, "properties": { "testKey": "testValue" } @@ -21,15 +23,17 @@ { "messageID": "ID:b-8bcfa572-428a-4642-879d-eb284b418fc8-1.mq.us-west-2.amazonaws.com-37557-1234520418293-4:1:1:1:1", "messageType": "jms/bytes-message", + "timestamp": 1598827811958, + "deliveryMode": 0, + "redelivered": false, + "expiration": 0, + "priority": 0, "data": "3DTOOW7crj51prgVLQaGQ82S48k=", - "connectionId": "myJMSCoID1", - "persistent": false, + "brokerInTime": 1598827811958, + "brokerOutTime": 1598827811959, "destination": { "physicalname": "testQueue" }, - "timestamp": 1598827811958, - "brokerInTime": 1598827811958, - "brokerOutTime": 1598827811959, "properties": { "testKey": "testValue" } diff --git a/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json b/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json index 6b839912d..140908250 100644 --- a/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json +++ b/aws-lambda-java-tests/src/test/resources/msk_firehose_event.json @@ -15,4 +15,4 @@ "kafkaRecordValue": "eyJOYW1lIjoiSGVsbG8gV29ybGQifQ==" } ] -} +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/msk_firehose_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/msk_firehose_event_roundtrip.json new file mode 100644 index 000000000..81b0a9c81 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/msk_firehose_event_roundtrip.json @@ -0,0 +1,18 @@ +{ + "invocationId": "12345621-4787-0000-a418-36e56Example", + "sourceMSKArn": "arn:aws:kafka:EXAMPLE", + "deliveryStreamArn": "arn:aws:firehose:EXAMPLE", + "region": "us-east-1", + "records": [ + { + "recordId": "00000000000000000000000000000000000000000000000000000000000000", + "approximateArrivalTimestamp": 1716369573887, + "mskRecordMetadata": { + "offset": "0", + "partitionId": "1", + "approximateArrivalTimestamp": "1716369573887" + }, + "kafkaRecordValue": "eyJOYW1lIjoiSGVsbG8gV29ybGQifQ==" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/partial_pojo.json b/aws-lambda-java-tests/src/test/resources/partial_pojo.json new file mode 100644 index 000000000..398218039 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/partial_pojo.json @@ -0,0 +1,4 @@ +{ + "name": "test", + "unknownField": "this will be dropped" +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/rabbitmq_event_roundtrip.json b/aws-lambda-java-tests/src/test/resources/rabbitmq_event_roundtrip.json new file mode 100644 index 000000000..44edf2f0a --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/rabbitmq_event_roundtrip.json @@ -0,0 +1,51 @@ +{ + "eventSource": "aws:rmq", + "eventSourceArn": "arn:aws:mq:us-west-2:112556298976:broker:test:b-9bcfa592-423a-4942-879d-eb284b418fc8", + "rmqMessagesByQueue": { + "test::/": [ + { + "basicProperties": { + "contentType": "text/plain", + "contentEncoding": null, + "headers": { + "header1": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 49 + ] + }, + "header2": { + "bytes": [ + 118, + 97, + 108, + 117, + 101, + 50 + ] + }, + "numberInHeader": 10 + }, + "deliveryMode": 1, + "priority": 34, + "correlationId": null, + "replyTo": null, + "expiration": 60000, + "messageId": null, + "timestamp": "Jan 1, 1970, 12:33:41 AM", + "type": null, + "userId": "AIDACKCEVSQ6C2EXAMPLE", + "appId": null, + "clusterId": null, + "bodySize": 80 + }, + "redelivered": false, + "data": "eyJ0aW1lb3V0IjowLCJkYXRhIjoiQ1pybWYwR3c4T3Y0YnFMUXhENEUifQ==" + } + ] + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/alb_response.json b/aws-lambda-java-tests/src/test/resources/response/alb_response.json new file mode 100644 index 000000000..355eb193a --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/alb_response.json @@ -0,0 +1,15 @@ +{ + "statusCode": 200, + "statusDescription": "200 OK", + "headers": { + "Content-Type": "text/html" + }, + "multiValueHeaders": { + "Set-Cookie": [ + "cookie1=value1", + "cookie2=value2" + ] + }, + "body": "Hello", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/apigw_proxy_response.json b/aws-lambda-java-tests/src/test/resources/response/apigw_proxy_response.json new file mode 100644 index 000000000..640ccdc5c --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/apigw_proxy_response.json @@ -0,0 +1,15 @@ +{ + "statusCode": 200, + "headers": { + "Content-Type": "application/json", + "X-Custom-Header": "custom-value" + }, + "multiValueHeaders": { + "Set-Cookie": [ + "cookie1=value1", + "cookie2=value2" + ] + }, + "body": "{\"message\":\"Hello from Lambda\"}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/apigw_v2_http_response.json b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_http_response.json new file mode 100644 index 000000000..c39236650 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_http_response.json @@ -0,0 +1,16 @@ +{ + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "multiValueHeaders": { + "Set-Cookie": [ + "cookie1=value1" + ] + }, + "cookies": [ + "session=abc123; Secure; HttpOnly" + ], + "body": "{\"message\":\"OK\"}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/apigw_v2_websocket_response.json b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_websocket_response.json new file mode 100644 index 000000000..08392e890 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/apigw_v2_websocket_response.json @@ -0,0 +1,14 @@ +{ + "statusCode": 200, + "headers": { + "Content-Type": "application/json" + }, + "multiValueHeaders": { + "X-Custom": [ + "val1", + "val2" + ] + }, + "body": "{\"action\":\"sendmessage\",\"data\":\"hello\"}", + "isBase64Encoded": false +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/msk_firehose_response.json b/aws-lambda-java-tests/src/test/resources/response/msk_firehose_response.json new file mode 100644 index 000000000..9ac497624 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/msk_firehose_response.json @@ -0,0 +1,9 @@ +{ + "records": [ + { + "recordId": "record-1", + "result": "Ok", + "kafkaRecordValue": "dHJhbnNmb3JtZWQgZGF0YQ==" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/s3_batch_response.json b/aws-lambda-java-tests/src/test/resources/response/s3_batch_response.json new file mode 100644 index 000000000..e63439d84 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/s3_batch_response.json @@ -0,0 +1,12 @@ +{ + "invocationSchemaVersion": "1.0", + "treatMissingKeysAs": "PermanentFailure", + "invocationId": "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo", + "results": [ + { + "taskId": "dGFza2lkZ29lc2hlcmUK", + "resultCode": "Succeeded", + "resultString": "Successfully processed" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/simple_iam_policy_response.json b/aws-lambda-java-tests/src/test/resources/response/simple_iam_policy_response.json new file mode 100644 index 000000000..5f23b6405 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/simple_iam_policy_response.json @@ -0,0 +1,7 @@ +{ + "isAuthorized": true, + "context": { + "userId": "user-123", + "scope": "read:all" + } +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/response/sqs_batch_response.json b/aws-lambda-java-tests/src/test/resources/response/sqs_batch_response.json new file mode 100644 index 000000000..5ef2de697 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/response/sqs_batch_response.json @@ -0,0 +1,7 @@ +{ + "batchItemFailures": [ + { + "itemIdentifier": "059f36b4-87a3-44ab-83d2-661975830a7d" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/s3_batch_event.json b/aws-lambda-java-tests/src/test/resources/s3_batch_event.json new file mode 100644 index 000000000..a70af0fdd --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/s3_batch_event.json @@ -0,0 +1,15 @@ +{ + "invocationSchemaVersion": "1.0", + "invocationId": "YXNkbGZqYWRmaiBhc2RmdW9hZHNmZGpmaGFzbGtkaGZza2RmaAo", + "job": { + "id": "f3cc4f60-61f6-4a2b-8a21-d07600c373ce" + }, + "tasks": [ + { + "taskId": "dGFza2lkZ29lc2hlcmUK", + "s3Key": "customerImage1.jpg", + "s3VersionId": "1", + "s3BucketArn": "arn:aws:s3:::amzn-s3-demo-bucket" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/s3_event.json b/aws-lambda-java-tests/src/test/resources/s3_event.json index 89e0bd312..73f59d072 100644 --- a/aws-lambda-java-tests/src/test/resources/s3_event.json +++ b/aws-lambda-java-tests/src/test/resources/s3_event.json @@ -28,8 +28,10 @@ }, "object": { "key": "test/key", + "urlDecodedKey": "test/key", "size": 1024, "eTag": "0123456789abcdef0123456789abcdef", + "versionId": "", "sequencer": "0A1B2C3D4E5F678901" } } diff --git a/aws-lambda-java-tests/src/test/resources/s3_object_lambda_event.json b/aws-lambda-java-tests/src/test/resources/s3_object_lambda_event.json new file mode 100644 index 000000000..db996e71c --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/s3_object_lambda_event.json @@ -0,0 +1,29 @@ +{ + "xAmzRequestId": "requestId", + "getObjectContext": { + "inputS3Url": "https://my-s3-ap-111122223333.s3-accesspoint.us-east-1.amazonaws.com/example?X-Amz-Security-Token=snip", + "outputRoute": "io-use1-001", + "outputToken": "OutputToken" + }, + "configuration": { + "accessPointArn": "arn:aws:s3-object-lambda:us-east-1:111122223333:accesspoint/example-object-lambda-ap", + "supportingAccessPointArn": "arn:aws:s3:us-east-1:111122223333:accesspoint/example-ap", + "payload": "{}" + }, + "userRequest": { + "url": "https://object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com/example", + "headers": { + "Host": "object-lambda-111122223333.s3-object-lambda.us-east-1.amazonaws.com", + "Accept-Encoding": "identity", + "X-Amz-Content-SHA256": "e3b0c44298fc1example" + } + }, + "userIdentity": { + "type": "AssumedRole", + "principalId": "principalId", + "arn": "arn:aws:sts::111122223333:assumed-role/Admin/example", + "accountId": "111122223333", + "accessKeyId": "accessKeyId" + }, + "protocolVersion": "1.00" +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/time_window_event_response.json b/aws-lambda-java-tests/src/test/resources/time_window_event_response.json new file mode 100644 index 000000000..3c77b3784 --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/time_window_event_response.json @@ -0,0 +1,10 @@ +{ + "state": { + "totalAmount": "500" + }, + "batchItemFailures": [ + { + "itemIdentifier": "49590338271490256608559692538361571095921575989136588898" + } + ] +} \ No newline at end of file diff --git a/aws-lambda-java-tests/src/test/resources/unstable_pojo.json b/aws-lambda-java-tests/src/test/resources/unstable_pojo.json new file mode 100644 index 000000000..19db9a1cc --- /dev/null +++ b/aws-lambda-java-tests/src/test/resources/unstable_pojo.json @@ -0,0 +1,3 @@ +{ + "name": "test" +} \ No newline at end of file