testcontainers-aws 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- testcontainers_aws-0.1.0/LICENSE +20 -0
- testcontainers_aws-0.1.0/MANIFEST.in +7 -0
- testcontainers_aws-0.1.0/PKG-INFO +576 -0
- testcontainers_aws-0.1.0/README.md +540 -0
- testcontainers_aws-0.1.0/examples/auto_init.py +99 -0
- testcontainers_aws-0.1.0/examples/basic_usage.py +35 -0
- testcontainers_aws-0.1.0/examples/lambda_example.py +140 -0
- testcontainers_aws-0.1.0/examples/lambda_sns_integration.py +201 -0
- testcontainers_aws-0.1.0/examples/pytest_example.py +88 -0
- testcontainers_aws-0.1.0/examples/sns_example.py +163 -0
- testcontainers_aws-0.1.0/pyproject.toml +73 -0
- testcontainers_aws-0.1.0/requirements-dev.txt +8 -0
- testcontainers_aws-0.1.0/requirements.txt +3 -0
- testcontainers_aws-0.1.0/setup.cfg +4 -0
- testcontainers_aws-0.1.0/testcontainers_aws/__init__.py +12 -0
- testcontainers_aws-0.1.0/testcontainers_aws/core.py +261 -0
- testcontainers_aws-0.1.0/testcontainers_aws/fixtures.py +291 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/__init__.py +17 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/base.py +25 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/dynamodb.py +168 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/kinesis.py +181 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/lambda_service.py +334 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/s3.py +104 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/sns.py +307 -0
- testcontainers_aws-0.1.0/testcontainers_aws/services/sqs.py +167 -0
- testcontainers_aws-0.1.0/testcontainers_aws.egg-info/PKG-INFO +576 -0
- testcontainers_aws-0.1.0/testcontainers_aws.egg-info/SOURCES.txt +32 -0
- testcontainers_aws-0.1.0/testcontainers_aws.egg-info/dependency_links.txt +1 -0
- testcontainers_aws-0.1.0/testcontainers_aws.egg-info/requires.txt +10 -0
- testcontainers_aws-0.1.0/testcontainers_aws.egg-info/top_level.txt +1 -0
- testcontainers_aws-0.1.0/tests/__init__.py +1 -0
- testcontainers_aws-0.1.0/tests/test_core.py +117 -0
- testcontainers_aws-0.1.0/tests/test_lambda_sns.py +405 -0
- testcontainers_aws-0.1.0/tests/test_services.py +150 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 testcontainers-aws contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testcontainers-aws
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simplified AWS service testing with Testcontainers - orchestrate LocalStack and AWS services for integration tests
|
|
5
|
+
Author-email: Manoj Kengudelu <mkengudelu@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/manojkken/testcontainers-aws
|
|
8
|
+
Project-URL: Documentation, https://github.com/manojkken/testcontainers-aws#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/manojkken/testcontainers-aws
|
|
10
|
+
Project-URL: Issues, https://github.com/manojkken/testcontainers-aws/issues
|
|
11
|
+
Keywords: testcontainers,aws,testing,localstack,docker,s3,dynamodb,sqs
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Testing
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.8
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: testcontainers[localstack]>=4.0.0
|
|
27
|
+
Requires-Dist: boto3>=1.28.0
|
|
28
|
+
Requires-Dist: botocore>=1.31.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# testcontainers-aws
|
|
38
|
+
|
|
39
|
+
**Simplified AWS service testing with Testcontainers** - Orchestrate LocalStack and AWS services for integration tests with minimal boilerplate.
|
|
40
|
+
|
|
41
|
+
[](https://badge.fury.io/py/testcontainers-aws)
|
|
42
|
+
[](https://pypi.org/project/testcontainers-aws/)
|
|
43
|
+
[](https://opensource.org/licenses/MIT)
|
|
44
|
+
|
|
45
|
+
## Why testcontainers-aws?
|
|
46
|
+
|
|
47
|
+
Testing AWS services locally is tedious:
|
|
48
|
+
- Manual container setup for each service
|
|
49
|
+
- Repetitive boto3 client configuration
|
|
50
|
+
- Boilerplate code for resource initialization
|
|
51
|
+
- No pytest fixtures for common patterns
|
|
52
|
+
|
|
53
|
+
**testcontainers-aws** solves this:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from testcontainers_aws import AWSTestEnvironment
|
|
57
|
+
|
|
58
|
+
# Before: Manual setup with testcontainers
|
|
59
|
+
with LocalStackContainer() as localstack:
|
|
60
|
+
localstack.execInContainer("awslocal", "s3", "mb", "s3://bucket")
|
|
61
|
+
client = boto3.client('s3', endpoint_url=localstack.get_url(), ...)
|
|
62
|
+
# More configuration...
|
|
63
|
+
|
|
64
|
+
# After: One line with testcontainers-aws
|
|
65
|
+
with AWSTestEnvironment(services=['s3', 'dynamodb']) as aws:
|
|
66
|
+
s3 = aws.get_s3_client() # Pre-configured, ready to use
|
|
67
|
+
aws.s3.create_bucket('test-bucket') # Helper methods
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Features
|
|
71
|
+
|
|
72
|
+
- ✅ **Single container orchestration** - Spin up multiple AWS services in one LocalStack container
|
|
73
|
+
- ✅ **Auto-configured boto3 clients** - No manual endpoint/credential setup
|
|
74
|
+
- ✅ **Service helper methods** - Create buckets, tables, queues with simple method calls
|
|
75
|
+
- ✅ **Pytest fixtures** - Ready-to-use fixtures for common testing patterns
|
|
76
|
+
- ✅ **Resource auto-initialization** - Define resources declaratively, created on startup
|
|
77
|
+
- ✅ **Automatic cleanup** - Resources cleaned up after tests complete
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install testcontainers-aws
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Requires:
|
|
86
|
+
- Python 3.8+
|
|
87
|
+
- Docker installed and running
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
### Basic Usage
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from testcontainers_aws import AWSTestEnvironment
|
|
95
|
+
|
|
96
|
+
# Create environment with services
|
|
97
|
+
with AWSTestEnvironment(services=['s3', 'dynamodb', 'sqs']) as aws:
|
|
98
|
+
# Get pre-configured clients
|
|
99
|
+
s3 = aws.get_s3_client()
|
|
100
|
+
dynamodb = aws.get_dynamodb_client()
|
|
101
|
+
sqs = aws.get_sqs_client()
|
|
102
|
+
|
|
103
|
+
# Use service helper methods
|
|
104
|
+
aws.s3.create_bucket('test-bucket')
|
|
105
|
+
aws.s3.put_object('test-bucket', 'key.txt', b'Hello World')
|
|
106
|
+
|
|
107
|
+
# Use boto3 clients directly
|
|
108
|
+
response = s3.get_object(Bucket='test-bucket', Key='key.txt')
|
|
109
|
+
print(response['Body'].read()) # b'Hello World'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### With Auto-Initialization
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# Define resources upfront - created automatically
|
|
116
|
+
with AWSTestEnvironment(
|
|
117
|
+
services=['s3', 'dynamodb', 'sqs'],
|
|
118
|
+
init_config={
|
|
119
|
+
's3_buckets': ['uploads', 'archives'],
|
|
120
|
+
'dynamodb_tables': [
|
|
121
|
+
{
|
|
122
|
+
'name': 'users',
|
|
123
|
+
'key_schema': [{'AttributeName': 'id', 'KeyType': 'HASH'}],
|
|
124
|
+
'attribute_definitions': [{'AttributeName': 'id', 'AttributeType': 'S'}]
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
'sqs_queues': ['notifications', 'tasks']
|
|
128
|
+
}
|
|
129
|
+
) as aws:
|
|
130
|
+
# Everything is ready to use
|
|
131
|
+
aws.s3.put_object('uploads', 'file.txt', b'data')
|
|
132
|
+
aws.dynamodb.put_item('users', {'id': {'S': '123'}, 'name': {'S': 'Alice'}})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### With Pytest
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
import pytest
|
|
139
|
+
from testcontainers_aws.fixtures import aws_environment, s3_bucket
|
|
140
|
+
|
|
141
|
+
def test_s3_operations(aws_environment):
|
|
142
|
+
"""Test with session-scoped environment (shared across tests)."""
|
|
143
|
+
s3 = aws_environment.get_s3_client()
|
|
144
|
+
aws_environment.s3.create_bucket('test-bucket')
|
|
145
|
+
|
|
146
|
+
# Your test code
|
|
147
|
+
aws_environment.s3.put_object('test-bucket', 'test.txt', b'content')
|
|
148
|
+
assert s3.list_objects_v2(Bucket='test-bucket')['KeyCount'] == 1
|
|
149
|
+
|
|
150
|
+
def test_with_auto_cleanup_bucket(s3_bucket, aws_environment):
|
|
151
|
+
"""Test with function-scoped bucket (auto-created and cleaned up)."""
|
|
152
|
+
aws_environment.s3.put_object(s3_bucket, 'file.txt', b'data')
|
|
153
|
+
|
|
154
|
+
# Bucket is automatically deleted after test
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Supported Services
|
|
158
|
+
|
|
159
|
+
| Service | Client Method | Helper Property |
|
|
160
|
+
|---------|--------------|-----------------|
|
|
161
|
+
| S3 | `get_s3_client()` | `aws.s3` |
|
|
162
|
+
| DynamoDB | `get_dynamodb_client()` | `aws.dynamodb` |
|
|
163
|
+
| SQS | `get_sqs_client()` | `aws.sqs` |
|
|
164
|
+
| Kinesis | `get_kinesis_client()` | `aws.kinesis` |
|
|
165
|
+
| SNS | `get_sns_client()` | `aws.sns` |
|
|
166
|
+
| Lambda | `get_lambda_client()` | `aws.lambda_` |
|
|
167
|
+
|
|
168
|
+
## Service Helpers
|
|
169
|
+
|
|
170
|
+
### S3 Operations
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
# Create bucket
|
|
174
|
+
aws.s3.create_bucket('my-bucket')
|
|
175
|
+
|
|
176
|
+
# Upload object
|
|
177
|
+
aws.s3.put_object('my-bucket', 'path/file.txt', b'content')
|
|
178
|
+
|
|
179
|
+
# Download object
|
|
180
|
+
response = aws.s3.get_object('my-bucket', 'path/file.txt')
|
|
181
|
+
|
|
182
|
+
# Delete bucket (with force to delete all objects)
|
|
183
|
+
aws.s3.delete_bucket('my-bucket', force=True)
|
|
184
|
+
|
|
185
|
+
# List all buckets
|
|
186
|
+
buckets = aws.s3.list_buckets()
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### DynamoDB Operations
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
# Create table
|
|
193
|
+
aws.dynamodb.create_table(
|
|
194
|
+
name='users',
|
|
195
|
+
key_schema=[{'AttributeName': 'id', 'KeyType': 'HASH'}],
|
|
196
|
+
attribute_definitions=[{'AttributeName': 'id', 'AttributeType': 'S'}]
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Put item
|
|
200
|
+
aws.dynamodb.put_item('users', {'id': {'S': '123'}, 'name': {'S': 'Alice'}})
|
|
201
|
+
|
|
202
|
+
# Get item
|
|
203
|
+
item = aws.dynamodb.get_item('users', {'id': {'S': '123'}})
|
|
204
|
+
|
|
205
|
+
# Query table
|
|
206
|
+
results = aws.dynamodb.query(
|
|
207
|
+
'users',
|
|
208
|
+
'id = :id',
|
|
209
|
+
{':id': {'S': '123'}}
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# List all tables
|
|
213
|
+
tables = aws.dynamodb.list_tables()
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### SQS Operations
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
# Create queue
|
|
220
|
+
response = aws.sqs.create_queue('my-queue')
|
|
221
|
+
queue_url = response['QueueUrl']
|
|
222
|
+
|
|
223
|
+
# Send message
|
|
224
|
+
aws.sqs.send_message(queue_url, 'Hello from SQS')
|
|
225
|
+
|
|
226
|
+
# Receive messages
|
|
227
|
+
messages = aws.sqs.receive_messages(queue_url, max_messages=10)
|
|
228
|
+
|
|
229
|
+
# Delete message
|
|
230
|
+
for msg in messages:
|
|
231
|
+
aws.sqs.delete_message(queue_url, msg['ReceiptHandle'])
|
|
232
|
+
|
|
233
|
+
# List queues
|
|
234
|
+
queues = aws.sqs.list_queues()
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Kinesis Operations
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
# Create stream
|
|
241
|
+
aws.kinesis.create_stream('my-stream', shard_count=1)
|
|
242
|
+
|
|
243
|
+
# Put record
|
|
244
|
+
aws.kinesis.put_record('my-stream', b'data', partition_key='key1')
|
|
245
|
+
|
|
246
|
+
# Put multiple records
|
|
247
|
+
aws.kinesis.put_records('my-stream', [
|
|
248
|
+
{'Data': b'record1', 'PartitionKey': 'key1'},
|
|
249
|
+
{'Data': b'record2', 'PartitionKey': 'key2'}
|
|
250
|
+
])
|
|
251
|
+
|
|
252
|
+
# Get shard iterator
|
|
253
|
+
shard_iterator = aws.kinesis.get_shard_iterator(
|
|
254
|
+
'my-stream',
|
|
255
|
+
'shardId-000000000000',
|
|
256
|
+
'TRIM_HORIZON'
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
# Get records
|
|
260
|
+
records = aws.kinesis.get_records(shard_iterator)
|
|
261
|
+
|
|
262
|
+
# List streams
|
|
263
|
+
streams = aws.kinesis.list_streams()
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Lambda Operations
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from testcontainers_aws.services.lambda_service import create_lambda_zip
|
|
270
|
+
|
|
271
|
+
# Create Lambda function
|
|
272
|
+
code = create_lambda_zip("""
|
|
273
|
+
def handler(event, context):
|
|
274
|
+
return {'statusCode': 200, 'body': 'Hello World'}
|
|
275
|
+
""")
|
|
276
|
+
|
|
277
|
+
aws.lambda_.create_function(
|
|
278
|
+
function_name='my-function',
|
|
279
|
+
runtime='python3.11',
|
|
280
|
+
handler='index.handler',
|
|
281
|
+
code=code,
|
|
282
|
+
environment_variables={'ENV': 'test'}
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Invoke function
|
|
286
|
+
response = aws.lambda_.invoke_function(
|
|
287
|
+
'my-function',
|
|
288
|
+
payload={'key': 'value'}
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
result = json.loads(response['Payload'].read())
|
|
292
|
+
|
|
293
|
+
# Update function code
|
|
294
|
+
new_code = create_lambda_zip("def handler(event, context): return {'updated': True}")
|
|
295
|
+
aws.lambda_.update_function_code('my-function', new_code)
|
|
296
|
+
|
|
297
|
+
# List functions
|
|
298
|
+
functions = aws.lambda_.list_functions()
|
|
299
|
+
|
|
300
|
+
# Delete function
|
|
301
|
+
aws.lambda_.delete_function('my-function')
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### SNS Operations
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
# Create topic
|
|
308
|
+
response = aws.sns.create_topic('notifications')
|
|
309
|
+
topic_arn = response['TopicArn']
|
|
310
|
+
|
|
311
|
+
# Publish message
|
|
312
|
+
aws.sns.publish(
|
|
313
|
+
topic_arn=topic_arn,
|
|
314
|
+
message='Hello from SNS!',
|
|
315
|
+
subject='Notification'
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
# Subscribe SQS queue to topic
|
|
319
|
+
aws.sns.subscribe(
|
|
320
|
+
topic_arn=topic_arn,
|
|
321
|
+
protocol='sqs',
|
|
322
|
+
endpoint='arn:aws:sqs:us-east-1:000000000000:my-queue'
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# Subscribe Lambda to topic
|
|
326
|
+
aws.sns.subscribe(
|
|
327
|
+
topic_arn=topic_arn,
|
|
328
|
+
protocol='lambda',
|
|
329
|
+
endpoint='arn:aws:lambda:us-east-1:000000000000:function:my-function'
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# Publish batch messages
|
|
333
|
+
aws.sns.publish_batch(
|
|
334
|
+
topic_arn=topic_arn,
|
|
335
|
+
messages=[
|
|
336
|
+
{'Id': '1', 'Message': 'First message'},
|
|
337
|
+
{'Id': '2', 'Message': 'Second message'}
|
|
338
|
+
]
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Get topic attributes
|
|
342
|
+
attributes = aws.sns.get_topic_attributes(topic_arn)
|
|
343
|
+
|
|
344
|
+
# List topics
|
|
345
|
+
topics = aws.sns.list_topics()
|
|
346
|
+
|
|
347
|
+
# List subscriptions
|
|
348
|
+
subscriptions = aws.sns.list_subscriptions_by_topic(topic_arn)
|
|
349
|
+
|
|
350
|
+
# Delete topic
|
|
351
|
+
aws.sns.delete_topic(topic_arn)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Pytest Fixtures
|
|
355
|
+
|
|
356
|
+
testcontainers-aws provides several built-in fixtures:
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
from testcontainers_aws.fixtures import (
|
|
360
|
+
aws_environment, # Session-scoped AWS environment
|
|
361
|
+
aws_environment_function, # Function-scoped AWS environment
|
|
362
|
+
s3_client, # Session-scoped S3 client
|
|
363
|
+
dynamodb_client, # Session-scoped DynamoDB client
|
|
364
|
+
sqs_client, # Session-scoped SQS client
|
|
365
|
+
kinesis_client, # Session-scoped Kinesis client
|
|
366
|
+
lambda_client, # Session-scoped Lambda client
|
|
367
|
+
sns_client, # Session-scoped SNS client
|
|
368
|
+
s3_bucket, # Function-scoped bucket (auto-cleanup)
|
|
369
|
+
dynamodb_table, # Function-scoped table (auto-cleanup)
|
|
370
|
+
sqs_queue, # Function-scoped queue (auto-cleanup)
|
|
371
|
+
kinesis_stream, # Function-scoped stream (auto-cleanup)
|
|
372
|
+
lambda_function, # Function-scoped Lambda function (auto-cleanup)
|
|
373
|
+
sns_topic, # Function-scoped SNS topic (auto-cleanup)
|
|
374
|
+
)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Custom Fixtures
|
|
378
|
+
|
|
379
|
+
Create your own fixtures for common patterns:
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
import pytest
|
|
383
|
+
from testcontainers_aws import AWSTestEnvironment
|
|
384
|
+
|
|
385
|
+
@pytest.fixture(scope='session')
|
|
386
|
+
def aws_with_data():
|
|
387
|
+
"""Custom fixture with pre-seeded data."""
|
|
388
|
+
with AWSTestEnvironment(
|
|
389
|
+
services=['dynamodb'],
|
|
390
|
+
init_config={
|
|
391
|
+
'dynamodb_tables': [
|
|
392
|
+
{
|
|
393
|
+
'name': 'users',
|
|
394
|
+
'key_schema': [{'AttributeName': 'id', 'KeyType': 'HASH'}],
|
|
395
|
+
'attribute_definitions': [{'AttributeName': 'id', 'AttributeType': 'S'}]
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
) as aws:
|
|
400
|
+
# Seed test data
|
|
401
|
+
for i in range(10):
|
|
402
|
+
aws.dynamodb.put_item('users', {
|
|
403
|
+
'id': {'S': f'user-{i}'},
|
|
404
|
+
'name': {'S': f'User {i}'}
|
|
405
|
+
})
|
|
406
|
+
|
|
407
|
+
yield aws
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## Configuration
|
|
411
|
+
|
|
412
|
+
### Specify LocalStack Version
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
with AWSTestEnvironment(
|
|
416
|
+
services=['s3'],
|
|
417
|
+
image_tag='2.3.0' # Specific LocalStack version
|
|
418
|
+
) as aws:
|
|
419
|
+
# ...
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### Access Container Details
|
|
423
|
+
|
|
424
|
+
```python
|
|
425
|
+
with AWSTestEnvironment(services=['s3']) as aws:
|
|
426
|
+
# Get endpoint URL
|
|
427
|
+
endpoint = aws.endpoint_url
|
|
428
|
+
print(f"LocalStack running at: {endpoint}")
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Examples
|
|
432
|
+
|
|
433
|
+
See the [examples/](examples/) directory for complete working examples:
|
|
434
|
+
|
|
435
|
+
- `basic_usage.py` - Simple S3 operations
|
|
436
|
+
- `pytest_example.py` - Pytest integration examples
|
|
437
|
+
- `auto_init.py` - Auto-initialization patterns
|
|
438
|
+
- `lambda_example.py` - Lambda function creation and invocation
|
|
439
|
+
- `sns_example.py` - SNS topics, publishing, and subscriptions
|
|
440
|
+
- `lambda_sns_integration.py` - Event-driven architecture with Lambda and SNS
|
|
441
|
+
|
|
442
|
+
## Real-World Use Cases
|
|
443
|
+
|
|
444
|
+
### Testing S3 Upload/Download Logic
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
def test_file_storage_service(aws_environment):
|
|
448
|
+
storage = FileStorageService(
|
|
449
|
+
s3_client=aws_environment.get_s3_client(),
|
|
450
|
+
bucket='uploads'
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
aws_environment.s3.create_bucket('uploads')
|
|
454
|
+
|
|
455
|
+
# Test upload
|
|
456
|
+
storage.upload_file('test.txt', b'content')
|
|
457
|
+
|
|
458
|
+
# Test download
|
|
459
|
+
data = storage.download_file('test.txt')
|
|
460
|
+
assert data == b'content'
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### Testing DynamoDB Repository
|
|
464
|
+
|
|
465
|
+
```python
|
|
466
|
+
def test_user_repository(aws_environment):
|
|
467
|
+
# Create table
|
|
468
|
+
aws_environment.dynamodb.create_table(
|
|
469
|
+
name='users',
|
|
470
|
+
key_schema=[{'AttributeName': 'user_id', 'KeyType': 'HASH'}],
|
|
471
|
+
attribute_definitions=[{'AttributeName': 'user_id', 'AttributeType': 'S'}]
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
repo = UserRepository(aws_environment.get_dynamodb_client())
|
|
475
|
+
|
|
476
|
+
# Test operations
|
|
477
|
+
repo.create_user('123', 'Alice')
|
|
478
|
+
user = repo.get_user('123')
|
|
479
|
+
assert user['name'] == 'Alice'
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Testing Event-Driven Architecture
|
|
483
|
+
|
|
484
|
+
```python
|
|
485
|
+
def test_event_processor(aws_environment):
|
|
486
|
+
from testcontainers_aws.services.lambda_service import create_lambda_zip
|
|
487
|
+
|
|
488
|
+
# Create SNS topic
|
|
489
|
+
topic_response = aws_environment.sns.create_topic('events')
|
|
490
|
+
topic_arn = topic_response['TopicArn']
|
|
491
|
+
|
|
492
|
+
# Create Lambda processor
|
|
493
|
+
code = create_lambda_zip("""
|
|
494
|
+
def handler(event, context):
|
|
495
|
+
for record in event.get('Records', []):
|
|
496
|
+
# Process SNS message
|
|
497
|
+
message = record['Sns']['Message']
|
|
498
|
+
print(f'Processing: {message}')
|
|
499
|
+
return {'statusCode': 200}
|
|
500
|
+
""")
|
|
501
|
+
|
|
502
|
+
aws_environment.lambda_.create_function(
|
|
503
|
+
function_name='event-processor',
|
|
504
|
+
runtime='python3.11',
|
|
505
|
+
handler='index.handler',
|
|
506
|
+
code=code
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
# Subscribe Lambda to SNS
|
|
510
|
+
lambda_arn = 'arn:aws:lambda:us-east-1:000000000000:function:event-processor'
|
|
511
|
+
aws_environment.sns.subscribe(
|
|
512
|
+
topic_arn=topic_arn,
|
|
513
|
+
protocol='lambda',
|
|
514
|
+
endpoint=lambda_arn
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
# Publish event
|
|
518
|
+
aws_environment.sns.publish(
|
|
519
|
+
topic_arn=topic_arn,
|
|
520
|
+
message='{"type": "user_created", "user_id": "123"}'
|
|
521
|
+
)
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Contributing
|
|
525
|
+
|
|
526
|
+
Contributions welcome! Please:
|
|
527
|
+
|
|
528
|
+
1. Fork the repository
|
|
529
|
+
2. Create a feature branch
|
|
530
|
+
3. Add tests for new functionality
|
|
531
|
+
4. Ensure all tests pass: `pytest tests/`
|
|
532
|
+
5. Submit a pull request
|
|
533
|
+
|
|
534
|
+
## Development Setup
|
|
535
|
+
|
|
536
|
+
```bash
|
|
537
|
+
# Clone repository
|
|
538
|
+
git clone https://github.com/yourusername/testcontainers-aws.git
|
|
539
|
+
cd testcontainers-aws
|
|
540
|
+
|
|
541
|
+
# Create virtual environment
|
|
542
|
+
python -m venv venv
|
|
543
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
544
|
+
|
|
545
|
+
# Install dev dependencies
|
|
546
|
+
pip install -r requirements-dev.txt
|
|
547
|
+
|
|
548
|
+
# Run tests
|
|
549
|
+
pytest tests/ -v
|
|
550
|
+
|
|
551
|
+
# Format code
|
|
552
|
+
black testcontainers_aws tests
|
|
553
|
+
|
|
554
|
+
# Type checking
|
|
555
|
+
mypy testcontainers_aws
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## License
|
|
559
|
+
|
|
560
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
561
|
+
|
|
562
|
+
## Credits
|
|
563
|
+
|
|
564
|
+
Built on top of:
|
|
565
|
+
- [testcontainers-python](https://github.com/testcontainers/testcontainers-python)
|
|
566
|
+
- [LocalStack](https://github.com/localstack/localstack)
|
|
567
|
+
- [boto3](https://github.com/boto/boto3)
|
|
568
|
+
|
|
569
|
+
## Support
|
|
570
|
+
|
|
571
|
+
- GitHub Issues: [Report bugs or request features](https://github.com/yourusername/testcontainers-aws/issues)
|
|
572
|
+
- Documentation: [Full API documentation](https://github.com/yourusername/testcontainers-aws#readme)
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
Made with ❤️ for the AWS testing community
|