diff --git a/python-test-samples/apigw-lambda-dynamodb/README.md b/python-test-samples/apigw-lambda-dynamodb/README.md index fe74edbf..0a70a2f7 100644 --- a/python-test-samples/apigw-lambda-dynamodb/README.md +++ b/python-test-samples/apigw-lambda-dynamodb/README.md @@ -1,4 +1,4 @@ -[![python: 3.9](https://img.shields.io/badge/Python-3.9-green)](https://img.shields.io/badge/Python-3.9-green) +[![python: 3.13](https://img.shields.io/badge/Python-3.13-green)](https://img.shields.io/badge/Python-3.13-green) [![AWS: DynamoDB](https://img.shields.io/badge/AWS-DynamoDB-blueviolet)](https://img.shields.io/badge/AWS-DynamoDB-blueviolet) [![test: unit](https://img.shields.io/badge/Test-Unit-blue)](https://img.shields.io/badge/Test-Unit-blue) [![test: integration](https://img.shields.io/badge/Test-Integration-yellow)](https://img.shields.io/badge/Test-Integration-yellow) @@ -86,11 +86,15 @@ The [unit test tear-down](tests/unit/mock_test.py#L61-66) removes the mocked Dyn To run the unit test, execute the following ```shell # Run from the project directory serverless-test-samples/python-test-samples/apigw-lambda-dynamodb +# Verify Python version (Should show Python 3.13.x) + +python3 --version +pip3 --version + # Create and Activate a Python Virtual Environment # One-time setup - pip3 install virtualenv -python3 -m venv venv +python3 -m virtualenv venv source ./venv/bin/activate # install dependencies diff --git a/python-test-samples/apigw-lambda-dynamodb/template.yaml b/python-test-samples/apigw-lambda-dynamodb/template.yaml index 8ab2b1bd..44b8be0f 100644 --- a/python-test-samples/apigw-lambda-dynamodb/template.yaml +++ b/python-test-samples/apigw-lambda-dynamodb/template.yaml @@ -56,7 +56,7 @@ Resources: TableName: !Ref DynamoDBTable CodeUri: src/ Handler: app.lambda_handler - Runtime: python3.9 + Runtime: python3.13 Architectures: - x86_64 Environment: diff --git a/python-test-samples/apigw-lambda-dynamodb/tests/integration/test_api_gateway.py b/python-test-samples/apigw-lambda-dynamodb/tests/integration/test_api_gateway.py index bf732deb..011f46ac 100644 --- a/python-test-samples/apigw-lambda-dynamodb/tests/integration/test_api_gateway.py +++ b/python-test-samples/apigw-lambda-dynamodb/tests/integration/test_api_gateway.py @@ -39,7 +39,7 @@ def setUp(self) -> None: """ stack_name = TestApiGateway.get_stack_name() - client = boto3.client("cloudformation") + client = boto3.client("cloudformation", region_name=self.aws_region) try: response = client.describe_stacks(StackName=stack_name) @@ -63,41 +63,64 @@ def setUp(self) -> None: # Using unique id's per unit test will isolate test data self.id_postfix = "_" + str(uuid4()) - # Seed the DynamoDB Table with Test Data - dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) - dynamodb_table = dynamodb_resource.Table(name=self.dynamodb_table_name) - dynamodb_table.put_item(Item={"PK": "TEST001" + self.id_postfix, - "SK": "NAME#", - "data": "Unit Test Name Data"}) - + try: + dynamodb_resource = boto3.resource("dynamodb", region_name=self.aws_region) + dynamodb_table = dynamodb_resource.Table(name=self.dynamodb_table_name) + dynamodb_table.put_item(Item={"PK": "TEST001" + self.id_postfix, + "SK": "NAME#", + "data": "Unit Test Name Data"}) + print(f"Successfully seeded test data for TEST001{self.id_postfix}") + except Exception as e: + print(f"Warning: Could not seed test data: {e}") + raise def tearDown(self) -> None: """ - # For tear-down, remove any data injected for the tests - # Take particular care to ensure these values are unique and identifiable as TEST data. + For tear-down, remove any data injected for the tests + Take particular care to ensure these values are unique and identifiable as TEST data. """ - dynamodb_resource = boto3.resource("dynamodb", region_name = self.aws_region) - dynamodb_table = dynamodb_resource.Table(name=self.dynamodb_table_name) - - for id in ["TEST001" + self.id_postfix,"TEST002" + self.id_postfix]: - id_items = dynamodb_table.query( - KeyConditionExpression=Key('PK').eq(id) - ) - if "Items" in id_items: - for item in id_items["Items"]: - dynamodb_table.delete_item(Key={"PK":item["PK"],"SK":item["SK"]}) + try: + dynamodb_resource = boto3.resource("dynamodb", region_name=self.aws_region) + dynamodb_table = dynamodb_resource.Table(name=self.dynamodb_table_name) + + for id in ["TEST001" + self.id_postfix,"TEST002" + self.id_postfix]: + try: + id_items = dynamodb_table.query( + KeyConditionExpression=Key('PK').eq(id) + ) + if "Items" in id_items: + for item in id_items["Items"]: + dynamodb_table.delete_item(Key={"PK":item["PK"],"SK":item["SK"]}) + print(f"Successfully cleaned up test data for {id}") + except Exception as item_error: + print(f"Could not clean up items for {id}: {item_error}") + except Exception as e: + print(f"Warning: tearDown cleanup failed (this may be a credentials issue): {e}") + print("Test data may remain in DynamoDB - clean up manually if needed") def test_api_gateway_200(self): """ Call the API Gateway endpoint and check the response for a 200 """ - response = requests.get(self.api_endpoint.replace("{id}","TEST001" + self.id_postfix)) - self.assertEqual(response.status_code, requests.codes.ok) + try: + response = requests.get(self.api_endpoint.replace("{id}","TEST001" + self.id_postfix)) + print(f"API Response Status: {response.status_code}") + print(f"API Response Body: {response.text}") + self.assertEqual(response.status_code, requests.codes.ok) + except Exception as e: + print(f"Test failed with error: {e}") + raise def test_api_gateway_404(self): """ Call the API Gateway endpoint and check the response for a 404 (id not found) """ - response = requests.get(self.api_endpoint.replace("{id}","TEST002" + self.id_postfix)) - self.assertEqual(response.status_code, requests.codes.not_found) + try: + response = requests.get(self.api_endpoint.replace("{id}","TEST002" + self.id_postfix)) + print(f"API Response Status: {response.status_code}") + print(f"API Response Body: {response.text}") + self.assertEqual(response.status_code, requests.codes.not_found) + except Exception as e: + print(f"Test failed with error: {e}") + raise \ No newline at end of file diff --git a/python-test-samples/apigw-lambda-dynamodb/tests/requirements.txt b/python-test-samples/apigw-lambda-dynamodb/tests/requirements.txt index 8ca1c035..41910f8c 100644 --- a/python-test-samples/apigw-lambda-dynamodb/tests/requirements.txt +++ b/python-test-samples/apigw-lambda-dynamodb/tests/requirements.txt @@ -1,9 +1,10 @@ -pytest +pytest>=7.0.0 pytest-mock -boto3 -moto -aws_lambda_powertools +moto>=4.0.0 +boto3>=1.26.0 +aws-lambda-powertools>=2.0.0 aws-xray-sdk fastjsonschema -pyyaml +requests>=2.28.0 +pyyaml>=6.0 uuid \ No newline at end of file diff --git a/python-test-samples/apigw-lambda-dynamodb/tests/unit/mock_test.py b/python-test-samples/apigw-lambda-dynamodb/tests/unit/mock_test.py index 6a0ef2b8..aa7714e0 100644 --- a/python-test-samples/apigw-lambda-dynamodb/tests/unit/mock_test.py +++ b/python-test-samples/apigw-lambda-dynamodb/tests/unit/mock_test.py @@ -10,15 +10,14 @@ import yaml import boto3 from boto3.dynamodb.conditions import Key -import moto +from moto import mock_aws # Changed from import moto # Import the handler under test from src import app # Mock the DynamoDB Service during the test -@moto.mock_dynamodb - +@mock_aws # Changed from @moto.mock_dynamodb class TestSampleLambdaWithDynamoDB(TestCase): """ Unit Test class for src/app.py @@ -136,7 +135,4 @@ def test_lambda_handler_notfound_path(self): # Check the log entry item for item in id_items["Items"]: self.assertEqual(item["data"], "NOTFOUND: Name Not Found for ID TEST002" + self.id_postfix) - self.assertEqual(item["SK"][0:11], "DT#" + datetime.now().strftime("%Y%m%d")) - - - \ No newline at end of file + self.assertEqual(item["SK"][0:11], "DT#" + datetime.now().strftime("%Y%m%d")) \ No newline at end of file