Skip to content

Commit a7e17ee

Browse files
authored
feat: add integration tests stack (#379)
* feat: add integration tests stack * fix: fmt and clippy * feat: add integration tests for lambda_http * fix: disable multiple_crate_versions
1 parent 1742571 commit a7e17ee

File tree

13 files changed

+393
-0
lines changed

13 files changed

+393
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
/target
22
/.cargo
33
lambda-runtime/libtest.rmeta
4+
lambda-integration-tests/target
45
Cargo.lock
56

67
# Built AWS Lambda zipfile
78
lambda.zip
89

910
# output.json from example docs
1011
output.json
12+
13+
.aws-sam
14+
build

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[workspace]
22
members = [
33
"lambda-http",
4+
"lambda-integration-tests",
45
"lambda-runtime-api-client",
56
"lambda-runtime",
67
"lambda-extension"

Makefile

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
INTEG_STACK_NAME ?= rust-lambda-integration-tests
2+
INTEG_FUNCTIONS_BUILD := runtime-fn runtime-trait http-fn
3+
INTEG_FUNCTIONS_INVOKE := RuntimeFn RuntimeFnAl2 RuntimeTrait RuntimeTraitAl2 Python PythonAl2
4+
INTEG_API_INVOKE := RestApiUrl HttpApiUrl
5+
INTEG_EXTENSIONS := extension-fn extension-trait
6+
# Using musl to run extensions on both AL1 and AL2
7+
INTEG_ARCH := x86_64-unknown-linux-musl
8+
9+
integration-tests:
10+
# Build Integration functions
11+
cross build --release --target $(INTEG_ARCH) -p lambda_integration_tests
12+
rm -rf ./build
13+
mkdir -p ./build
14+
${MAKE} ${MAKEOPTS} $(foreach function,${INTEG_FUNCTIONS_BUILD}, build-integration-function-${function})
15+
${MAKE} ${MAKEOPTS} $(foreach extension,${INTEG_EXTENSIONS}, build-integration-extension-${extension})
16+
# Deploy to AWS
17+
sam deploy \
18+
--template lambda-integration-tests/template.yaml \
19+
--stack-name ${INTEG_STACK_NAME} \
20+
--capabilities CAPABILITY_IAM \
21+
--resolve-s3 \
22+
--no-fail-on-empty-changeset
23+
# Invoke functions
24+
${MAKE} ${MAKEOPTS} $(foreach function,${INTEG_FUNCTIONS_INVOKE}, invoke-integration-function-${function})
25+
${MAKE} ${MAKEOPTS} $(foreach api,${INTEG_API_INVOKE}, invoke-integration-api-${api})
26+
27+
build-integration-function-%:
28+
mkdir -p ./build/$*
29+
cp -v ./target/$(INTEG_ARCH)/release/$* ./build/$*/bootstrap
30+
31+
build-integration-extension-%:
32+
mkdir -p ./build/$*/extensions
33+
cp -v ./target/$(INTEG_ARCH)/release/$* ./build/$*/extensions/$*
34+
35+
invoke-integration-function-%:
36+
aws lambda invoke --function-name $$(aws cloudformation describe-stacks --stack-name $(INTEG_STACK_NAME) \
37+
--query 'Stacks[0].Outputs[?OutputKey==`$*`].OutputValue' \
38+
--output text) --payload '{"command": "hello"}' --cli-binary-format raw-in-base64-out /dev/stdout
39+
40+
invoke-integration-api-%:
41+
$(eval API_URL := $(shell aws cloudformation describe-stacks --stack-name $(INTEG_STACK_NAME) \
42+
--query 'Stacks[0].Outputs[?OutputKey==`$*`].OutputValue' \
43+
--output text))
44+
curl $(API_URL)/get
45+
curl $(API_URL)/al2/get
46+
curl -X POST -d '{"command": "hello"}' $(API_URL)/post
47+
curl -X POST -d '{"command": "hello"}' $(API_URL)/al2/post
48+

lambda-extension/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![deny(clippy::all, clippy::cargo)]
2+
#![allow(clippy::multiple_crate_versions)]
23
#![warn(missing_docs, nonstandard_style, rust_2018_idioms)]
34

45
//! This module includes utilities to create Lambda Runtime Extensions.

lambda-integration-tests/Cargo.toml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "lambda_integration_tests"
3+
version = "0.4.1"
4+
edition = "2018"
5+
description = "AWS Lambda Runtime integration tests"
6+
license = "Apache-2.0"
7+
repository = "https://github.com/awslabs/aws-lambda-rust-runtime"
8+
categories = ["web-programming::http-server"]
9+
keywords = ["AWS", "Lambda", "API"]
10+
readme = "../README.md"
11+
12+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13+
14+
[dependencies]
15+
lambda_http = { path = "../lambda-http", version = "0.4.1" }
16+
lambda_runtime = { path = "../lambda-runtime", version = "0.4.1" }
17+
lambda_extension = { path = "../lambda-extension", version = "0.1.0" }
18+
log = "0.4"
19+
serde = { version = "1", features = ["derive"] }
20+
simple_logger = { version = "1.15", default-features = false }
21+
tokio = { version = "1", features = ["full"] }
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
def handler(event, context):
2+
return {
3+
"message": event["command"].upper()
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use lambda_extension::{extension_fn, Error, NextEvent};
2+
use log::{info, LevelFilter};
3+
use simple_logger::SimpleLogger;
4+
5+
async fn my_extension(event: NextEvent) -> Result<(), Error> {
6+
match event {
7+
NextEvent::Shutdown(e) => {
8+
info!("[extension-fn] Shutdown event received: {:?}", e);
9+
}
10+
NextEvent::Invoke(e) => {
11+
info!("[extension-fn] Request event received: {:?}", e);
12+
}
13+
}
14+
15+
Ok(())
16+
}
17+
18+
#[tokio::main]
19+
async fn main() -> Result<(), Error> {
20+
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
21+
22+
lambda_extension::run(extension_fn(my_extension)).await
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use lambda_extension::{Error, Extension, NextEvent};
2+
use log::{info, LevelFilter};
3+
use simple_logger::SimpleLogger;
4+
use std::{
5+
future::{ready, Future},
6+
pin::Pin,
7+
};
8+
9+
#[derive(Default)]
10+
struct MyExtension {
11+
invoke_count: usize,
12+
}
13+
14+
impl Extension for MyExtension {
15+
type Fut = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
16+
17+
fn call(&mut self, event: NextEvent) -> Self::Fut {
18+
match event {
19+
NextEvent::Shutdown(e) => {
20+
info!("[extension] Shutdown event received: {:?}", e);
21+
}
22+
NextEvent::Invoke(e) => {
23+
self.invoke_count += 1;
24+
info!("[extension] Request event {} received: {:?}", self.invoke_count, e);
25+
}
26+
}
27+
28+
Box::pin(ready(Ok(())))
29+
}
30+
}
31+
32+
#[tokio::main]
33+
async fn main() -> Result<(), Error> {
34+
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
35+
36+
lambda_extension::run(MyExtension::default()).await
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use lambda_http::{
2+
lambda_runtime::{self, Context, Error},
3+
IntoResponse, Request, Response,
4+
};
5+
use log::{info, LevelFilter};
6+
use simple_logger::SimpleLogger;
7+
8+
async fn handler(event: Request, _context: Context) -> Result<impl IntoResponse, Error> {
9+
info!("[http-fn] Received event {} {}", event.method(), event.uri().path());
10+
11+
Ok(Response::builder().status(200).body("Hello, world!").unwrap())
12+
}
13+
14+
#[tokio::main]
15+
async fn main() -> Result<(), Error> {
16+
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
17+
18+
lambda_runtime::run(lambda_http::handler(handler)).await
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use lambda_runtime::{handler_fn, Context, Error};
2+
use log::{info, LevelFilter};
3+
use serde::{Deserialize, Serialize};
4+
use simple_logger::SimpleLogger;
5+
6+
#[derive(Deserialize, Debug)]
7+
struct Request {
8+
command: String,
9+
}
10+
11+
#[derive(Serialize, Debug)]
12+
struct Response {
13+
message: String,
14+
}
15+
16+
async fn handler(event: Request, _context: Context) -> Result<Response, Error> {
17+
info!("[handler-fn] Received event: {:?}", event);
18+
19+
Ok(Response {
20+
message: event.command.to_uppercase(),
21+
})
22+
}
23+
24+
#[tokio::main]
25+
async fn main() -> Result<(), Error> {
26+
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
27+
28+
lambda_runtime::run(handler_fn(handler)).await
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use lambda_runtime::{Context, Error, Handler};
2+
use log::{info, LevelFilter};
3+
use serde::{Deserialize, Serialize};
4+
use simple_logger::SimpleLogger;
5+
use std::{
6+
future::{ready, Future},
7+
pin::Pin,
8+
};
9+
10+
#[derive(Deserialize, Debug)]
11+
struct Request {
12+
command: String,
13+
}
14+
15+
#[derive(Serialize, Debug)]
16+
struct Response {
17+
message: String,
18+
}
19+
20+
#[derive(Default)]
21+
struct MyHandler {
22+
invoke_count: usize,
23+
}
24+
25+
impl Handler<Request, Response> for MyHandler {
26+
type Error = Error;
27+
type Fut = Pin<Box<dyn Future<Output = Result<Response, Error>>>>;
28+
29+
fn call(&mut self, event: Request, _context: Context) -> Self::Fut {
30+
self.invoke_count += 1;
31+
info!("[handler] Received event {}: {:?}", self.invoke_count, event);
32+
Box::pin(ready(Ok(Response {
33+
message: event.command.to_uppercase(),
34+
})))
35+
}
36+
}
37+
38+
#[tokio::main]
39+
async fn main() -> Result<(), Error> {
40+
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
41+
42+
lambda_runtime::run(MyHandler::default()).await
43+
}

0 commit comments

Comments
 (0)