Skip to content

Commit 4808b8f

Browse files
author
Pascal Romanens
committed
1298 introduced E2E tests for validation
1 parent eda478d commit 4808b8f

File tree

11 files changed

+471
-0
lines changed

11 files changed

+471
-0
lines changed

powertools-e2e-tests/handlers/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<module>metrics</module>
3333
<module>idempotency</module>
3434
<module>parameters</module>
35+
<module>validation</module>
3536
</modules>
3637

3738
<dependencyManagement>
@@ -79,6 +80,11 @@
7980
<artifactId>powertools-batch</artifactId>
8081
<version>${lambda.powertools.version}</version>
8182
</dependency>
83+
<dependency>
84+
<groupId>software.amazon.lambda</groupId>
85+
<artifactId>powertools-validation</artifactId>
86+
<version>${lambda.powertools.version}</version>
87+
</dependency>
8288
<dependency>
8389
<groupId>com.amazonaws</groupId>
8490
<artifactId>aws-lambda-java-core</artifactId>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>software.amazon.lambda</groupId>
7+
<artifactId>e2e-test-handlers-parent</artifactId>
8+
<version>1.0.0</version>
9+
</parent>
10+
11+
<artifactId>e2e-test-handler-validation</artifactId>
12+
<packaging>jar</packaging>
13+
<name>A Lambda function using Powertools for AWS Lambda (Java) validation</name>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>software.amazon.lambda</groupId>
18+
<artifactId>powertools-validation</artifactId>
19+
</dependency>
20+
<dependency>
21+
<groupId>com.amazonaws</groupId>
22+
<artifactId>aws-lambda-java-events</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.aspectj</groupId>
26+
<artifactId>aspectjrt</artifactId>
27+
</dependency>
28+
</dependencies>
29+
30+
<build>
31+
<plugins>
32+
<plugin>
33+
<groupId>dev.aspectj</groupId>
34+
<artifactId>aspectj-maven-plugin</artifactId>
35+
<configuration>
36+
<source>${maven.compiler.source}</source>
37+
<target>${maven.compiler.target}</target>
38+
<complianceLevel>${maven.compiler.target}</complianceLevel>
39+
<aspectLibraries>
40+
<aspectLibrary>
41+
<groupId>software.amazon.lambda</groupId>
42+
<artifactId>powertools-validation</artifactId>
43+
</aspectLibrary>
44+
</aspectLibraries>
45+
</configuration>
46+
<executions>
47+
<execution>
48+
<goals>
49+
<goal>compile</goal>
50+
</goals>
51+
</execution>
52+
</executions>
53+
</plugin>
54+
<plugin>
55+
<groupId>org.apache.maven.plugins</groupId>
56+
<artifactId>maven-shade-plugin</artifactId>
57+
</plugin>
58+
</plugins>
59+
</build>
60+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates.
3+
* Licensed under the Apache License, Version 2.0 (the
4+
* "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*
13+
*/
14+
15+
package software.amazon.lambda.powertools.e2e;
16+
17+
import com.amazonaws.services.lambda.runtime.Context;
18+
import com.amazonaws.services.lambda.runtime.RequestHandler;
19+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
20+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
21+
22+
import software.amazon.lambda.powertools.validation.Validation;
23+
24+
public class Function implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
25+
@Validation(inboundSchema = "classpath:/validation/inbound_schema.json", outboundSchema = "classpath:/validation/outbound_schema.json")
26+
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
27+
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
28+
response.setBody(input.getBody());
29+
response.setStatusCode(200);
30+
response.setIsBase64Encoded(false);
31+
return response;
32+
}
33+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration>
3+
<Appenders>
4+
<Console name="JsonAppender" target="SYSTEM_OUT">
5+
<JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" />
6+
</Console>
7+
</Appenders>
8+
<Loggers>
9+
<Root level="INFO">
10+
<AppenderRef ref="JsonAppender"/>
11+
</Root>
12+
<Logger name="JsonLogger" level="INFO" additivity="false">
13+
<AppenderRef ref="JsonAppender"/>
14+
</Logger>
15+
</Loggers>
16+
</Configuration>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "http://example.com/product.json",
4+
"type": "object",
5+
"title": "Product schema",
6+
"description": "JSON schema to validate Products",
7+
"default": {},
8+
"examples": [
9+
{
10+
"id": 43242,
11+
"name": "FooBar XY",
12+
"price": 258
13+
}
14+
],
15+
"required": [
16+
"price"
17+
],
18+
"properties": {
19+
"price": {
20+
"$id": "#/properties/price",
21+
"type": "number",
22+
"title": "Price of the product",
23+
"description": "Positive price of the product",
24+
"default": 0,
25+
"exclusiveMinimum": 0,
26+
"examples": [
27+
258.99
28+
]
29+
}
30+
},
31+
"additionalProperties": true
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "http://example.com/product.json",
4+
"type": "object",
5+
"title": "Product schema",
6+
"description": "JSON schema to validate Products",
7+
"default": {},
8+
"examples": [
9+
{
10+
"id": 43242,
11+
"name": "FooBar XY",
12+
"price": 258
13+
}
14+
],
15+
"required": [
16+
"price"
17+
],
18+
"properties": {
19+
"price": {
20+
"$id": "#/properties/price",
21+
"type": "number",
22+
"title": "Price of the product",
23+
"description": "Positive price of the product",
24+
"default": 0,
25+
"exclusiveMaximum": 1000,
26+
"examples": [
27+
258.99
28+
]
29+
}
30+
},
31+
"additionalProperties": true
32+
}

