Skip to content

Commit 9dd78d4

Browse files
committed
Add integration test harness for Lambda examples
Create a comprehensive test harness to deploy and smoke test all chapter examples before making updates. This ensures existing functionality works correctly and provides a safety net for the upcoming modernization work. - Deploy/teardown scripts for individual chapters and all chapters - Smoke tests for chapters 2, 3, 4, 5-api, and 5-event-sources - GitHub Actions workflow with OIDC authentication - Automatic cleanup on success or failure
1 parent 34fb04d commit 9dd78d4

File tree

14 files changed

+807
-0
lines changed

14 files changed

+807
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
name: Integration Tests
2+
3+
on:
4+
workflow_dispatch: # Manual trigger
5+
push:
6+
branches:
7+
- main
8+
- 'feature/**'
9+
pull_request:
10+
branches:
11+
- main
12+
13+
permissions:
14+
id-token: write # Required for OIDC
15+
contents: read
16+
17+
env:
18+
AWS_REGION: us-east-1
19+
20+
jobs:
21+
integration-test:
22+
runs-on: ubuntu-latest
23+
timeout-minutes: 60
24+
25+
steps:
26+
- name: Checkout code
27+
uses: actions/checkout@v4
28+
29+
- name: Set up Java 8
30+
uses: actions/setup-java@v4
31+
with:
32+
distribution: 'corretto'
33+
java-version: '8'
34+
cache: 'maven'
35+
36+
- name: Set up SAM CLI
37+
uses: aws-actions/setup-sam@v2
38+
with:
39+
use-installer: true
40+
version: '1.53.0' # Version from ~2022, compatible with 2020 templates
41+
42+
- name: Configure AWS credentials
43+
uses: aws-actions/configure-aws-credentials@v4
44+
with:
45+
role-to-assume: arn:aws:iam::397589511426:role/account-wide-resources-GithubActionsRole-1CAIKQPYM5WD3
46+
aws-region: ${{ env.AWS_REGION }}
47+
48+
- name: Make scripts executable
49+
run: |
50+
chmod +x test-harness/**/*.sh
51+
52+
- name: Deploy all stacks
53+
id: deploy
54+
run: |
55+
./test-harness/deploy/deploy-all.sh
56+
57+
- name: Run smoke tests
58+
id: test
59+
run: |
60+
./test-harness/test/test-all.sh
61+
62+
- name: Teardown all stacks
63+
if: always()
64+
run: |
65+
./test-harness/teardown/teardown-all.sh

test-harness/README.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Test Harness for Programming AWS Lambda Examples
2+
3+
This test harness deploys and tests all chapter examples to ensure they work correctly with AWS Lambda.
4+
5+
## Prerequisites
6+
7+
- AWS CLI configured with appropriate credentials
8+
- SAM CLI installed
9+
- Maven 3.6.0+
10+
- Java 8 (for current examples)
11+
12+
## Directory Structure
13+
14+
```
15+
test-harness/
16+
├── config.sh # Shared configuration
17+
├── deploy/
18+
│ ├── deploy-stack.sh # Deploy single chapter
19+
│ └── deploy-all.sh # Deploy all chapters
20+
├── test/
21+
│ ├── test-stack.sh # Test single chapter
22+
│ ├── test-all.sh # Test all chapters
23+
│ └── tests/ # Per-chapter test scripts
24+
└── teardown/
25+
├── teardown-stack.sh # Delete single stack
26+
└── teardown-all.sh # Delete all stacks
27+
```
28+
29+
## Usage
30+
31+
### Deploy a single chapter
32+
33+
```bash
34+
./test-harness/deploy/deploy-stack.sh chapter2
35+
```
36+
37+
### Deploy all chapters
38+
39+
```bash
40+
./test-harness/deploy/deploy-all.sh
41+
```
42+
43+
### Run tests for a single chapter
44+
45+
```bash
46+
./test-harness/test/test-stack.sh chapter5-api
47+
```
48+
49+
### Run all tests
50+
51+
```bash
52+
./test-harness/test/test-all.sh
53+
```
54+
55+
### Cleanup
56+
57+
```bash
58+
# Single stack
59+
./test-harness/teardown/teardown-stack.sh chapter2
60+
61+
# All stacks
62+
./test-harness/teardown/teardown-all.sh
63+
```
64+
65+
## Stack Naming
66+
67+
Stacks are named using the pattern: `lambda-book-test-{run-id}-{chapter}`
68+
69+
- In CI: Uses `GITHUB_RUN_ID`
70+
- Locally: Uses `local`
71+
72+
Example: `lambda-book-test-local-chapter5-api`
73+
74+
## Chapters Tested
75+
76+
| Chapter | Description | Test Type |
77+
|---------|-------------|-----------|
78+
| chapter2 | Hello World | Lambda invoke |
79+
| chapter3 | Environment Variables | Lambda invoke |
80+
| chapter4 | DynamoDB | Lambda invoke |
81+
| chapter5-api | Weather Events API | HTTP POST/GET |
82+
| chapter5-event-sources | API Gateway | HTTP GET |
83+
84+
## GitHub Actions
85+
86+
The workflow in `.github/workflows/integration-test.yml` runs on:
87+
- Push to `main` or `feature/**` branches
88+
- Pull requests to `main`
89+
- Manual trigger (workflow_dispatch)
90+
91+
It deploys all stacks, runs tests, and always tears down (even on failure).
92+
93+
## Adding Tests for New Chapters
94+
95+
1. Create a test script in `test-harness/test/tests/{chapter-name}.sh`
96+
2. Add the chapter to the `CHAPTERS` array in `config.sh`
97+
98+
## Troubleshooting
99+
100+
### Stack deletion fails
101+
Some stacks have S3 buckets that must be emptied before deletion. The teardown script handles this automatically.
102+
103+
### Test times out
104+
CloudFormation deployments can take 5-10 minutes. If tests fail, check that the stack deployed successfully in the AWS Console.
105+
106+
### API Gateway 403 errors
107+
Ensure the API Gateway stage is deployed. SAM deploy should handle this automatically.

test-harness/config.sh

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/bin/bash
2+
# Shared configuration for test harness
3+
4+
# AWS Configuration
5+
export AWS_REGION="us-east-1"
6+
export AWS_DEFAULT_REGION="us-east-1"
7+
8+
# Stack naming
9+
# Use GITHUB_RUN_ID if available (in CI), otherwise use "local"
10+
export STACK_PREFIX="lambda-book-test-${GITHUB_RUN_ID:-local}"
11+
12+
# Project root (parent of test-harness)
13+
export PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
14+
15+
# Chapters to test (in order)
16+
export CHAPTERS=(
17+
"chapter2"
18+
"chapter3"
19+
"chapter4"
20+
"chapter5-api"
21+
"chapter5-event-sources"
22+
)
23+
24+
# Get stack name for a chapter
25+
get_stack_name() {
26+
local chapter="$1"
27+
echo "${STACK_PREFIX}-${chapter}"
28+
}
29+
30+
# Colors for output
31+
RED='\033[0;31m'
32+
GREEN='\033[0;32m'
33+
YELLOW='\033[1;33m'
34+
NC='\033[0m' # No Color
35+
36+
log_info() {
37+
echo -e "${GREEN}[INFO]${NC} $1"
38+
}
39+
40+
log_warn() {
41+
echo -e "${YELLOW}[WARN]${NC} $1"
42+
}
43+
44+
log_error() {
45+
echo -e "${RED}[ERROR]${NC} $1"
46+
}
47+
48+
# Wait for stack to reach a stable state
49+
wait_for_stack() {
50+
local stack_name="$1"
51+
local timeout="${2:-600}" # Default 10 minutes
52+
53+
log_info "Waiting for stack $stack_name to stabilize..."
54+
55+
aws cloudformation wait stack-create-complete \
56+
--stack-name "$stack_name" \
57+
--region "$AWS_REGION" 2>/dev/null || \
58+
aws cloudformation wait stack-update-complete \
59+
--stack-name "$stack_name" \
60+
--region "$AWS_REGION" 2>/dev/null
61+
62+
return $?
63+
}
64+
65+
# Get stack output value
66+
get_stack_output() {
67+
local stack_name="$1"
68+
local output_key="$2"
69+
70+
aws cloudformation describe-stacks \
71+
--stack-name "$stack_name" \
72+
--region "$AWS_REGION" \
73+
--query "Stacks[0].Outputs[?OutputKey=='${output_key}'].OutputValue" \
74+
--output text
75+
}
76+
77+
# Check if stack exists
78+
stack_exists() {
79+
local stack_name="$1"
80+
aws cloudformation describe-stacks \
81+
--stack-name "$stack_name" \
82+
--region "$AWS_REGION" &>/dev/null
83+
return $?
84+
}

test-harness/deploy/deploy-all.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
# Deploy all chapter stacks
3+
4+
set -e
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
source "$SCRIPT_DIR/../config.sh"
8+
9+
log_info "Deploying all chapters..."
10+
log_info "Stack prefix: $STACK_PREFIX"
11+
12+
FAILED_DEPLOYS=()
13+
14+
for chapter in "${CHAPTERS[@]}"; do
15+
log_info "========================================"
16+
log_info "Deploying: $chapter"
17+
log_info "========================================"
18+
19+
if "$SCRIPT_DIR/deploy-stack.sh" "$chapter"; then
20+
log_info "Successfully deployed $chapter"
21+
else
22+
log_error "Failed to deploy $chapter"
23+
FAILED_DEPLOYS+=("$chapter")
24+
fi
25+
26+
echo ""
27+
done
28+
29+
if [ ${#FAILED_DEPLOYS[@]} -gt 0 ]; then
30+
log_error "Failed deployments: ${FAILED_DEPLOYS[*]}"
31+
exit 1
32+
fi
33+
34+
log_info "All deployments completed successfully!"
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/bin/bash
2+
# Deploy a single chapter stack
3+
4+
set -e
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
source "$SCRIPT_DIR/../config.sh"
8+
9+
if [ -z "$1" ]; then
10+
echo "Usage: $0 <chapter>"
11+
echo "Available chapters: ${CHAPTERS[*]}"
12+
exit 1
13+
fi
14+
15+
CHAPTER="$1"
16+
STACK_NAME=$(get_stack_name "$CHAPTER")
17+
CHAPTER_DIR="$PROJECT_ROOT/$CHAPTER"
18+
19+
if [ ! -d "$CHAPTER_DIR" ]; then
20+
log_error "Chapter directory not found: $CHAPTER_DIR"
21+
exit 1
22+
fi
23+
24+
log_info "Deploying $CHAPTER as stack: $STACK_NAME"
25+
26+
cd "$CHAPTER_DIR"
27+
28+
# Build the project
29+
log_info "Building $CHAPTER..."
30+
if [ -f "pom.xml" ]; then
31+
# Check if it's a multi-module project
32+
if [ -d "bulk-events-stage" ] || [ -d "single-event-stage" ]; then
33+
mvn clean package -DskipTests -q
34+
else
35+
mvn clean package -DskipTests -q
36+
fi
37+
else
38+
log_error "No pom.xml found in $CHAPTER_DIR"
39+
exit 1
40+
fi
41+
42+
# Deploy with SAM
43+
log_info "Deploying with SAM..."
44+
sam deploy \
45+
--stack-name "$STACK_NAME" \
46+
--region "$AWS_REGION" \
47+
--resolve-s3 \
48+
--capabilities CAPABILITY_IAM \
49+
--no-confirm-changeset \
50+
--no-fail-on-empty-changeset
51+
52+
log_info "Successfully deployed $STACK_NAME"
53+
54+
# Show outputs if any
55+
OUTPUTS=$(aws cloudformation describe-stacks \
56+
--stack-name "$STACK_NAME" \
57+
--region "$AWS_REGION" \
58+
--query 'Stacks[0].Outputs' \
59+
--output table 2>/dev/null || echo "No outputs")
60+
61+
if [ "$OUTPUTS" != "No outputs" ] && [ -n "$OUTPUTS" ]; then
62+
log_info "Stack outputs:"
63+
echo "$OUTPUTS"
64+
fi
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/bash
2+
# Teardown all chapter stacks
3+
4+
set -e
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
source "$SCRIPT_DIR/../config.sh"
8+
9+
log_info "Tearing down all stacks..."
10+
log_info "Stack prefix: $STACK_PREFIX"
11+
12+
FAILED_TEARDOWNS=()
13+
14+
# Teardown in reverse order (in case of dependencies)
15+
for ((i=${#CHAPTERS[@]}-1; i>=0; i--)); do
16+
chapter="${CHAPTERS[$i]}"
17+
18+
log_info "========================================"
19+
log_info "Tearing down: $chapter"
20+
log_info "========================================"
21+
22+
if "$SCRIPT_DIR/teardown-stack.sh" "$chapter"; then
23+
log_info "Successfully deleted $chapter stack"
24+
else
25+
log_error "Failed to delete $chapter stack"
26+
FAILED_TEARDOWNS+=("$chapter")
27+
fi
28+
29+
echo ""
30+
done
31+
32+
if [ ${#FAILED_TEARDOWNS[@]} -gt 0 ]; then
33+
log_error "Failed teardowns: ${FAILED_TEARDOWNS[*]}"
34+
exit 1
35+
fi
36+
37+
log_info "All stacks deleted successfully!"

0 commit comments

Comments
 (0)