Skip to content

Commit 9446919

Browse files
authored
fix awscli patch for new create_nested_client, introduce bats tests (#93)
1 parent 034919b commit 9446919

File tree

10 files changed

+185
-16
lines changed

10 files changed

+185
-16
lines changed

.github/workflows/integration.yml

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ jobs:
1717
strategy:
1818
matrix:
1919
include:
20-
- { python-version: '3.12', region: us-west-1, access-key-id: '000000000001'}
20+
- { python-version: '3.13', awscli-version: "1.41.0", region: us-west-1, access-key-id: '000000000001'}
21+
- { python-version: '3.13', region: us-west-1, access-key-id: 'test'}
2122
- { python-version: '3.12', region: us-east-1, access-key-id: 'test'}
2223
- { python-version: '3.11', region: us-east-1, access-key-id: 'test'}
2324
- { python-version: '3.10', region: us-east-1, access-key-id: 'test'}
2425
- { python-version: '3.9', region: us-east-1, access-key-id: 'test'}
25-
- { python-version: '3.8', region: us-east-1, access-key-id: 'test'}
2626

2727
env:
2828
AWS_ACCESS_KEY_ID: ${{ matrix.access-key-id }}
@@ -44,22 +44,59 @@ jobs:
4444
- name: Install deps
4545
run: pip install '.[ver1]'
4646

47+
- name: Install specific version of awscli
48+
if: ${{ matrix.awscli-version }}
49+
run: pip install "awscli==${{ matrix.awscli-version }}"
50+
4751
- name: Check aws-cli version
4852
run: |
4953
aws --version
5054
awslocal --version
5155
which aws
5256
which awslocal
5357
54-
- name: Start and wait for localstack (Community)
58+
- name: Setup BATS
59+
run: |
60+
git clone https://github.com/bats-core/bats-core.git "$HOME"/bats-core
61+
cd "$HOME"/bats-core
62+
sudo ./install.sh /usr/local
63+
64+
- name: Start and wait for LocalStack (Community)
5565
timeout-minutes: 10
5666
run: |
5767
docker pull localstack/localstack:latest
5868
localstack start -d
5969
localstack wait -t 30
6070
61-
- name: Test usage of awslocal
71+
- name: Run bats tests
6272
run: |
63-
awslocal lambda list-functions
64-
awslocal s3api list-buckets
65-
awslocal s3 ls
73+
bats --report-formatter junit -r tests/bin/ --output .
74+
mv report.xml tests-junit-bin-${{ matrix.python-version }}-${{ matrix.awscli-version }}-${{ matrix.region }}-${{ matrix.access-key-id }}.xml
75+
76+
- name: Archive Test Results
77+
uses: actions/upload-artifact@v4
78+
if: success() || failure()
79+
with:
80+
name: test-results-tests-bin-${{ matrix.python-version }}-${{ matrix.awscli-version }}-${{ matrix.region }}-${{ matrix.access-key-id }}
81+
path: tests-junit-*.xml
82+
retention-days: 30
83+
84+
report:
85+
runs-on: ubuntu-latest
86+
needs: test
87+
if: success() || failure()
88+
steps:
89+
- name: Download Test Results
90+
uses: actions/download-artifact@v4
91+
with:
92+
pattern: test-results-tests-*
93+
merge-multiple: true
94+
95+
- name: Publish Test Results
96+
uses: EnricoMi/publish-unit-test-result-action@v2
97+
if: success() || failure()
98+
with:
99+
files: tests-junit-*.xml
100+
check_name: "Smoke Tests"
101+
action_fail_on_inconclusive: true
102+

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
*.egg-info/
44
*.pyc
55
__pycache__/
6+
/.idea/
7+
/output/*
8+
!/output/.gitkeep

bin/awslocal

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ import os
2121
import sys
2222
import subprocess
2323
import re
24-
from threading import Thread
25-
26-
from boto3.session import Session
2724

2825
PARENT_FOLDER = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
2926
S3_VIRTUAL_ENDPOINT_HOSTNAME = 's3.localhost.localstack.cloud'
@@ -100,6 +97,7 @@ def prepare_environment():
10097

10198
env_dict.pop('AWS_DATA_PATH', None)
10299

100+
from boto3.session import Session
103101
session = Session()
104102
credentials = session.get_credentials()
105103

@@ -236,14 +234,14 @@ def patch_awscli_libs():
236234
# get stack frame of caller
237235
curframe = inspect.currentframe()
238236
calframe = inspect.getouterframes(curframe, 2)
239-
fname = calframe[1].filename
240237

241-
# check if we are executing within the target method
242-
is_target = (os.path.join('cloudformation', 'deploy.py') in fname
243-
or os.path.join('cloudformation', 'package.py') in fname)
244-
if is_target:
238+
# try to find the Cfn deploy or package frame
239+
cfn_frame = next(filter(lambda frame: os.path.join('cloudformation', 'deploy.py') in frame.filename
240+
or os.path.join('cloudformation', 'package.py') in frame.filename, calframe), None)
241+
242+
if cfn_frame:
245243
if 'endpoint_url' not in kwargs:
246-
args_passed = inspect.getargvalues(calframe[1].frame).locals
244+
args_passed = inspect.getargvalues(cfn_frame.frame).locals
247245
kwargs['endpoint_url'] = args_passed['parsed_args'].s3_endpoint_url
248246
return create_client_orig(*args, **kwargs)
249247

output/.gitkeep

Whitespace-only changes.

tests/bin/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# BATS tests
2+
3+
The tests in this folder are not regular Pytest tests.
4+
They are implemented with [BATS](https://github.com/bats-core/bats-core) to test `awslocal` from `bash`.
5+
6+
## Prerequisites
7+
8+
**Install BATS**: If you don't have BATS installed, you need to install it first. On a Unix-like system, you can usually install it using a package manager.
9+
10+
For any system with `npm`:
11+
```bash
12+
npm install -g bats
13+
```
14+
15+
For macOS using Homebrew:
16+
```bash
17+
brew install bats-core
18+
```
19+
20+
Alternatively, you can install BATS manually by cloning the repository and adding the `bin` folder to your `PATH` environment variable.
21+
```bash
22+
git clone https://github.com/bats-core/bats-core.git
23+
cd bats-core
24+
sudo ./install.sh /usr/local
25+
```
26+
27+
## Writing tests
28+
29+
Create a file with a `.bats` extension, for example, `test_example.bats`. Here’s a simple test file:
30+
31+
```bash
32+
#!/usr/bin/env bats
33+
@test "test description" {
34+
run echo "hello"
35+
[ "$status" -eq 0 ]
36+
[ "$output" = "hello" ]
37+
}
38+
```
39+
40+
## Running Tests
41+
To run the tests, execute the bats command followed by the test file or directory containing test files:
42+
43+
```bash
44+
bats test_example.bats
45+
```
46+
47+
You can also run all `.bats` files in a directory:
48+
49+
```bash
50+
bats tests/bin
51+
```
52+
53+
To run with some debug information, you can use this:
54+
55+
```bash
56+
bats --trace --verbose-run --print-output-on-failure -r tests/bin/
57+
```
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "test-lambda",
3+
"version": "0.0.1"
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
exports.handler = async (event, context) => { return "Pong!" };
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
Resources:
4+
TestLambda:
5+
Type: AWS::Serverless::Function
6+
Properties:
7+
FunctionName: TestLambda
8+
CodeUri: TestLambda
9+
Handler: src/app.handler
10+
Runtime: nodejs22.x
11+
TestLambdaUrl:
12+
Type: AWS::Lambda::Url
13+
Properties:
14+
TargetFunctionArn: !GetAtt TestLambda.Arn
15+
AuthType: NONE
16+
Outputs:
17+
LambdaURL:
18+
Description: URL for the Lambda
19+
Value: !GetAtt TestLambdaUrl.FunctionUrl

tests/bin/test.cloudformation.bats

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bats
2+
3+
setup_file() {
4+
# Ensure the AWS CLI is configured to use LocalStack
5+
export AWS_ACCESS_KEY_ID=test
6+
export AWS_SECRET_ACCESS_KEY=test
7+
export AWS_DEFAULT_REGION=us-east-1
8+
9+
# Create a bucket for the test
10+
awslocal s3 mb s3://cfn-package-lambdas
11+
}
12+
13+
@test "cloudformation package, deploy, execute" {
14+
# Package the template
15+
run awslocal cloudformation package --template $BATS_TEST_DIRNAME/cfn_templates/lamba/template.yaml --output-template output/packaged_template.yaml --s3-bucket cfn-package-lambdas
16+
[ "$status" -eq 0 ]
17+
run awslocal cloudformation deploy --template-file output/packaged_template.yaml --stack-name test-stack
18+
[ "$status" -eq 0 ]
19+
URL=`awslocal cloudformation describe-stacks --stack-name test-stack --query "Stacks[0].Outputs[?OutputKey=='LambdaURL'].OutputValue" --output text`
20+
curl -s $URL | grep "Pong!"
21+
[ "$status" -eq 0 ]
22+
}
23+
24+
teardown_file() {
25+
awslocal cloudformation delete-stack --stack-name test-stack
26+
awslocal s3 rb s3://cfn-package-lambdas --force
27+
}

tests/bin/test.smoke.bats

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bats
2+
3+
setup_file() {
4+
# Ensure the AWS CLI is configured to use LocalStack
5+
export AWS_ACCESS_KEY_ID=test
6+
export AWS_SECRET_ACCESS_KEY=test
7+
export AWS_DEFAULT_REGION=us-east-1
8+
}
9+
10+
@test "lambda list-functions" {
11+
run awslocal lambda list-functions
12+
[ "$status" -eq 0 ]
13+
}
14+
15+
@test "s3api list-buckets" {
16+
run awslocal s3api list-buckets
17+
[ "$status" -eq 0 ]
18+
}
19+
20+
@test "s3 ls" {
21+
run awslocal s3api list-buckets
22+
[ "$status" -eq 0 ]
23+
}

0 commit comments

Comments
 (0)