Skip to content

Commit f031481

Browse files
author
Davide Melfi
committed
test: Add round-trip fixtures for 21 unregistered events
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.
1 parent 8c4052e commit f031481

24 files changed

+630
-9
lines changed

aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/LambdaEventAssert.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ class LambdaEventAssert {
3737
private static final ObjectMapper MAPPER = new ObjectMapper();
3838

3939
/**
40-
* Verifies that the given lambda event class can be deserialized from the named
41-
* classpath resource and re-serialized without data loss.
40+
* Round-trip using the registered {@link LambdaEventSerializers} path
41+
* (Jackson + mixins + DateModule + DateTimeModule + naming strategies).
4242
*
4343
* <p>
4444
* The check performs two consecutive round-trips
@@ -56,14 +56,13 @@ class LambdaEventAssert {
5656
* @throws AssertionError if the original and final JSON trees differ
5757
*/
5858
public static <T> void assertSerializationRoundTrip(String fileName, Class<T> targetClass) {
59+
PojoSerializer<T> serializer = LambdaEventSerializers.serializerFor(targetClass,
60+
ClassLoader.getSystemClassLoader());
5961

6062
if (!fileName.endsWith(".json")) {
6163
throw new IllegalArgumentException("File " + fileName + " must have json extension");
6264
}
6365

64-
PojoSerializer<T> serializer = LambdaEventSerializers.serializerFor(targetClass,
65-
ClassLoader.getSystemClassLoader());
66-
6766
byte[] originalBytes;
6867
try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName)) {
6968
if (stream == null) {

aws-lambda-java-tests/src/test/java/com/amazonaws/services/lambda/runtime/tests/SerializationRoundTripTest.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,24 @@
1212
import static org.junit.jupiter.api.Assertions.*;
1313

1414
/**
15-
* Verifies serialization round-trip fidelity for every Lambda-supported event
16-
* that has a test fixture. Each case feeds the JSON fixture through
15+
* Verifies serialization round-trip fidelity for events that are registered in
16+
* {@code LambdaEventSerializers.SUPPORTED_EVENTS}.
17+
*
18+
* <p>Registered events go through the full customized serialization path in the
19+
* Runtime Interface Client (RIC): {@code EventHandlerLoader.getSerializer()}
20+
* detects them via {@code isLambdaSupportedEvent()} and delegates to
21+
* {@code LambdaEventSerializers.serializerFor()}, which applies Jackson mixins,
22+
* {@code DateModule}/{@code DateTimeModule}, and naming strategies.</p>
23+
*
24+
* <p>Each case feeds a JSON fixture through
1725
* {@link LambdaEventAssert#assertSerializationRoundTrip} which performs two
1826
* consecutive round-trips and compares the original JSON tree against the
19-
* final output.
27+
* final output.</p>
28+
*
29+
* @see UnregisteredEventSerializationRoundTripTest for events not in SUPPORTED_EVENTS
2030
*/
2131
public class SerializationRoundTripTest {
2232

23-
2433
@ParameterizedTest(name = "{0}")
2534
@MethodSource("passingCases")
2635
void roundTrip(String displayName, String fixture, Class<?> eventClass) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
2+
package com.amazonaws.services.lambda.runtime.tests;
3+
4+
import com.amazonaws.services.lambda.runtime.events.*;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.Arguments;
7+
import org.junit.jupiter.params.provider.MethodSource;
8+
9+
import java.util.stream.Stream;
10+
11+
import static org.junit.jupiter.api.Assertions.*;
12+
13+
/**
14+
* Verifies serialization round-trip fidelity for events that are NOT registered
15+
* in {@code LambdaEventSerializers.SUPPORTED_EVENTS}.
16+
*
17+
* <p>In the Runtime Interface Client (RIC), when a handler's event type is not
18+
* in {@code SUPPORTED_EVENTS}, {@code EventHandlerLoader.getSerializer()} falls
19+
* through to {@code JacksonFactory.getInstance().getSerializer(type)} — a bare
20+
* {@code PojoSerializer} backed by Jackson without any mixins or naming
21+
* strategies. However, {@code LambdaEventSerializers.serializerFor()} (used by
22+
* this test) unconditionally registers {@code DateModule} and
23+
* {@code DateTimeModule}, so Joda/java.time types are still handled. For most
24+
* unregistered events this makes no practical difference because they don't
25+
* contain Joda DateTime fields.</p>
26+
*
27+
* @see SerializationRoundTripTest for events registered in SUPPORTED_EVENTS
28+
*/
29+
@SuppressWarnings("deprecation") // APIGatewayV2ProxyRequestEvent is deprecated
30+
public class UnregisteredEventSerializationRoundTripTest {
31+
32+
@ParameterizedTest(name = "{0}")
33+
@MethodSource("passingCases")
34+
void roundTrip(String displayName, String fixture, Class<?> eventClass) {
35+
LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass);
36+
}
37+
38+
@ParameterizedTest(name = "{0} (known failure)")
39+
@MethodSource("knownFailureCases")
40+
void roundTripKnownFailures(String displayName, String fixture, Class<?> eventClass) {
41+
assertThrows(Throwable.class,
42+
() -> LambdaEventAssert.assertSerializationRoundTrip(fixture, eventClass),
43+
displayName + " was expected to fail but passed — move it to passingCases()");
44+
}
45+
46+
private static Stream<Arguments> passingCases() {
47+
return Stream.of(
48+
// S3 Batch
49+
args(S3BatchEvent.class, "s3_batch_event.json"),
50+
// AppSync
51+
args(AppSyncLambdaAuthorizerEvent.class, "appsync_authorizer_event.json"),
52+
args(AppSyncLambdaAuthorizerResponse.class, "appsync_authorizer_response.json"),
53+
// TimeWindow response
54+
args(TimeWindowEventResponse.class, "time_window_event_response.json"),
55+
// Cognito UserPool triggers
56+
args(CognitoUserPoolPreSignUpEvent.class, "cognito/cognito_userpool_presignup.json"),
57+
args(CognitoUserPoolPostConfirmationEvent.class, "cognito/cognito_userpool_postconfirmation.json"),
58+
args(CognitoUserPoolPreAuthenticationEvent.class, "cognito/cognito_userpool_preauthentication.json"),
59+
args(CognitoUserPoolPostAuthenticationEvent.class, "cognito/cognito_userpool_postauthentication.json"),
60+
args(CognitoUserPoolDefineAuthChallengeEvent.class, "cognito/cognito_userpool_define_auth_challenge.json"),
61+
args(CognitoUserPoolCreateAuthChallengeEvent.class, "cognito/cognito_userpool_create_auth_challenge.json"),
62+
args(CognitoUserPoolVerifyAuthChallengeResponseEvent.class, "cognito/cognito_userpool_verify_auth_challenge.json"),
63+
args(CognitoUserPoolMigrateUserEvent.class, "cognito/cognito_userpool_migrate_user.json"),
64+
args(CognitoUserPoolCustomMessageEvent.class, "cognito/cognito_userpool_custom_message.json"),
65+
args(CognitoUserPoolPreTokenGenerationEvent.class, "cognito/cognito_userpool_pre_token_generation.json"),
66+
// Kinesis Analytics
67+
args(KinesisAnalyticsFirehoseInputPreprocessingEvent.class, "kinesis/kinesis_analytics_firehose_input_preprocessing.json"),
68+
args(KinesisAnalyticsStreamsInputPreprocessingEvent.class, "kinesis/kinesis_analytics_streams_input_preprocessing.json"),
69+
args(KinesisAnalyticsInputPreprocessingResponse.class, "kinesis/kinesis_analytics_input_preprocessing_response.json"),
70+
args(KinesisAnalyticsOutputDeliveryEvent.class, "kinesis/kinesis_analytics_output_delivery.json"),
71+
args(KinesisAnalyticsOutputDeliveryResponse.class, "kinesis/kinesis_analytics_output_delivery_response.json"),
72+
// API Gateway V2 WebSocket
73+
args(APIGatewayV2WebSocketEvent.class, "apigw_websocket_event.json"),
74+
args(APIGatewayV2ProxyRequestEvent.class, "apigw_websocket_event.json"));
75+
}
76+
77+
private static Stream<Arguments> knownFailureCases() {
78+
return Stream.of(
79+
// S3ObjectLambdaEvent: Lombok generates getXAmzRequestId() for field
80+
// "xAmzRequestId". With USE_STD_BEAN_NAMING, Jackson derives the property
81+
// name as "XAmzRequestId" (capital X), so the original "xAmzRequestId" key
82+
// is silently dropped during deserialization.
83+
// Fix: add @JsonProperty("xAmzRequestId") on the field or getter.
84+
args(S3ObjectLambdaEvent.class, "s3_object_lambda_event.json"));
85+
}
86+
87+
private static Arguments args(Class<?> clazz, String fixture) {
88+
return Arguments.of(clazz.getSimpleName(), fixture, clazz);
89+
}
90+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"resource": "/",
3+
"path": "/",
4+
"httpMethod": "GET",
5+
"headers": {
6+
"Host": "abcdef1234.execute-api.us-east-1.amazonaws.com",
7+
"Sec-WebSocket-Extensions": "permessage-deflate",
8+
"Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==",
9+
"Sec-WebSocket-Version": "13",
10+
"X-Forwarded-For": "192.0.2.1",
11+
"X-Forwarded-Port": "443",
12+
"X-Forwarded-Proto": "https"
13+
},
14+
"multiValueHeaders": {
15+
"Host": [
16+
"abcdef1234.execute-api.us-east-1.amazonaws.com"
17+
],
18+
"Sec-WebSocket-Extensions": [
19+
"permessage-deflate"
20+
],
21+
"Sec-WebSocket-Key": [
22+
"dGhlIHNhbXBsZSBub25jZQ=="
23+
],
24+
"Sec-WebSocket-Version": [
25+
"13"
26+
],
27+
"X-Forwarded-For": [
28+
"192.0.2.1"
29+
],
30+
"X-Forwarded-Port": [
31+
"443"
32+
],
33+
"X-Forwarded-Proto": [
34+
"https"
35+
]
36+
},
37+
"queryStringParameters": {
38+
"param1": "value1"
39+
},
40+
"multiValueQueryStringParameters": {
41+
"param1": [
42+
"value1"
43+
]
44+
},
45+
"pathParameters": {
46+
"proxy": "path/to/resource"
47+
},
48+
"stageVariables": {
49+
"stageVar1": "value1"
50+
},
51+
"requestContext": {
52+
"accountId": "123456789012",
53+
"resourceId": "abcdef",
54+
"stage": "prod",
55+
"requestId": "abc-def-ghi",
56+
"identity": {
57+
"cognitoIdentityPoolId": "us-east-1:id-pool",
58+
"accountId": "123456789012",
59+
"cognitoIdentityId": "us-east-1:identity-id",
60+
"caller": "caller-id",
61+
"apiKey": "api-key-id",
62+
"sourceIp": "192.0.2.1",
63+
"cognitoAuthenticationType": "authenticated",
64+
"cognitoAuthenticationProvider": "provider",
65+
"userArn": "arn:aws:iam::123456789012:user/testuser",
66+
"userAgent": "Mozilla/5.0",
67+
"user": "testuser",
68+
"accessKey": "AKIAIOSFODNN7EXAMPLE"
69+
},
70+
"resourcePath": "/",
71+
"httpMethod": "GET",
72+
"apiId": "abcdef1234",
73+
"connectedAt": 1583348638390,
74+
"connectionId": "abc123=",
75+
"domainName": "abcdef1234.execute-api.us-east-1.amazonaws.com",
76+
"eventType": "CONNECT",
77+
"extendedRequestId": "abc123=",
78+
"integrationLatency": "100",
79+
"messageDirection": "IN",
80+
"messageId": "msg-001",
81+
"requestTime": "09/Apr/2020:18:03:58 +0000",
82+
"requestTimeEpoch": 1583348638390,
83+
"routeKey": "$connect",
84+
"status": "200"
85+
},
86+
"body": "request body",
87+
"isBase64Encoded": false
88+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"authorizationToken": "BE9DC5E3-D410-4733-AF76-70178092E681",
3+
"requestContext": {
4+
"apiId": "giy7kumfmvcqvbedntjwjvagii",
5+
"accountId": "254688921111",
6+
"requestId": "b80ed838-14c6-4500-b4c3-b694c7bef086",
7+
"queryDocument": "mutation MyNewTask($desc: String!) {\n createTask(description: $desc) {\n id\n }\n}\n",
8+
"operationName": "MyNewTask",
9+
"variables": {}
10+
},
11+
"requestHeaders": {
12+
"host": "giy7kumfmvcqvbedntjwjvagii.appsync-api.us-east-1.amazonaws.com",
13+
"content-type": "application/json"
14+
}
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"isAuthorized": true,
3+
"resolverContext": {
4+
"name": "Foo Man",
5+
"balance": "100"
6+
},
7+
"deniedFields": [
8+
"Mutation.createEvent"
9+
],
10+
"ttlOverride": 15
11+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"version": "1",
3+
"triggerSource": "CreateAuthChallenge_Authentication",
4+
"region": "us-east-1",
5+
"userPoolId": "us-east-1_uPoolId",
6+
"userName": "testuser",
7+
"callerContext": {
8+
"awsSdkVersion": "2.0.0",
9+
"clientId": "abcdefg1234567"
10+
},
11+
"request": {
12+
"userAttributes": {
13+
"email": "user@example.com"
14+
},
15+
"clientMetadata": {
16+
"meta1": "value1"
17+
},
18+
"challengeName": "CUSTOM_CHALLENGE",
19+
"session": [
20+
{
21+
"challengeName": "PASSWORD_VERIFIER",
22+
"challengeResult": true,
23+
"challengeMetadata": "metadata1"
24+
}
25+
],
26+
"userNotFound": false
27+
},
28+
"response": {
29+
"publicChallengeParameters": {
30+
"captchaUrl": "url/123.jpg"
31+
},
32+
"privateChallengeParameters": {
33+
"answer": "5"
34+
},
35+
"challengeMetadata": "CAPTCHA_CHALLENGE"
36+
}
37+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"version": "1",
3+
"triggerSource": "CustomMessage_SignUp",
4+
"region": "us-east-1",
5+
"userPoolId": "us-east-1_uPoolId",
6+
"userName": "testuser",
7+
"callerContext": {
8+
"awsSdkVersion": "2.0.0",
9+
"clientId": "abcdefg1234567"
10+
},
11+
"request": {
12+
"userAttributes": {
13+
"email": "user@example.com",
14+
"phone_number_verified": "true",
15+
"email_verified": "true"
16+
},
17+
"clientMetadata": {
18+
"meta1": "value1"
19+
},
20+
"codeParameter": "####",
21+
"usernameParameter": "testuser"
22+
},
23+
"response": {
24+
"smsMessage": "Your code is ####",
25+
"emailMessage": "Your code is ####",
26+
"emailSubject": "Welcome"
27+
}
28+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"version": "1",
3+
"triggerSource": "DefineAuthChallenge_Authentication",
4+
"region": "us-east-1",
5+
"userPoolId": "us-east-1_uPoolId",
6+
"userName": "testuser",
7+
"callerContext": {
8+
"awsSdkVersion": "2.0.0",
9+
"clientId": "abcdefg1234567"
10+
},
11+
"request": {
12+
"userAttributes": {
13+
"email": "user@example.com"
14+
},
15+
"clientMetadata": {
16+
"meta1": "value1"
17+
},
18+
"session": [
19+
{
20+
"challengeName": "PASSWORD_VERIFIER",
21+
"challengeResult": true,
22+
"challengeMetadata": "metadata1"
23+
}
24+
],
25+
"userNotFound": false
26+
},
27+
"response": {
28+
"challengeName": "CUSTOM_CHALLENGE",
29+
"issueTokens": false,
30+
"failAuthentication": false
31+
}
32+
}

0 commit comments

Comments
 (0)