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