Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring v2 #3

Merged
merged 3 commits into from
Jun 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions tests/step_defs/conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
"""
conftest module
"""
from moto import mock_aws
import pytest
from utilities.classes.sqs_client import SQSClient
from utilities.classes.log import CustomLogger
from utilities.settings import QUEUE_NAME, SQS_BUCKET, REPORT_DIR
from utilities import os_funcs as cmd
from moto import mock_aws

def pytest_addoption(parser: 'pytest.Parser') -> None:
"""
Expand All @@ -12,16 +15,16 @@ def pytest_addoption(parser: 'pytest.Parser') -> None:
Args:
parser (pytest.Parser): The pytest parser object.
"""
parser.addoption("--mock-aws",
action="store",
parser.addoption("--mock-aws",
action="store",
default="True",
choices=["True", "False"],
type=str,
help="Boolean to indicate if AWS should be mocked"
)

@pytest.fixture(scope='module')
def mock_aws_flag(request: 'pytest.FixtureRequest') -> bool:
@pytest.fixture(scope='module', name='mock_aws_flag')
def get_mock_aws_flag(request: 'pytest.FixtureRequest') -> bool:
"""
Fixture to retrieve the value of the --mock-aws option.

Expand All @@ -32,12 +35,12 @@ def mock_aws_flag(request: 'pytest.FixtureRequest') -> bool:
bool: Boolean indicating whether AWS should be mocked.
"""
mock_aws_value = request.config.getoption("--mock-aws")
mock_aws_flag = mock_aws_value == "True"
mock_aws_flag_value = mock_aws_value == "True"

return mock_aws_flag
return mock_aws_flag_value

@pytest.fixture(scope='module')
def log() -> CustomLogger:
@pytest.fixture(scope='module', name='log')
def get_log() -> CustomLogger:
"""
Fixture to create a CustomLogger instance.

Expand All @@ -57,7 +60,7 @@ def setup(request: 'pytest.FixtureRequest', mock_aws_flag: bool, log: CustomLogg
log (CustomLogger): A CustomLogger instance.
"""
log.info(f"AWS Mock Flag: {mock_aws_flag}")

if mock_aws_flag:
# Setup mocked AWS environment
log.info("Creating mocked SQS client")
Expand Down Expand Up @@ -94,8 +97,8 @@ def sqs_cli(mock_aws_flag: bool, log: CustomLogger) -> SQSClient:
Returns:
SQSClient: An SQSClient instance.
"""
sqs_cli = SQSClient(log=log, bucket=SQS_BUCKET, mock_aws_flag=mock_aws_flag)
sqs_cli.create_queue(QUEUE_NAME)
sqs_cli.get_queue_url(QUEUE_NAME)
sqs_client_instance = SQSClient(log=log, bucket=SQS_BUCKET, mock_aws_flag=mock_aws_flag)
sqs_client_instance.create_queue(QUEUE_NAME)
sqs_client_instance.get_queue_url(QUEUE_NAME)

return sqs_cli
return sqs_client_instance
35 changes: 22 additions & 13 deletions tests/step_defs/test_messages_steps.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from pytest_bdd import scenarios, given, when, then
"""
steps to scenarios of cars_stream_processing feature
"""
import json
from pytest_bdd import scenarios, given, when, then
from utilities.settings import PROJECT_DIR

# Load feature files for pytest-bdd
scenarios('../features/cars_stream_processing.feature')

# Given Step
@given('a list of cars are added to car queue', target_fixture='added_cars')
@given('a list of cars are added to car queue', target_fixture='cars_list')
def added_cars(sqs_cli: 'SQSClient', log: 'CustomLogger') -> list:
"""
Given step to add a list of cars to the car queue.
Expand All @@ -19,8 +22,10 @@ def added_cars(sqs_cli: 'SQSClient', log: 'CustomLogger') -> list:
list: List of cars added to the queue.
"""
log.info("######## Start Step: 'Given a list of cars are added to car queue' ########")
f = open(PROJECT_DIR + "\\test-data\\cars.json", "r")
cars = json.loads(f.read())
#f = open(PROJECT_DIR + "\\test-data\\cars.json", "r")
#cars = json.loads(f.read())
with open(PROJECT_DIR + "\\test-data\\cars.json", "r", encoding='utf-8') as f:
cars = json.loads(f.read())

for car in cars:
car_details = car["car detail"]
Expand All @@ -36,7 +41,7 @@ def added_cars(sqs_cli: 'SQSClient', log: 'CustomLogger') -> list:
return cars

# When Step
@when("the queue list is returned", target_fixture='queue_list')
@when("the queue list is returned", target_fixture='messages')
def queue_list(sqs_cli: 'SQSClient', log: 'CustomLogger') -> dict:
"""
When step to return the queue list.
Expand All @@ -58,38 +63,42 @@ def queue_list(sqs_cli: 'SQSClient', log: 'CustomLogger') -> dict:

# Then Step
@then("the list contains the cars added")
def assert_response_code(sqs_cli: 'SQSClient', added_cars: list, queue_list: dict, log: 'CustomLogger') -> None:
def assert_response_code(sqs_cli: 'SQSClient',
cars_list: list, messages: dict, log: 'CustomLogger') -> None:
"""
Then step to assert that the list contains the cars added.

Args:
sqs_cli (SQSClient): An instance of SQSClient.
added_cars (list): List of cars added to the queue.
queue_list (dict): Queue list.
cars_list (list): List of cars added to the queue.
messages (dict): Queue list.
log (CustomLogger): A CustomLogger instance.
"""
log.info("######## Start Step: 'Then the list contains the cars added' ########")
messages_count = 0
for message in queue_list["Messages"]:
for message in messages["Messages"]:
in_message_id = message["MessageId"]
body = message["Body"]
receipt_handle = message["ReceiptHandle"]

log.info(f"message body: {message}")

for car in added_cars:
for car in cars_list:

car_details = car["car detail"]
out_message_id = car["id"]

if in_message_id == out_message_id:

assert car_details == json.loads(body), f"body of the message {in_message_id} doesn't match car detail"
error_msg = f"body of the message {in_message_id} doesn't match car detail"
assert car_details == json.loads(body), error_msg

log.info(f"delete message with id {in_message_id}")
sqs_cli.delete_message(receipt_handle)
messages_count += 1

assert len(added_cars) == messages_count, f"Couldn't find all messages sent. Were sent {len(added_cars)} msgs, but found only {messages_count} msgs"
error_messages = f"""Couldn't find all messages sent.
Were sent {len(cars_list)} msgs,
but found only {messages_count} msgs"""
assert len(cars_list) == messages_count, error_messages

log.info("######## End Step: 'Then the list contains the cars added' ########")
20 changes: 19 additions & 1 deletion utilities/classes/log.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
"""
A custom logger class that logs messages to both console and file with timed rotation.
"""
import logging
import re
from logging import handlers, Logger


class CustomLogger(Logger):
"""
A custom logger class that logs messages to both console and file with timed rotation.

This class extends the standard Logger class
from the logging module and provides additional functionality
for logging messages to both console and file. Log files are rotated daily,
and old log files are automatically
deleted after a specified number of days.

Attributes:
log_folder (str): The directory where log files will be stored.
backupCount_days (int): The number of days to keep backup log files.

Methods:
__init__: Initializes the custom logger.
"""
def __init__(self, log_folder: str, backupCount_days: int = 5):
"""
Initialize the CustomLogger.
Expand Down Expand Up @@ -35,4 +53,4 @@ def __init__(self, log_folder: str, backupCount_days: int = 5):
file_handler.suffix = '%Y_%m_%d.log'
file_handler.extMatch = re.compile(r"^\d{4}_\d{2}_\d{2}.log$")
file_handler.setFormatter(formatter)
self.addHandler(file_handler)
self.addHandler(file_handler)
56 changes: 47 additions & 9 deletions utilities/classes/sqs_client.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
"""
SQSClient module

This module contains the SQSClient class which provides methods for interacting with Amazon SQS.

Classes:
SQSClient: A class for interacting with Amazon SQS.

"""
import json
import boto3
from botocore.exceptions import ClientError
import json
from utilities.settings import LOCALHOST, REGION_NAME

class SQSClient:
def __init__(self, log: 'CustomLogger', bucket: str, mock_aws_flag: bool, host: str = LOCALHOST, region_name: str = REGION_NAME):
"""
A class for interacting with Amazon SQS.

Attributes:
log (CustomLogger): Logger object.
bucket_name (str): Name of the AWS bucket.
host (str): Host URL.
mock_aws_flag (bool): Flag indicating whether to mock AWS or not.
client (boto3.client): SQS client object.
queue_url (str): URL of the SQS queue.

Methods:
__init__: Initializes the SQSClient.
create_client: Creates an SQS client based on mock_aws_flag.
create_queue: Creates an SQS queue.
get_queue_url: Gets the URL of an SQS queue.
send_message: Sends a message to the SQS queue.
receive_messages: Receives messages from the SQS queue.
delete_message: Deletes a message from the SQS queue.
"""

def __init__(self, log: 'CustomLogger', bucket: str,
mock_aws_flag: bool, host: str = LOCALHOST):
"""
Initialize SQSClient.

Expand All @@ -13,12 +44,11 @@ def __init__(self, log: 'CustomLogger', bucket: str, mock_aws_flag: bool, host:
bucket (str): Name of the AWS bucket.
mock_aws_flag (bool): Flag indicating whether to mock AWS or not.
host (str, optional): Host URL. Defaults to LOCALHOST.
region_name (str, optional): AWS region name. Defaults to REGION_NAME.
"""
self.log = log
self.bucket_name = bucket
self.host = host
self.region_name = region_name
self.region_name = REGION_NAME
self.mock_aws_flag = mock_aws_flag
self.client = self.create_client()
self.queue_url = ''
Expand All @@ -31,9 +61,14 @@ def create_client(self) -> boto3.client:
boto3.client: SQS client object.
"""
if self.mock_aws_flag:
return boto3.client(self.bucket_name, region_name=self.region_name)
else:
return boto3.client(self.bucket_name, endpoint_url=self.host, region_name=self.region_name)
return boto3.client(self.bucket_name,
region_name=self.region_name
)

return boto3.client(self.bucket_name,
endpoint_url=self.host,
region_name=self.region_name
)

def create_queue(self, queue_name: str) -> None:
"""
Expand Down Expand Up @@ -82,7 +117,8 @@ def send_message(self, car: dict) -> dict:
"""
try:
message = car
response = self.client.send_message(QueueUrl=self.queue_url, MessageBody=json.dumps(message))
response = self.client.send_message(QueueUrl=self.queue_url,
MessageBody=json.dumps(message))
self.log.info("Message sent")
return response
except ClientError as error:
Expand All @@ -97,7 +133,9 @@ def receive_messages(self) -> dict:
dict: Messages received from the SQS service.
"""
try:
messages = self.client.receive_message(QueueUrl=self.queue_url, MaxNumberOfMessages=10, WaitTimeSeconds=10)
messages = self.client.receive_message(QueueUrl=self.queue_url,
MaxNumberOfMessages=10,
WaitTimeSeconds=10)
self.log.info("Messages received")
return messages
except ClientError as error:
Expand Down
Loading