Skip to content

Commit 6e2c8ce

Browse files
authored
feat: add dockerized testing
2 parents 684c317 + a504b6a commit 6e2c8ce

File tree

9 files changed

+308
-0
lines changed

9 files changed

+308
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: dockerized-test
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
push:
8+
branches: [ main ]
9+
pull_request:
10+
branches: [ '*' ]
11+
workflow_dispatch:
12+
13+
14+
jobs:
15+
dockerized-test:
16+
runs-on: ubuntu-latest
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
ruby_version: ['3.3', '3.4']
21+
22+
steps:
23+
- uses: actions/checkout@v3
24+
25+
- name: Set up ruby
26+
uses: ruby/setup-ruby@v1
27+
with:
28+
ruby-version: ${{ matrix.ruby_version }}
29+
30+
- name: Build the lib
31+
run: make build
32+
33+
- name: Build the image
34+
run: docker build . -t local/test -f Dockerfile.test --build-arg BASE_IMAGE=public.ecr.aws/lambda/ruby:${{ matrix.ruby_version }}
35+
36+
- name: Run tests
37+
uses: aws/containerized-test-runner-for-aws-lambda@main
38+
with:
39+
suiteFileArray: '["./test/dockerized/suites/*.json"]'
40+
dockerImageName: 'local/test'
41+
taskFolder: './test/dockerized/tasks'

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ test/examples/hello-world-docker/pkg
1111
*.iml
1212
.DS_Store
1313
Gemfile.lock
14+
# useful when using rbenv
15+
.ruby-version
16+
# containerized test runner clone
17+
.test-runner/

Dockerfile.test

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
ARG BASE_IMAGE
2+
FROM $BASE_IMAGE
3+
ENV GEM_HOME=/var/runtime
4+
ADD test/dockerized/tasks /var/task
5+
RUN gem uninstall aws_lambda_ric --executables
6+
ADD pkg /tmp/pkg
7+
RUN gem install /tmp/pkg/aws_lambda_ric-*.gem
8+
RUN rm -rf /tmp/pkg

Makefile

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,36 @@ build:
3131
run-local-ric:
3232
scripts/run-local-ric.sh
3333

34+
.PHONY: test-dockerized
35+
test-dockerized:
36+
@echo "Running dockerized tests locally..."
37+
@if [ -z "$(RUBY_VERSION)" ]; then \
38+
echo "Error: RUBY_VERSION is not set. Usage: make test-dockerized RUBY_VERSION=3.3"; \
39+
exit 1; \
40+
fi
41+
@echo "Building the lib..."
42+
$(MAKE) build
43+
@echo "Building Docker image for Ruby $(RUBY_VERSION)..."
44+
docker build . -t local/test -f Dockerfile.test --build-arg BASE_IMAGE=public.ecr.aws/lambda/ruby:$(RUBY_VERSION)
45+
@echo "Setting up containerized test runner..."
46+
@if [ ! -d ".test-runner" ]; then \
47+
echo "Copying local containerized-test-runner-for-aws-lambda..."; \
48+
cp -r ../containerized-test-runner-for-aws-lambda .test-runner; \
49+
fi
50+
@echo "Building test runner Docker image..."
51+
@docker build -t test-runner:local -f .test-runner/Dockerfile .test-runner
52+
@echo "Running tests in Docker..."
53+
@docker run --rm \
54+
--entrypoint suite \
55+
-e DOCKER_API_VERSION=1.41 \
56+
-v /var/run/docker.sock:/var/run/docker.sock \
57+
-v $(CURDIR)/test/dockerized/tasks:$(CURDIR)/test/dockerized/tasks:ro \
58+
-v $(CURDIR)/test/dockerized/suites:/suites:ro \
59+
test-runner:local \
60+
--test-image local/test \
61+
--debug \
62+
/suites/*.json
63+
3464
.PHONY: pr
3565
pr: init test-unit test-smoke
3666

@@ -46,6 +76,7 @@ TARGETS
4676
test-integ Run Integration tests.
4777
test-unit Run Unit Tests.
4878
test-smoke Run Sanity/Smoke tests.
79+
test-dockerized Run dockerized tests locally (requires RUBY_VERSION=X.X).
4980
run-local-ric Run local RIC changes with Runtime Interface Emulator.
5081
pr Perform all checks before submitting a Pull Request.
5182

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,16 @@ Then,
186186
* to run integration tests: `make test-integ`
187187
* to run smoke tests: `make test-smoke`
188188

189+
### Running Dockerized Harness Tests
190+
191+
To run the containerized test harness locally, use:
192+
193+
```shell script
194+
make test-dockerized RUBY_VERSION=3.4
195+
```
196+
197+
This command builds your Lambda function in a Docker container using the specified Ruby version, sets up the containerized test runner, and executes the test suites defined in `test/dockerized/suites/`.
198+
189199
### Troubleshooting
190200
While running integration tests, you might encounter the Docker Hub rate limit error with the following body:
191201
```

test/dockerized/suites/core.json

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
{
2+
"tests": [
3+
{
4+
"name": "test_echo",
5+
"handler": "core.ping",
6+
"request": {
7+
"msg": "message"
8+
},
9+
"assertions": [
10+
{
11+
"response": {
12+
"msg": "pong[message]"
13+
}
14+
}
15+
]
16+
},
17+
{
18+
"name": "test_string_payload",
19+
"handler": "core.str_ping",
20+
"request": "message",
21+
"assertions": [
22+
{
23+
"response": {
24+
"msg": "pong[message]"
25+
}
26+
}
27+
]
28+
},
29+
{
30+
"name": "test_module_echo",
31+
"handler": "core.HandlerClass.ping",
32+
"request": {
33+
"msg": "MyMessage"
34+
},
35+
"assertions": [
36+
{
37+
"response": "Module Message: 'MyMessage'"
38+
}
39+
]
40+
},
41+
{
42+
"name": "test_deep_module_echo",
43+
"handler": "core.DeepModule::Handler.ping",
44+
"request": {
45+
"msg": "MyMessage"
46+
},
47+
"assertions": [
48+
{
49+
"response": "Deep Module Message: 'MyMessage'"
50+
}
51+
]
52+
},
53+
{
54+
"name": "test_error",
55+
"handler": "core.broken",
56+
"request": {
57+
"msg": "message"
58+
},
59+
"assertions": [
60+
{
61+
"errorType": "Function<ArgumentError>"
62+
}
63+
]
64+
},
65+
{
66+
"name": "test_string",
67+
"handler": "core.string",
68+
"request": {
69+
"msg": "MyMessage"
70+
},
71+
"assertions": [
72+
{
73+
"response": "Message: 'MyMessage'"
74+
}
75+
]
76+
}
77+
]
78+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"tests": [
3+
{
4+
"name": "test_ctx_cognito_pool_id",
5+
"handler": "ctx.get_cognito_pool_id",
6+
"cognitoIdentity": {
7+
"cognitoIdentityId": "4ab95ea510c14353a7f6da04489c43b8",
8+
"cognitoIdentityPoolId": "35ab4794a79a4f23947d3e851d3d6578"
9+
},
10+
"request": {},
11+
"assertions": [
12+
{
13+
"response": {
14+
"cognito_pool_id": "35ab4794a79a4f23947d3e851d3d6578"
15+
}
16+
}
17+
]
18+
},
19+
{
20+
"name": "test_ctx_cognito_identity_id",
21+
"handler": "ctx.get_cognito_identity_id",
22+
"cognitoIdentity": {
23+
"cognitoIdentityId": "4ab95ea510c14353a7f6da04489c43b8",
24+
"cognitoIdentityPoolId": "35ab4794a79a4f23947d3e851d3d6578"
25+
},
26+
"request": {},
27+
"assertions": [
28+
{
29+
"response": {
30+
"cognito_identity_id": "4ab95ea510c14353a7f6da04489c43b8"
31+
}
32+
}
33+
]
34+
},
35+
{
36+
"name": "get_remaining_time_in_millis | elapsedTime",
37+
"handler": "ctx.get_remaining_time_from_context",
38+
"request": {
39+
"sleepTimeSeconds": 0.1
40+
},
41+
"assertions": [
42+
{
43+
"transform": ".elapsedTime >= 100",
44+
"response": true
45+
}
46+
]
47+
}
48+
]
49+
}

test/dockerized/tasks/core.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
3+
def ping(event:, context:)
4+
resp = {}
5+
if event.nil?
6+
resp[:event_nil] = true
7+
else
8+
resp[:msg] = "pong[#{event["msg"]}]"
9+
end
10+
puts "Hello, loggers!"
11+
resp
12+
end
13+
14+
def str_ping(event:, context:)
15+
{ msg: "pong[#{event}]" }
16+
end
17+
18+
def broken(_)
19+
raise ArgumentError.new("My error message.")
20+
end
21+
22+
def string(event:, context:)
23+
"Message: '#{event["msg"]}'"
24+
end
25+
26+
def curl(event:,context:)
27+
resp = Net::HTTP.get(URI(event["url"]))
28+
if resp.size > 0
29+
{ success: true }
30+
else
31+
raise "Empty response!"
32+
end
33+
end
34+
35+
def io(_)
36+
StringIO.new("This is IO!")
37+
end
38+
39+
def execution_env(_)
40+
{ "AWS_EXECUTION_ENV" => ENV["AWS_EXECUTION_ENV"] }
41+
end
42+
43+
class HandlerClass
44+
def self.ping(event:,context:)
45+
"Module Message: '#{event["msg"]}'"
46+
end
47+
end
48+
49+
module DeepModule
50+
class Handler
51+
def self.ping(event:,context:)
52+
"Deep Module Message: '#{event["msg"]}'"
53+
end
54+
end
55+
end

test/dockerized/tasks/ctx.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
3+
def get_context(event:,context:)
4+
{
5+
function_name: context.function_name,
6+
deadline_ns: context.deadline_ns,
7+
aws_request_id: context.aws_request_id,
8+
invoked_function_arn: context.invoked_function_arn,
9+
log_group_name: context.log_group_name,
10+
log_stream_name: context.log_stream_name,
11+
memory_limit_in_mb: context.memory_limit_in_mb,
12+
function_version: context.function_version
13+
}
14+
end
15+
16+
def get_cognito_pool_id(event:,context:)
17+
{ cognito_pool_id: context.identity&.dig("cognitoIdentityPoolId")}
18+
end
19+
20+
def get_cognito_identity_id(event:,context:)
21+
{ cognito_identity_id: context.identity&.dig("cognitoIdentityId") }
22+
end
23+
24+
def echo_context(event:,context:)
25+
context.client_context
26+
end
27+
28+
def get_remaining_time_from_context(event:, context:)
29+
before = context.get_remaining_time_in_millis()
30+
sleep(event['sleepTimeSeconds'])
31+
return { elapsedTime: before - context.get_remaining_time_in_millis() }
32+
end

0 commit comments

Comments
 (0)