powertools-e2e-tests/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@
172172
<artifactId>powertools-serialization</artifactId>
173173
<scope>test</scope>
174174
</dependency>
175+
176+
<dependency>
177+
<groupId>com.amazonaws</groupId>
178+
<artifactId>aws-lambda-java-tests</artifactId>
179+
<version>1.1.1</version>
180+
<scope>test</scope>
181+
</dependency>
175182
</dependencies>
176183

177184
<build>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates.
3+
* Licensed under the Apache License, Version 2.0 (the
4+
* "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*
13+
*/
14+
15+
package software.amazon.lambda.powertools;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static software.amazon.lambda.powertools.testutils.Infrastructure.FUNCTION_NAME_OUTPUT;
19+
import static software.amazon.lambda.powertools.testutils.lambda.LambdaInvoker.invokeFunction;
20+
21+
import java.io.IOException;
22+
import java.util.Map;
23+
import java.util.concurrent.TimeUnit;
24+
25+
import org.junit.jupiter.api.AfterAll;
26+
import org.junit.jupiter.api.BeforeAll;
27+
import org.junit.jupiter.api.Timeout;
28+
import org.junit.jupiter.params.ParameterizedTest;
29+
30+
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
31+
import com.amazonaws.services.lambda.runtime.tests.annotations.Event;
32+
import com.fasterxml.jackson.databind.JsonNode;
33+
import com.fasterxml.jackson.databind.ObjectMapper;
34+
35+
import software.amazon.lambda.powertools.testutils.Infrastructure;
36+
import software.amazon.lambda.powertools.testutils.lambda.InvocationResult;
37+
38+
class ValidationE2ET {
39+
40+
private static final ObjectMapper objectMapper = new ObjectMapper();
41+
42+
private static Infrastructure infrastructure;
43+
private static String functionName;
44+
45+
@BeforeAll
46+
@Timeout(value = 5, unit = TimeUnit.MINUTES)
47+
public static void setup() {
48+
infrastructure = Infrastructure.builder().testName(ValidationE2ET.class.getSimpleName())
49+
.pathToFunction("validation").build();
50+
Map<String, String> outputs = infrastructure.deploy();
51+
functionName = outputs.get(FUNCTION_NAME_OUTPUT);
52+
}
53+
54+
@AfterAll
55+
public static void tearDown() {
56+
if (infrastructure != null) {
57+
infrastructure.destroy();
58+
}
59+
}
60+
61+
@ParameterizedTest
62+
@Event(value = "/validation/valid_api_gw_in_out_event.json", type = APIGatewayProxyRequestEvent.class)
63+
void test_validInboundApiGWEvent(APIGatewayProxyRequestEvent validEvent) throws IOException {
64+
// WHEN
65+
InvocationResult invocationResult = invokeFunction(functionName, objectMapper.writeValueAsString(validEvent));
66+
67+
// THEN
68+
// invocation should pass validation and return 200
69+
JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult());
70+
assertThat(validJsonNode.get("statusCode").asInt()).isEqualTo(200);
71+
assertThat(validJsonNode.get("body").asText()).isEqualTo("{\"price\": 150}");
72+
}
73+
74+
@ParameterizedTest
75+
@Event(value = "/validation/invalid_api_gw_in_event.json", type = APIGatewayProxyRequestEvent.class)
76+
void test_invalidInboundApiGWEvent(APIGatewayProxyRequestEvent validEvent) throws IOException {
77+
// WHEN
78+
InvocationResult invocationResult = invokeFunction(functionName, objectMapper.writeValueAsString(validEvent));
79+
80+
// THEN
81+
// invocation should fail inbound validation and return 400
82+
JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult());
83+
assertThat(validJsonNode.get("statusCode").asInt()).isEqualTo(400);
84+
assertThat(validJsonNode.get("body").asText()).contains("$.price: is missing but it is required");
85+
}
86+
87+
@ParameterizedTest
88+
@Event(value = "/validation/invalid_api_gw_out_event.json", type = APIGatewayProxyRequestEvent.class)
89+
void test_invalidOutboundApiGWEvent(APIGatewayProxyRequestEvent validEvent) throws IOException {
90+
// WHEN
91+
InvocationResult invocationResult = invokeFunction(functionName, objectMapper.writeValueAsString(validEvent));
92+
93+
// THEN
94+
// invocation should fail outbound validation and return 400
95+
JsonNode validJsonNode = objectMapper.readTree(invocationResult.getResult());
96+
assertThat(validJsonNode.get("statusCode").asInt()).isEqualTo(400);
97+
assertThat(validJsonNode.get("body").asText()).contains("$.price: must have an exclusive maximum value of 1000");
98+
}
99+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"body": "{\"message\": \"Lambda rocks\"}",
3+
"resource": "/{proxy+}",
4+
"path": "/path/to/resource",
5+
"httpMethod": "POST",
6+
"isBase64Encoded": false,
7+
"queryStringParameters": {
8+
"foo": "bar"
9+
},
10+
"pathParameters": {
11+
"proxy": "/path/to/resource"
12+
},
13+
"stageVariables": {
14+
"baz": "qux"
15+
},
16+
"headers": {
17+
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
18+
"Accept-Encoding": "gzip, deflate, sdch",
19+
"Accept-Language": "en-US,en;q=0.8",
20+
"Cache-Control": "max-age=0",
21+
"CloudFront-Forwarded-Proto": "https",
22+
"CloudFront-Is-Desktop-Viewer": "true",
23+
"CloudFront-Is-Mobile-Viewer": "false",
24+
"CloudFront-Is-SmartTV-Viewer": "false",
25+
"CloudFront-Is-Tablet-Viewer": "false",
26+
"CloudFront-Viewer-Country": "US",
27+
"Host": "1234567890.execute-api.us-east-1.amazonaws.com",
28+
"Upgrade-Insecure-Requests": "1",
29+
"User-Agent": "Custom User Agent String",
30+
"Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
31+
"X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
32+
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
33+
"X-Forwarded-Port": "443",
34+
"X-Forwarded-Proto": "https"
35+
},
36+
"requestContext": {
37+
"accountId": "123456789012",
38+
"resourceId": "123456",
39+
"stage": "prod",
40+
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
41+
"requestTime": "09/Apr/2015:12:34:56 +0000",
42+
"requestTimeEpoch": 1428582896000,
43+
"identity": {
44+
"cognitoIdentityPoolId": null,
45+
"accountId": null,
46+
"cognitoIdentityId": null,
47+
"caller": null,
48+
"accessKey": null,
49+
"sourceIp": "127.0.0.1",
50+
"cognitoAuthenticationType": null,
51+
"cognitoAuthenticationProvider": null,
52+
"userArn": null,
53+
"userAgent": "Custom User Agent String",
54+
"user": null
55+
},
56+
"path": "/prod/path/to/resource",
57+
"resourcePath": "/{proxy+}",
58+
"httpMethod": "POST",
59+
"apiId": "1234567890",
60+
"protocol": "HTTP/1.1"
61+
}
62+
}

0 commit comments

Comments
 (0)