Skip to content

Commit 9b95ebf

Browse files
committed
feat: run integration tests on CI
1 parent 150c5f0 commit 9b95ebf

File tree

8 files changed

+286
-0
lines changed

8 files changed

+286
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Run integration tests
2+
3+
permissions:
4+
id-token: write
5+
contents: read
6+
7+
on:
8+
workflow_dispatch:
9+
push:
10+
11+
jobs:
12+
run-integration-tests:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: install Cargo Lambda
16+
uses: jaxxstorm/action-install-gh-release@v1.9.0
17+
with:
18+
repo: cargo-lambda/cargo-lambda
19+
platform: linux
20+
arch: x86_64
21+
- name: install Zig toolchain
22+
uses: korandoru/setup-zig@v1
23+
with:
24+
zig-version: 0.10.0
25+
- name: install SAM
26+
uses: aws-actions/setup-sam@v2
27+
with:
28+
use-installer: true
29+
- uses: actions/checkout@v3
30+
- name: configure aws credentials
31+
uses: aws-actions/configure-aws-credentials@v4.0.2
32+
with:
33+
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
34+
role-session-name: ${{ secrets.ROLE_SESSION_NAME }}
35+
aws-region: ${{ secrets.AWS_REGION }}
36+
- name: build stack
37+
run: cd lambda-integration-tests-ci && sam build --beta-features
38+
- name: validate stack
39+
run: cd lambda-integration-tests-ci && sam validate --lint
40+
- name: deploy stack
41+
id: deploy_stack
42+
env:
43+
AWS_REGION: ${{ secrets.AWS_REGION }}
44+
run: |
45+
cd lambda-integration-tests-ci
46+
stackName="aws-lambda-rust-integ-test-$GITHUB_RUN_ID"
47+
echo "STACK_NAME=$stackName" >> "$GITHUB_OUTPUT"
48+
echo "Stack name = $stackName"
49+
sam deploy --stack-name "${stackName}" --parameter-overrides "ParameterKey=SecretToken,ParameterValue=${{ secrets.SECRET_TOKEN }}" --no-confirm-changeset --no-progressbar > disable_output
50+
TEST_ENDPOINT=$(sam list stack-outputs --stack-name "${stackName}" --output json | jq -r '.[] | .OutputValue')
51+
echo "TEST_ENDPOINT=$TEST_ENDPOINT" >> "$GITHUB_OUTPUT"
52+
- name: run test
53+
env:
54+
SECRET_TOKEN: ${{ secrets.SECRET_TOKEN }}
55+
TEST_ENDPOINT: ${{ steps.deploy_stack.outputs.TEST_ENDPOINT }}
56+
run: cd lambda-integration-tests-ci && cargo test
57+
- name: cleanup
58+
if: always()
59+
env:
60+
AWS_REGION: ${{ secrets.AWS_REGION }}
61+
STACK_NAME: ${{ steps.deploy_stack.outputs.STACK_NAME }}
62+
run: sam delete --stack-name "${STACK_NAME}" --no-prompts

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ resolver = "2"
33
members = [
44
"lambda-http",
55
"lambda-integration-tests",
6+
"lambda-integration-tests-ci",
67
"lambda-runtime-api-client",
78
"lambda-runtime",
89
"lambda-extension",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "aws_lambda_rust_integration_tests"
3+
version = "0.1.0"
4+
authors = ["Maxime David"]
5+
edition = "2021"
6+
description = "AWS Lambda Runtime integration tests"
7+
license = "Apache-2.0"
8+
repository = "https://github.com/awslabs/aws-lambda-rust-runtime"
9+
categories = ["web-programming::http-server"]
10+
keywords = ["AWS", "Lambda", "API"]
11+
readme = "../README.md"
12+
13+
[dependencies]
14+
lambda_runtime = { path = "../lambda-runtime" }
15+
aws_lambda_events = { path = "../lambda-events" }
16+
serde_json = "1.0.121"
17+
tokio = { version = "1", features = ["full"] }
18+
tracing-subscriber = { version = "0.3", default-features = false, features = ["json"] }
19+
serde = { version = "1.0.204", features = ["derive"] }
20+
21+
[dev-dependencies]
22+
reqwest = { version = "0.12.5", features = ["blocking"] }
23+
openssl = { version = "0.10", features = ["vendored"] }
24+
25+
[[bin]]
26+
name = "helloworld"
27+
path = "src/helloworld.rs"
28+
29+
[[bin]]
30+
name = "authorizer"
31+
path = "src/authorizer.rs"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
version = 0.1
2+
3+
[default]
4+
[default.build.parameters]
5+
cached = true
6+
parallel = true
7+
8+
[default.validate.parameters]
9+
lint = true
10+
11+
[default.deploy.parameters]
12+
capabilities = "CAPABILITY_IAM"
13+
confirm_changeset = true
14+
resolve_s3 = true
15+
s3_prefix = "aws-lambda-rust-integration-test"
16+
17+
[default.package.parameters]
18+
resolve_s3 = true
19+
20+
[default.sync.parameters]
21+
watch = true
22+
23+
[default.local_start_api.parameters]
24+
warm_containers = "EAGER"
25+
26+
[default.local_start_lambda.parameters]
27+
warm_containers = "EAGER"
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use std::env;
2+
3+
use aws_lambda_events::{
4+
apigw::{ApiGatewayCustomAuthorizerPolicy, ApiGatewayCustomAuthorizerResponse},
5+
event::iam::IamPolicyStatement,
6+
};
7+
use lambda_runtime::{service_fn, Error, LambdaEvent};
8+
use serde::Deserialize;
9+
use serde_json::json;
10+
use tracing_subscriber::EnvFilter;
11+
12+
#[derive(Deserialize)]
13+
#[serde(rename_all = "camelCase")]
14+
struct APIGatewayCustomAuthorizerRequest {
15+
authorization_token: String,
16+
method_arn: String,
17+
}
18+
19+
#[tokio::main]
20+
async fn main() -> Result<(), Error> {
21+
tracing_subscriber::fmt()
22+
.json()
23+
.with_env_filter(EnvFilter::from_default_env())
24+
.with_target(false)
25+
.with_current_span(false)
26+
.with_span_list(false)
27+
.without_time()
28+
.init();
29+
let func = service_fn(func);
30+
lambda_runtime::run(func).await?;
31+
Ok(())
32+
}
33+
34+
async fn func(
35+
event: LambdaEvent<APIGatewayCustomAuthorizerRequest>,
36+
) -> Result<ApiGatewayCustomAuthorizerResponse, Error> {
37+
let expected_token = env::var("SECRET_TOKEN").expect("could not read the secret token");
38+
if event.payload.authorization_token == expected_token {
39+
return Ok(allow(&event.payload.method_arn));
40+
}
41+
panic!("token is not valid");
42+
}
43+
44+
fn allow(method_arn: &str) -> ApiGatewayCustomAuthorizerResponse {
45+
let stmt = IamPolicyStatement {
46+
action: vec!["execute-api:Invoke".to_string()],
47+
resource: vec![method_arn.to_owned()],
48+
effect: aws_lambda_events::iam::IamPolicyEffect::Allow,
49+
condition: None,
50+
};
51+
let policy = ApiGatewayCustomAuthorizerPolicy {
52+
version: Some("2012-10-17".to_string()),
53+
statement: vec![stmt],
54+
};
55+
ApiGatewayCustomAuthorizerResponse {
56+
principal_id: Some("user".to_owned()),
57+
policy_document: policy,
58+
context: json!({ "hello": "world" }),
59+
usage_identifier_key: None,
60+
}
61+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use aws_lambda_events::{
2+
apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse},
3+
http::HeaderMap,
4+
};
5+
use lambda_runtime::{service_fn, Error, LambdaEvent};
6+
use tracing_subscriber::EnvFilter;
7+
8+
#[tokio::main]
9+
async fn main() -> Result<(), Error> {
10+
tracing_subscriber::fmt()
11+
.json()
12+
.with_env_filter(EnvFilter::from_default_env())
13+
.with_target(false)
14+
.with_current_span(false)
15+
.with_span_list(false)
16+
.without_time()
17+
.init();
18+
let func = service_fn(func);
19+
lambda_runtime::run(func).await?;
20+
Ok(())
21+
}
22+
23+
async fn func(_event: LambdaEvent<ApiGatewayProxyRequest>) -> Result<ApiGatewayProxyResponse, Error> {
24+
let mut headers = HeaderMap::new();
25+
headers.insert("content-type", "text/html".parse().unwrap());
26+
let resp = ApiGatewayProxyResponse {
27+
status_code: 200,
28+
multi_value_headers: headers.clone(),
29+
is_base64_encoded: false,
30+
body: Some("Hello world!".into()),
31+
headers,
32+
};
33+
Ok(resp)
34+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
Description: maxday-test
4+
5+
Parameters:
6+
SecretToken:
7+
Type: String
8+
9+
Globals:
10+
Function:
11+
Timeout: 3
12+
13+
Resources:
14+
API:
15+
Type: AWS::Serverless::Api
16+
Properties:
17+
StageName: integ-test
18+
Auth:
19+
DefaultAuthorizer: MyLambdaAuthorizer
20+
Authorizers:
21+
MyLambdaAuthorizer:
22+
FunctionArn: !GetAtt AuthorizerFunction.Arn
23+
HelloWorldFunction:
24+
Type: AWS::Serverless::Function
25+
Metadata:
26+
BuildMethod: rust-cargolambda
27+
BuildProperties:
28+
Binary: helloworld
29+
Properties:
30+
CodeUri: ./
31+
Handler: bootstrap
32+
Runtime: provided.al2023
33+
Events:
34+
HelloWorld:
35+
Type: Api
36+
Properties:
37+
RestApiId: !Ref API
38+
Path: /hello
39+
Method: get
40+
41+
AuthorizerFunction:
42+
Type: AWS::Serverless::Function
43+
Metadata:
44+
BuildMethod: rust-cargolambda
45+
BuildProperties:
46+
Binary: authorizer
47+
Properties:
48+
CodeUri: ./
49+
Handler: bootstrap
50+
Runtime: provided.al2023
51+
Environment:
52+
Variables:
53+
SECRET_TOKEN: !Ref SecretToken
54+
55+
Outputs:
56+
HelloApiEndpoint:
57+
Description: "API Gateway endpoint URL for HelloWorld"
58+
Value: !Sub "https://${API}.execute-api.${AWS::Region}.amazonaws.com/integ-test/hello/"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#[test]
2+
fn test_calling_lambda_should_return_200() {
3+
let test_endpoint = std::env::var("TEST_ENDPOINT").expect("could not read TEST_ENDPOINT");
4+
let secret_token = std::env::var("SECRET_TOKEN").expect("could not read SECRET_TOKEN");
5+
let client = reqwest::blocking::Client::new();
6+
let res = client
7+
.get(test_endpoint)
8+
.header("Authorization", secret_token)
9+
.send()
10+
.expect("could not the request");
11+
assert_eq!(res.status(), 200);
12+
}

0 commit comments

Comments
 (0)