test(General): implement unit test for data parser, rabbitmq client and other python files
Unit test coverage / pytest (push) Failing after 1m7s
Unit test coverage / pytest (push) Failing after 1m7s
This commit bump the coverage rate to 77%
This commit is contained in:
@@ -36,6 +36,30 @@ from amqp.router.router_base import AMQ_REPLY_TO_EXCHANGE
|
||||
from amqp.router.utils import await_result
|
||||
|
||||
|
||||
async def _create_reply_exchange_and_queue(
|
||||
connection: AbstractRobustConnection,
|
||||
) -> tuple[AbstractRobustChannel, AbstractExchange, str]:
|
||||
"""
|
||||
Create objects for reply.
|
||||
Return (channel for reply queue, exchange for reply, reply queue name)
|
||||
"""
|
||||
|
||||
# Wrap calls to RabbitMQ API to make it Awaitable, so it can be run thread-safe with
|
||||
# correct event loop. This is needed because the RabbitMQ API is not thread-safe and needs
|
||||
# to be called in the context of event loop the first RabbitMQ connection was created with.
|
||||
_channel = await connection.channel()
|
||||
_exchange_name: str = AMQ_REPLY_TO_EXCHANGE
|
||||
_exchange = await _channel.get_exchange(name=_exchange_name, ensure=True)
|
||||
_queue = await _channel.declare_queue(exclusive=False) # Declare a unique, non-exclusive queue
|
||||
_queue_name = _queue.name
|
||||
await _queue.bind(
|
||||
exchange=_exchange_name,
|
||||
routing_key=_queue_name, # Use queue name as routing key
|
||||
)
|
||||
logging_debug(f"Publishing to exchange: {_exchange_name}, q: {_queue_name}")
|
||||
return _channel, _exchange, _queue_name
|
||||
|
||||
|
||||
async def _convert_file_response(
|
||||
obj: FileResponse,
|
||||
connection: AbstractRobustConnection,
|
||||
@@ -47,33 +71,12 @@ async def _convert_file_response(
|
||||
The file content itself is transmitted through the named stream (which is implemented as
|
||||
temporary dedicated queue.
|
||||
"""
|
||||
|
||||
async def _wrap_amq_api_calls(
|
||||
connection: AbstractRobustConnection,
|
||||
) -> tuple[AbstractRobustChannel, AbstractExchange, str]:
|
||||
# Wrap calls to RabbitMQ API to make it Awaitable, so it can be run thread-safe with
|
||||
# correct event loop. This is needed because the RabbitMQ API is not thread-safe and needs
|
||||
# to be called in the context of event loop the first RabbitMQ connection was created with.
|
||||
_channel = await connection.channel()
|
||||
_exchange_name: str = AMQ_REPLY_TO_EXCHANGE
|
||||
_exchange = await _channel.get_exchange(name=_exchange_name, ensure=True)
|
||||
_queue = await _channel.declare_queue(
|
||||
exclusive=False
|
||||
) # Declare a unique, non-exclusive queue
|
||||
_queue_name = _queue.name
|
||||
await _queue.bind(
|
||||
exchange=_exchange_name,
|
||||
routing_key=_queue_name, # Use queue name as routing key
|
||||
)
|
||||
logging_debug(f"Publishing to exchange: {_exchange_name}, q: {_queue_name}")
|
||||
return _channel, _exchange, _queue_name
|
||||
|
||||
# All calls to RabbitMQ API have to be called threadsafe, because those appear to be attached
|
||||
# to parent event loop, and need to execute at that event loop, not the current one
|
||||
# allow coroutine to execute. calling _future.result() will block the entire thread
|
||||
# and the coroutine will never execute
|
||||
_channel, _exchange, _routing_key = await await_result(
|
||||
asyncio.run_coroutine_threadsafe(_wrap_amq_api_calls(connection), loop)
|
||||
asyncio.run_coroutine_threadsafe(_create_reply_exchange_and_queue(connection), loop)
|
||||
)
|
||||
# publish_file also needs RabbitMQ API, ensure the loop is passed as argument
|
||||
(_stream_name, _file_size) = await file_handler.publish_file(
|
||||
|
||||
@@ -40,7 +40,7 @@ class MultipartFormDataParser:
|
||||
try:
|
||||
self.form_data = json.loads(message.body())
|
||||
self.is_json = True
|
||||
if self.form_data["files"] and self.form_data["form"]:
|
||||
if self.form_data["files"] is not None and self.form_data["form"] is not None:
|
||||
self.form_data = AMQDataParser.process_form_data(self.form_data)
|
||||
except Exception as jsonerror:
|
||||
logging_error(f"Parsing JSON: {jsonerror}")
|
||||
@@ -55,7 +55,7 @@ class MultipartFormDataParser:
|
||||
try:
|
||||
self.form_data = json.loads(message.body())
|
||||
self.is_json = True
|
||||
if self.form_data["files"] and self.form_data["form"]:
|
||||
if self.form_data["files"] is not None and self.form_data["form"] is not None:
|
||||
self.form_data = AMQDataParser.process_form_data(self.form_data)
|
||||
except Exception as jsonerror:
|
||||
logging_error(f"Parsing JSON: {jsonerror}")
|
||||
@@ -109,7 +109,6 @@ class AMQDataParser:
|
||||
for key, value in data["files"].items():
|
||||
if value: # Only override if files data is non-empty
|
||||
result[key] = value[0]
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
@@ -162,7 +161,7 @@ class AMQDataParser:
|
||||
}
|
||||
if len(_form_data) == 0 and len(message.body()) > 0:
|
||||
_form_data = json.loads(message.body())
|
||||
if _form_data["files"] and _form_data["form"]:
|
||||
if _form_data["files"] is not None and _form_data["form"] is not None:
|
||||
_form_data = AMQDataParser.process_form_data(_form_data)
|
||||
return _form_data
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user