Skip to content

Add serialization round-trip tests for event types#598

Open
darklight3it wants to merge 15 commits intomainfrom
dmelfi/improve-serialization-testing
Open

Add serialization round-trip tests for event types#598
darklight3it wants to merge 15 commits intomainfrom
dmelfi/improve-serialization-testing

Conversation

@darklight3it
Copy link
Contributor

@darklight3it darklight3it commented Mar 25, 2026

Issue #, if available:

Related to #594

Description of changes:

Add a serialization round-trip test that catches data loss and non-idempotent serialization across the events library. Previously, EventLoaderTest covered 27 event types with field-by-field assertions after a single deserialization pass. The new round-trip tests cover 66 event types (61 passing + 5 known failures), bringing coverage from ~40% to ~97% of the 68 classes in aws-lambda-java-events.

How the test works

LambdaEventAssert.assertSerializationRoundTrip performs two consecutive round-trips (JSON → POJO → JSON → POJO → JSON) and compares the original JSON tree against the final output. Explicit nulls are stripped from the original since the serializer uses Include.NON_NULL. A JsonNodeUtils helper produces a structured diff listing every path that changed.

Two passes catch both silently dropped fields and unstable serialization (output that changes across round-trips).

The test is split into three parameterized classes: SerializationRoundTripTest (registered events), UnregisteredEventSerializationRoundTripTest (unregistered input events), and ResponseEventSerializationRoundTripTest (response types). Each has passingCases() for events that survive the round-trip cleanly, and knownFailureCases() for events expected to fail (wrapped in assertThrows). If a known failure suddenly passes, the test fails with a message to move it to passingCases().

Why this approach

Field-by-field assertions in EventLoaderTest only verify the fields the test author remembered to check. The round-trip approach is exhaustive: any field present in the JSON fixture that gets lost or mutated during serialization is caught automatically. This kind of broad tests are the ones needed when updating jackson.

Fixture corrections (false negatives in existing fixtures)

Several existing JSON fixtures contained fields that the event models silently drop during deserialization. These were false negatives — the fixtures looked complete but the data was being lost without any test catching it. Fixed by removing the unsupported fields from fixtures so the round-trip test has a clean baseline:

Event Missing Field(s) Root Cause
APIGatewayProxyRequestEvent requestContext.identity.clientCert RequestIdentity has no clientCert field
APIGatewayV2HTTPEvent requestContext.authentication RequestContext has no authentication field
CloudFrontEvent request.querystring (as string) Model uses Map<String, List<String>>, not String
ConnectEvent contactData.MediaStreams ContactData has no mediaStreams field
S3Event s3.object.urlDecodedKey, s3.object.versionId Fields exist in S3EventNotification but missing from S3ObjectEntity
ActiveMQEvent destination.physicalname Case mismatch: JSON "physicalname" vs getter getPhysicalName() — no @JsonProperty mapping

Events requiring separate round-trip fixtures

Some events needed dedicated _roundtrip.json fixtures because the original fixtures contain values that Jackson leniently coerces during deserialization (e.g., numbers to strings), making the output structurally different from the input:

Event Fixture Reason
DynamodbEvent dynamo_event_roundtrip.json approximateCreationDateTime epoch seconds → Date formatting
KinesisEvent kinesis_event_roundtrip.json approximateArrivalTimestamp epoch seconds → Date formatting
KafkaEvent kafka_event_roundtrip.json Numeric values in Map<String, String> coerced to strings
LexEvent lex_event_roundtrip.json slots typed as Map<String, String> — numeric "Nights" coerced
MSKFirehoseEvent msk_firehose_event_roundtrip.json Numeric metadata values coerced to strings
RabbitMQEvent rabbitmq_event_roundtrip.json expiration (String→int), timestamp (long→formatted string)

Known failures (3 events)

These five events fail the round-trip and are captured in knownFailureCases():

  1. APIGatewayV2CustomAuthorizerEventgetTime() parses "12/Mar/2020:19:03:58 +0000" into a Joda DateTime via a custom formatter. Jackson serializes it as ISO-8601, which the formatter can't parse 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 — not a bug, just a lossy transformation that prevents a clean round-trip.

  2. ActiveMQEvent (bug)Destination.physicalName (camelCase) vs JSON "physicalname" (lowercase). Since ACCEPT_CASE_INSENSITIVE_PROPERTIES is disabled in JacksonFactory, the field is silently dropped during deserialization. Fix: add a mixin with @JsonProperty("physicalname").

  3. S3ObjectLambdaEvent (bug) — 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.

Target (OCI, Managed Runtime, both):

Both

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Davide Melfi added 12 commits March 24, 2026 11:43
Add JSON test fixtures and round-trip test cases for CognitoEvent,
DynamodbTimeWindowEvent, IoTButtonEvent, and KinesisTimeWindowEvent.
These were the only events registered in LambdaEventSerializers
SUPPORTED_EVENTS that lacked test fixtures.

Fixtures based on official AWS Lambda documentation examples. Time
window event fixtures use round-trip-safe date formats to avoid
coercion issues.

Coverage: 32 passing + 2 known failures (34 total, up from 30).
Add UnregisteredEventSerializationRoundTripTest covering events not in
LambdaEventSerializers.SUPPORTED_EVENTS: 10 Cognito UserPool triggers,
5 Kinesis Analytics events, 2 API Gateway V2 WebSocket, 2 AppSync,
1 S3 Batch, and 1 TimeWindow response. S3ObjectLambdaEvent is a known
failure (Lombok xAmzRequestId naming issue).

Split SerializationRoundTripTest into registered (34 tests) and
unregistered (22 tests) for clarity. Total: 56 tests, 53 passing,
3 known failures.
Add ResponseEventSerializationRoundTripTest covering all 11 response
event types in aws-lambda-java-events. 9 pass cleanly, 2 are known
failures (IamPolicyResponse, IamPolicyResponseV1 — getPolicyDocument()
returns Map<String,Object> instead of the PolicyDocument POJO, making
round-trip impossible by design since these are serialize-only types).

Also update SerializationRoundTripTest comment for
APIGatewayV2CustomAuthorizerEvent to clarify the date format change
is a lossy transformation, not a bug.

Total: 69 tests (34 registered + 22 unregistered + 11 response + 2
LambdaEventAssertTest), all green. Coverage now 66/68 event classes
(97%).
@codecov
Copy link

codecov bot commented Mar 25, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 65.38%. Comparing base (09c4b4e) to head (27a1641).

Additional details and impacted files
@@            Coverage Diff            @@
##               main     #598   +/-   ##
=========================================
  Coverage     65.38%   65.38%           
  Complexity      211      211           
=========================================
  Files            34       34           
  Lines           988      988           
  Branches        142      142           
=========================================
  Hits            646      646           
  Misses          290      290           
  Partials         52       52           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@darklight3it darklight3it self-assigned this Mar 25, 2026
@darklight3it darklight3it marked this pull request as ready for review March 25, 2026 13:12
@darklight3it darklight3it marked this pull request as draft March 25, 2026 13:27
@darklight3it darklight3it marked this pull request as ready for review March 25, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant