Skip to content

Commit

Permalink
Merge pull request #2 from san99tiago/develop
Browse files Browse the repository at this point in the history
Include UnitTests for the Lambda Function
  • Loading branch information
san99tiago authored Apr 27, 2023
2 parents e6448d2 + 8f0a072 commit a98d454
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 5 deletions.
14 changes: 11 additions & 3 deletions important_commands.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ pip install aws-cdk-lib

# Configure AWS credentials (follow steps)
aws configure
# --> Alternative 1: Environment variables added to terminal session
# --> Alternative 2: AWS Cloud9 with the right permissions

# Bootstrap CDK (provision initial resources to work with CDK.. S3, roles, etc)
#! Change "ACCOUNT-NUMBER" and "REGION" to your needed values
Expand All @@ -56,13 +58,19 @@ pip install -r requirements.txt || pip3 install -r requirements.txt
# PART 3: Main CDK and Python commands (most used)
################################################################################

cdk bootstrap
source .venv/bin/activate || echo "Make sure that virtual env exists"
# Activate Python virtual environment and check dependencies installed
# --> On the root of the repository (same context as this file), run:
source ./cdk/.venv/bin/activate || echo "Make sure that virtual env exists"
pip install -r ./cdk/requirements.txt
pip install -r ./lambda/requirements.txt
pip install -r ./lambda/requirements-dev.txt

# Test Lambda Python Stack
# Test Lambda Python Stack (optional)
# --> On the root of the repository (same context as this file), run:
python -m pytest

# CDK commands
# --> On the "./cdk" folder, run:
cdk bootstrap
cdk synthesize
cdk diff
Expand Down
Empty file added lambda/__init__.py
Empty file.
2 changes: 2 additions & 0 deletions lambda/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pytest==6.2.5
moto==4.1.7
Empty file added lambda/src/__init__.py
Empty file.
5 changes: 3 additions & 2 deletions lambda/src/lambda_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def process_messages(event: SQSEvent):
# Note: if input does not contain "Message" key, this will raise an error
message = json.loads(payload)["Message"]
logger.info("Message: {}".format(message))

return True

@logger.inject_lambda_context(log_event=True)
@event_source(data_class=SQSEvent)
Expand All @@ -47,7 +47,8 @@ def handler(event: SQSEvent, context: LambdaContext) -> str:
logger.debug("Starting messages processing")
tracer.put_metadata(key="details", value="messages processing handler")
try:
process_messages(event)
result = process_messages(event)
logger.debug(result)
except Exception as e:
logger.exception("Error processing the messages")
raise RuntimeError("Processing failed for the input messages") from e
Expand Down
Empty file added lambda/tests/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions lambda/tests/test_event_01_good_single.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"Records": [
{
"messageId": "4332b17c-b7cd-4aef-8dba-7cb7240e3d0e",
"receiptHandle": "AWEBZGy/Ajve1xtBoqIJhPmO0ANH9FWhcpjt7xevdsSw5+BK0OIljV8Jq/6SNgQ9boZKcTdGkK2aaiSo9iCAgsnSyUJ3u3FenB5rOnlOTf3ctMHHEJ4tGrwSR6lx8be8/CggdbtuCxjzZsPU2IUEIo4URuA4Kum54DELIWYaBNTBbfKlvlyKeRilpTh3a3I3rmy2ctn5HkKjGVmIv1WyIPNga3POZ28pxAEcQsVEFuU0fU/Bf9H5sDuj0i47JxzCBuimyFQEN1cMYwE5W7jz54hCEN3wC1blt9/M0U+jWqsC3psmlPb+qkj50ZriJb8bNoIHMutp+ERFy5CGrBJIiVIt8xvsJmkz9LuUhKDOTmwSN9yg9p+jrTD5L6B8yH8/WSu+4XMgK0QDv/BE/5eeLZcv6Q==",
"body": "{\n \"Message\": \"Test Message OK: good example message\",\n \"Details\": \"Test details\" \n}",
"attributes": {
"ApproximateReceiveCount": "1",
"AWSTraceHeader": "Root=1-64495159-17ceb00068aada396a66bd23",
"SentTimestamp": "1682526553465",
"SenderId": "AROASECKEBOYZPRE774YO:BackplaneAssumeRoleSession",
"ApproximateFirstReceiveTimestamp": "1682526553466"
},
"messageAttributes": {},
"md5OfBody": "d46c3294a7ebf36b9a9a99f46b768a9d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:12345678901:apigw-sqs-lambda-queue",
"awsRegion": "us-east-1"
}
]
}
38 changes: 38 additions & 0 deletions lambda/tests/test_event_02_good_multiple.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"Records": [
{
"messageId": "4332b17c-b7cd-4aef-8dba-7cb7240e3d0e",
"receiptHandle": "AWEBZGy/Ajve1xtBoqIJhPmO0ANH9FWhcpjt7xevdsSw5+BK0OIljV8Jq/6SNgQ9boZKcTdGkK2aaiSo9iCAgsnSyUJ3u3FenB5rOnlOTf3ctMHHEJ4tGrwSR6lx8be8/CggdbtuCxjzZsPU2IUEIo4URuA4Kum54DELIWYaBNTBbfKlvlyKeRilpTh3a3I3rmy2ctn5HkKjGVmIv1WyIPNga3POZ28pxAEcQsVEFuU0fU/Bf9H5sDuj0i47JxzCBuimyFQEN1cMYwE5W7jz54hCEN3wC1blt9/M0U+jWqsC3psmlPb+qkj50ZriJb8bNoIHMutp+ERFy5CGrBJIiVIt8xvsJmkz9LuUhKDOTmwSN9yg9p+jrTD5L6B8yH8/WSu+4XMgK0QDv/BE/5eeLZcv6Q==",
"body": "{\n \"Message\": \"Test Message OK: good example message 1\",\n \"Details\": \"Test details\" \n}",
"attributes": {
"ApproximateReceiveCount": "1",
"AWSTraceHeader": "Root=1-64495159-17ceb00068aada396a66bd23",
"SentTimestamp": "1682526553465",
"SenderId": "AROASECKEBOYZPRE774YO:BackplaneAssumeRoleSession",
"ApproximateFirstReceiveTimestamp": "1682526553466"
},
"messageAttributes": {},
"md5OfBody": "d46c3294a7ebf36b9a9a99f46b768a9d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:12345678901:apigw-sqs-lambda-queue",
"awsRegion": "us-east-1"
},
{
"messageId": "4332b17c-b7cd-4aef-8dba-7cb7240e3d0e",
"receiptHandle": "AWEBZGy/Ajve1xtBoqIJhPmO0ANH9FWhcpjt7xevdsSw5+BK0OIljV8Jq/6SNgQ9boZKcTdGkK2aaiSo9iCAgsnSyUJ3u3FenB5rOnlOTf3ctMHHEJ4tGrwSR6lx8be8/CggdbtuCxjzZsPU2IUEIo4URuA4Kum54DELIWYaBNTBbfKlvlyKeRilpTh3a3I3rmy2ctn5HkKjGVmIv1WyIPNga3POZ28pxAEcQsVEFuU0fU/Bf9H5sDuj0i47JxzCBuimyFQEN1cMYwE5W7jz54hCEN3wC1blt9/M0U+jWqsC3psmlPb+qkj50ZriJb8bNoIHMutp+ERFy5CGrBJIiVIt8xvsJmkz9LuUhKDOTmwSN9yg9p+jrTD5L6B8yH8/WSu+4XMgK0QDv/BE/5eeLZcv6Q==",
"body": "{\n \"Message\": \"Test Message OK: good example message 2\",\n \"Details\": \"Test details\" \n}",
"attributes": {
"ApproximateReceiveCount": "1",
"AWSTraceHeader": "Root=1-64495159-17ceb00068aada396a66bd23",
"SentTimestamp": "1682526553465",
"SenderId": "AROASECKEBOYZPRE774YO:BackplaneAssumeRoleSession",
"ApproximateFirstReceiveTimestamp": "1682526553466"
},
"messageAttributes": {},
"md5OfBody": "d46c3294a7ebf36b9a9a99f46b768a9d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:12345678901:apigw-sqs-lambda-queue",
"awsRegion": "us-east-1"
}
]
}
21 changes: 21 additions & 0 deletions lambda/tests/test_event_03_bad.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"Records": [
{
"messageId": "4332b17c-b7cd-4aef-8dba-7cb7240e3d0e",
"receiptHandle": "AWCBZGy/Ajve1xtBoqIJhPmO0ANH9FWhcpjt7xevdsSw5+BK0OIljV8Jq/6SNgQ9boZKcTdGkK2aaiSo9iCAgsnSyUJ3u3FenB5rOnlOTf3ctMHHEJ4tGrwSR6lx8be8/CggdbtuCxjzZsPU2IUEIo4URuA4Kum54DELIWYaBNTBbfKlvlyKeRilpTh3a3I3rmy2ctn5HkKjGVmIv1WyIPNga3POZ28pxAEcQsVEFuU0fU/Bf9H5sDuj0i47JxzCBuimyFQEN1cMYwE5W7jz54hCEN3wC1blt9/M0U+jWqsC3psmlPb+qkj50ZriJb8bNoIHMutp+ERFy5CGrBJIiVIt8xvsJmkz9LuUhKDOTmwSN9yg9p+jrTD5L6B8yH8/WSu+4XMgK0QDv/BE/5eeLZcv6Q==",
"body": "{\n \"MessageIntentionalWrongKey\": \"Test Message NOT OK: good example message with wrong keys\"\n}",
"attributes": {
"ApproximateReceiveCount": "1",
"AWSTraceHeader": "Root=1-64495159-17ceb00068aada396a66bd23",
"SentTimestamp": "1682526553465",
"SenderId": "AROASECKEBOYZPRE774YO:BackplaneAssumeRoleSession",
"ApproximateFirstReceiveTimestamp": "1682526553466"
},
"messageAttributes": {},
"md5OfBody": "d46c3294a7ebf36b9a9a99f46b768a9d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:12345678901:apigw-sqs-lambda-queue",
"awsRegion": "us-east-1"
}
]
}
80 changes: 80 additions & 0 deletions lambda/tests/test_lambda_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Built-in imports
import os, sys
import json
import unittest

# External imports
from aws_lambda_powertools.utilities.data_classes import event_source, SQSEvent
from moto import mock_sts

# Add path to find lambda directory for own imports
sys.path.append(os.path.abspath(os.path.dirname(os.path.dirname(__file__))))

# Own imports
import src.lambda_function as _lambda # noqa: E402


class TestLambdaFunction(unittest.TestCase):
"""
TestCase for unit testing the inner Lambda Function functionalities.
"""

def load_test_event(self, test_event_name: str) -> dict:
"""
Load test event from given file.
"""
path_to_event = os.path.join(
os.path.dirname(__file__),
test_event_name,
)
with open(path_to_event) as file:
test_event = json.load(file)
return test_event

# The "mock_sts" decorator allows to "mock/fake" the sts API calls for tests
@mock_sts()
def test_process_messages_success_single(self):
"""
Test successful process_messages call for a single message.
"""
# Load pre-configured event for current test case
event = self.load_test_event("test_event_01_good_single.json")

# Middleware to load event with correct SQSEvent data class
event_sqs = SQSEvent(event)
result = _lambda.process_messages(event_sqs)

self.assertEqual(result, True)

@mock_sts()
def test_process_messages_success_multiple(self):
"""
Test successful process_messages call for multiple messages.
"""
# Load pre-configured event for current test case
event = self.load_test_event("test_event_02_good_multiple.json")

# Middleware to load event with correct SQSEvent data class
event_sqs = SQSEvent(event)
result = _lambda.process_messages(event_sqs)

self.assertEqual(result, True)

@mock_sts()
def test_process_messages_error(self):
"""
Test errors on process_messages call due to wrong message format.
"""
# Load pre-configured event for current test case
event = self.load_test_event("test_event_03_bad.json")

# Middleware to load event with correct SQSEvent data class
event_sqs = SQSEvent(event)

# Expected an exception intentionally, otherwise fails
with self.assertRaises(Exception):
_lambda.process_messages(event_sqs)


if __name__ == "__main__":
unittest.main()

0 comments on commit a98d454

Please sign in to comment.