test(easyqueue): add unit test
parent
e735a36a54
commit
1253499c14
@ -0,0 +1,26 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from amqpworker.easyqueue.queue import BaseJsonQueue
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def base_json_queue(mocker):
|
||||||
|
return BaseJsonQueue(mocker.ANY, mocker.ANY, mocker.ANY)
|
||||||
|
|
||||||
|
|
||||||
|
def test_serialize(base_json_queue):
|
||||||
|
body = {"teste": "aãç"}
|
||||||
|
result = base_json_queue.serialize(body)
|
||||||
|
assert result == '{"teste": "a\\u00e3\\u00e7"}'
|
||||||
|
|
||||||
|
|
||||||
|
def test_serialize_with_ensure_ascii_false(base_json_queue):
|
||||||
|
body = {"teste": "aãç"}
|
||||||
|
result = base_json_queue.serialize(body, ensure_ascii=False)
|
||||||
|
assert '{"teste": "aãç"}' == result
|
||||||
|
|
||||||
|
|
||||||
|
def test_deserialize(base_json_queue):
|
||||||
|
body = '{"teste": "aãç"}'.encode("utf-8")
|
||||||
|
result = base_json_queue.deserialize(body)
|
||||||
|
assert {"teste": "aãç"} == result
|
@ -0,0 +1,57 @@
|
|||||||
|
import amqpstorm
|
||||||
|
import pytest
|
||||||
|
from amqpworker.easyqueue.connection import AMQPConnection
|
||||||
|
from tests.easyqueue.test_queue import SubscriptableMock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def connection(mocker):
|
||||||
|
mocked_connection = mocker.Mock(
|
||||||
|
return_value=mocker.Mock(channel=SubscriptableMock(return_value=mocker.Mock(basic=mocker.Mock(
|
||||||
|
publish=mocker.Mock(), qos=mocker.Mock(), consume=mocker.Mock(return_value='consumer_666')
|
||||||
|
)))))
|
||||||
|
mocker.patch.object(amqpstorm.Connection, '__new__', mocked_connection)
|
||||||
|
return mocked_connection, AMQPConnection(**dict(
|
||||||
|
host="money.que.é.good",
|
||||||
|
username="nós",
|
||||||
|
password="não",
|
||||||
|
virtual_host="have",
|
||||||
|
heartbeat=5,
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
from amqpstorm import Connection
|
||||||
|
|
||||||
|
|
||||||
|
def test_connection_lock_ensures_amqp_connect_is_only_called_once(
|
||||||
|
mocker, connection
|
||||||
|
):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
protocol = Mock(channel=Mock(is_open=True))
|
||||||
|
connect = mocker.patch.object(Connection, "__new__",
|
||||||
|
return_value=protocol
|
||||||
|
)
|
||||||
|
[connection[1]._connect() for _ in range(100)]
|
||||||
|
assert connect.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_connects_with_correct_args(mocker, connection):
|
||||||
|
connection[1]._connect()
|
||||||
|
conn_params = dict(
|
||||||
|
host="money.que.é.good",
|
||||||
|
username="nós",
|
||||||
|
password="não",
|
||||||
|
virtual_host="have",
|
||||||
|
heartbeat=5,
|
||||||
|
)
|
||||||
|
assert connection[0].call_args_list == [
|
||||||
|
mocker.call(
|
||||||
|
amqpstorm.Connection,
|
||||||
|
hostname=conn_params["host"],
|
||||||
|
port=5672,
|
||||||
|
username=conn_params["username"],
|
||||||
|
password=conn_params["password"],
|
||||||
|
virtual_host=conn_params["virtual_host"],
|
||||||
|
heartbeat=conn_params["heartbeat"],
|
||||||
|
)
|
||||||
|
]
|
@ -0,0 +1,169 @@
|
|||||||
|
import pytest
|
||||||
|
from amqpworker.easyqueue.exceptions import UndecodableMessageException
|
||||||
|
from amqpworker.easyqueue.message import AMQPMessage
|
||||||
|
|
||||||
|
|
||||||
|
def test_lazy_deserialization_raises_an_error_if_deserialization_fails(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
data = b"Xablau"
|
||||||
|
deserializer = Mock(side_effect=ValueError)
|
||||||
|
msg = AMQPMessage(
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=data,
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=deserializer,
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(UndecodableMessageException):
|
||||||
|
_ = msg.deserialized_data
|
||||||
|
|
||||||
|
deserializer.assert_called_once_with(data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_successful_deserialization(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
data = b'["Xablau"]'
|
||||||
|
deserializer = Mock(return_value=["Xablau"])
|
||||||
|
msg = AMQPMessage(
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=data,
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=deserializer,
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
assert msg.deserialized_data == ["Xablau"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_deserialization_is_only_called_once(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
data = b'["Xablau"]'
|
||||||
|
deserializer = Mock(return_value=["Xablau"])
|
||||||
|
|
||||||
|
msg = AMQPMessage(
|
||||||
|
queue=Mock(),
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=data,
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=deserializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = [msg.deserialized_data for _ in range(10)]
|
||||||
|
|
||||||
|
deserializer.assert_called_once_with(data)
|
||||||
|
|
||||||
|
|
||||||
|
def test_equal_messages(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
msg1 = AMQPMessage(
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=Mock(),
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=Mock(),
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
msg2 = AMQPMessage(
|
||||||
|
connection=msg1.connection,
|
||||||
|
channel=msg1.channel,
|
||||||
|
queue_name=msg1.queue_name,
|
||||||
|
serialized_data=msg1.serialized_data,
|
||||||
|
delivery_tag=msg1.delivery_tag,
|
||||||
|
properties=msg1._properties,
|
||||||
|
deserialization_method=msg1._deserialization_method,
|
||||||
|
queue=msg1._queue,
|
||||||
|
)
|
||||||
|
assert msg1 == msg2
|
||||||
|
|
||||||
|
|
||||||
|
def test_not_equal_messages(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
msg1 = AMQPMessage(
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=Mock(),
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=Mock(),
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
msg2 = AMQPMessage(
|
||||||
|
connection=msg1.connection,
|
||||||
|
channel=Mock(),
|
||||||
|
queue_name=msg1.queue_name,
|
||||||
|
serialized_data=msg1.serialized_data,
|
||||||
|
delivery_tag=msg1.delivery_tag,
|
||||||
|
properties=msg1._properties,
|
||||||
|
deserialization_method=msg1._deserialization_method,
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
assert msg1 != msg2
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_acks_messages(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
msg = AMQPMessage(
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(basic=Mock(ack=Mock())),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=Mock(),
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=Mock(),
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
msg.ack()
|
||||||
|
|
||||||
|
msg.channel.basic.ack.assert_called_once_with(msg.delivery_tag)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_rejects_messages_without_requeue(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
msg = AMQPMessage(
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(return_value=Mock(basic=Mock(reject=Mock()))),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=Mock(),
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=Mock(),
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
msg.reject()
|
||||||
|
|
||||||
|
msg.channel.basic.reject.assert_called_once_with(
|
||||||
|
delivery_tag=msg.delivery_tag, requeue=False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_rejects_messages_with_requeue(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
msg = AMQPMessage(
|
||||||
|
connection=Mock(),
|
||||||
|
channel=Mock(return_value=Mock(basic=Mock(reject=Mock()))),
|
||||||
|
queue_name=Mock(),
|
||||||
|
serialized_data=Mock(),
|
||||||
|
delivery_tag=Mock(),
|
||||||
|
properties=Mock(),
|
||||||
|
deserialization_method=Mock(),
|
||||||
|
queue=Mock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
msg.reject(requeue=True)
|
||||||
|
msg.channel.basic.reject.assert_called_once_with(
|
||||||
|
delivery_tag=msg.delivery_tag, requeue=True
|
||||||
|
)
|
@ -0,0 +1,493 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import amqpstorm
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import Mock as Mk
|
||||||
|
from amqpworker.easyqueue.message import AMQPMessage
|
||||||
|
from amqpworker.easyqueue.queue import (
|
||||||
|
ConnType,
|
||||||
|
JsonQueue,
|
||||||
|
QueueConsumerDelegate,
|
||||||
|
_ConsumptionHandler,
|
||||||
|
_ensure_conn_is_ready,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_raises_an_error_if_its_initialized_with_both_delegate_and_delegate_class(mocker):
|
||||||
|
with pytest.raises(ValueError) as e:
|
||||||
|
JsonQueue(
|
||||||
|
host="127.0.0.1",
|
||||||
|
username="guest",
|
||||||
|
password="guest",
|
||||||
|
delegate=mocker.Mock(),
|
||||||
|
delegate_class=mocker.Mock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_its_possible_to_initialize_without_a_delegate():
|
||||||
|
queue = JsonQueue(
|
||||||
|
host="127.0.0.1",
|
||||||
|
username="guest",
|
||||||
|
password="guest",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert isinstance(queue, JsonQueue)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_initializes_a_delegate_if_delegate_class_is_provided(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
delegate_class = Mock()
|
||||||
|
JsonQueue(Mock(), Mock(), Mock(), delegate_class=delegate_class)
|
||||||
|
delegate_class.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
|
class SettableMock(Mk):
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SubscriptableMock(Mk):
|
||||||
|
def __getitem__(self, item):
|
||||||
|
if item == "consumer_tag":
|
||||||
|
return 'consumer_666'
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# basic = Mk(
|
||||||
|
#
|
||||||
|
# publish=Mk(),
|
||||||
|
# qos=Mk(),
|
||||||
|
# consume=Mk(
|
||||||
|
# return_value='consumer_666'
|
||||||
|
# ),
|
||||||
|
#
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def connected_queue(mocker):
|
||||||
|
mocked_connection = mocker.Mock(
|
||||||
|
return_value=mocker.Mock(channel=SubscriptableMock(return_value=mocker.Mock(basic=mocker.Mock(
|
||||||
|
publish=mocker.Mock(), qos=mocker.Mock(), consume=mocker.Mock(return_value='consumer_666')
|
||||||
|
)))))
|
||||||
|
mocker.patch.object(amqpstorm.Connection, '__new__', mocked_connection)
|
||||||
|
return JsonQueue(host="127.0.0.1",
|
||||||
|
username="guest",
|
||||||
|
password="guest", delegate=mocker.Mock())
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def write_conn(connected_queue):
|
||||||
|
return connected_queue.conn_for(ConnType.WRITE)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_dont_call_consumer_handler_methods(mocker, connected_queue):
|
||||||
|
assert not connected_queue.delegate.on_queue_message.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_puts_messages_into_queue_as_json_if_message_is_a_json_serializeable(mocker, connected_queue, write_conn):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
message = {
|
||||||
|
"artist": "Great White",
|
||||||
|
"song": "Once Bitten Twice Shy",
|
||||||
|
"album": "Twice Shy",
|
||||||
|
}
|
||||||
|
|
||||||
|
exchange = Mock()
|
||||||
|
routing_key = Mock()
|
||||||
|
properties = SettableMock()
|
||||||
|
mandatory = Mock()
|
||||||
|
immediate = Mock()
|
||||||
|
connected_queue.put(
|
||||||
|
data=message,
|
||||||
|
exchange=exchange,
|
||||||
|
routing_key=routing_key,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate,
|
||||||
|
)
|
||||||
|
expected = mocker.call(body=json.dumps(message).encode(),
|
||||||
|
exchange=exchange,
|
||||||
|
routing_key=routing_key,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate, )
|
||||||
|
assert [expected] == write_conn.channel.basic.publish.call_args_list
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_raises_an_error_if_both_data_and_json_are_passed_to_put_message(
|
||||||
|
mocker, connected_queue, write_conn
|
||||||
|
):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
message = {
|
||||||
|
"artist": "Great White",
|
||||||
|
"song": "Once Bitten Twice Shy",
|
||||||
|
"album": "Twice Shy",
|
||||||
|
}
|
||||||
|
exchange = Mock()
|
||||||
|
routing_key = Mock()
|
||||||
|
properties = SettableMock()
|
||||||
|
mandatory = Mock()
|
||||||
|
immediate = Mock()
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
connected_queue.put(
|
||||||
|
serialized_data=json.dumps(message),
|
||||||
|
data=message,
|
||||||
|
exchange=exchange,
|
||||||
|
routing_key=routing_key,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate,
|
||||||
|
)
|
||||||
|
expected = mocker.call(
|
||||||
|
body=json.dumps(message).encode(),
|
||||||
|
routing_key=routing_key,
|
||||||
|
exchange_name=exchange,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate,
|
||||||
|
)
|
||||||
|
write_conn.channel.basic.publish.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_encodes_payload_into_bytes_if_payload_is_str(mocker, connected_queue, write_conn):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
payload = json.dumps({"dog": "Xablau"})
|
||||||
|
exchange = Mock()
|
||||||
|
routing_key = Mock()
|
||||||
|
properties = SettableMock()
|
||||||
|
mandatory = Mock()
|
||||||
|
immediate = Mock()
|
||||||
|
connected_queue.put(
|
||||||
|
serialized_data=payload,
|
||||||
|
exchange=exchange,
|
||||||
|
routing_key=routing_key,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate,
|
||||||
|
)
|
||||||
|
|
||||||
|
write_conn.channel.basic.publish.assert_called_once_with(
|
||||||
|
body=payload.encode(),
|
||||||
|
routing_key=routing_key,
|
||||||
|
exchange=exchange,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_doesnt_encodes_payload_into_bytes_if_payload_is_already_bytes(
|
||||||
|
mocker, connected_queue, write_conn
|
||||||
|
):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
payload = json.dumps({"dog": "Xablau"}).encode()
|
||||||
|
exchange = Mock()
|
||||||
|
routing_key = Mock()
|
||||||
|
properties = SettableMock()
|
||||||
|
mandatory = Mock()
|
||||||
|
immediate = Mock()
|
||||||
|
connected_queue.put(
|
||||||
|
serialized_data=payload,
|
||||||
|
exchange=exchange,
|
||||||
|
routing_key=routing_key,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate,
|
||||||
|
)
|
||||||
|
|
||||||
|
write_conn.channel.basic.publish.assert_called_once_with(
|
||||||
|
body=payload,
|
||||||
|
routing_key=routing_key,
|
||||||
|
exchange=exchange,
|
||||||
|
properties=properties,
|
||||||
|
mandatory=mandatory,
|
||||||
|
immediate=immediate,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_connect_gets_if_put_is_called_before_connect(mocker, connected_queue, write_conn):
|
||||||
|
message = {
|
||||||
|
"artist": "Great White",
|
||||||
|
"song": "Once Bitten Twice Shy",
|
||||||
|
"album": "Twice Shy",
|
||||||
|
}
|
||||||
|
Mock = mocker.Mock
|
||||||
|
connect = mocker.patch.object(write_conn, "_connect")
|
||||||
|
mocker.patch.object(
|
||||||
|
write_conn,
|
||||||
|
"channel",
|
||||||
|
Mock(is_open=False, return_value={'basic': Mock(
|
||||||
|
publish=Mock()
|
||||||
|
)}),
|
||||||
|
)
|
||||||
|
connected_queue.put(data=message, routing_key="Xablau")
|
||||||
|
connect.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_raises_and_error_if_put_message_isnt_json_serializeable(
|
||||||
|
mocker, connected_queue, write_conn
|
||||||
|
):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
message = Mock()
|
||||||
|
exchange = Mock()
|
||||||
|
routing_key = Mock()
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
connected_queue.put(message, exchange=exchange, routing_key=routing_key)
|
||||||
|
write_conn.channel.basic.publish.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def consume_conn(connected_queue):
|
||||||
|
return connected_queue.conn_for(ConnType.CONSUME)
|
||||||
|
|
||||||
|
|
||||||
|
class ConsumeException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_calls_on_before_start_consumption_before_queue_consume(
|
||||||
|
mocker, connected_queue, consume_conn
|
||||||
|
):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
connected_queue.connection._connect()
|
||||||
|
mocker.patch.object(connected_queue.connection.channel.basic, 'consume', side_effect=ConsumeException())
|
||||||
|
delegate = mocker.Mock(on_before_start_consumption=mocker.Mock())
|
||||||
|
queue_name = mocker.Mock()
|
||||||
|
with pytest.raises(ConsumeException):
|
||||||
|
connected_queue.consume(queue_name, Mock(), delegate)
|
||||||
|
delegate.on_before_start_consumption.assert_called_once_with(
|
||||||
|
queue_name=queue_name, queue=connected_queue
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_connect_gets_called_if_consume_is_called_before_connect(
|
||||||
|
mocker, connected_queue
|
||||||
|
):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
channel = Mock(
|
||||||
|
is_open=False,
|
||||||
|
return_value={
|
||||||
|
'basic': Mock(qoc=Mock(), consume=Mock())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
connect = mocker.patch.object(
|
||||||
|
connected_queue.connection, "_connect"
|
||||||
|
)
|
||||||
|
mocker.patch.object(connected_queue.connection, "channel", channel)
|
||||||
|
queue_name = Mock()
|
||||||
|
connected_queue.consume(
|
||||||
|
queue_name, Mock(), delegate=Mock(spec=QueueConsumerDelegate)
|
||||||
|
)
|
||||||
|
connect.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_calling_consume_starts_message_consumption(mocker, connected_queue):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
connected_queue.connection._connect()
|
||||||
|
connected_queue.consume(queue_name=Mock(), pool=Mock(), delegate=Mock(spec=QueueConsumerDelegate))
|
||||||
|
assert connected_queue.connection.channel.basic.consume.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_calling_consume_binds_handler_method(mocker, connected_queue):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
connected_queue.connection._connect()
|
||||||
|
channel = connected_queue.connection.channel
|
||||||
|
queue_name = Mock()
|
||||||
|
consumer_name = Mock()
|
||||||
|
expected_prefetch_count = 666
|
||||||
|
connected_queue.prefetch_count = expected_prefetch_count
|
||||||
|
Handler = mocker.patch(
|
||||||
|
"amqpworker.easyqueue.queue._ConsumptionHandler",
|
||||||
|
return_value=Mock(spec=_ConsumptionHandler),
|
||||||
|
)
|
||||||
|
delegate = Mock(spec=QueueConsumerDelegate)
|
||||||
|
pool = Mock()
|
||||||
|
connected_queue.consume(
|
||||||
|
queue_name=queue_name,
|
||||||
|
pool=pool,
|
||||||
|
consumer_name=consumer_name,
|
||||||
|
delegate=delegate,
|
||||||
|
)
|
||||||
|
expected = mocker.call(
|
||||||
|
callback=mocker.ANY, queue=queue_name, consumer_tag=consumer_name
|
||||||
|
)
|
||||||
|
assert connected_queue.connection.channel.basic.consume.call_args_list == [expected]
|
||||||
|
_, kwargs = channel.basic.consume.call_args_list[0]
|
||||||
|
callback = kwargs["callback"]
|
||||||
|
message = Mock()
|
||||||
|
callback(
|
||||||
|
message=message
|
||||||
|
)
|
||||||
|
Handler.assert_called_once_with(
|
||||||
|
delegate=delegate, queue=connected_queue, queue_name=queue_name
|
||||||
|
)
|
||||||
|
Handler.return_value.handle_message.assert_called_once_with(
|
||||||
|
message=message
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_calling_consume_sets_a_prefetch_qos(mocker, connected_queue):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
connected_queue.connection._connect()
|
||||||
|
expected_prefetch_count = 666
|
||||||
|
connected_queue.prefetch_count = expected_prefetch_count
|
||||||
|
connected_queue.consume(
|
||||||
|
queue_name=Mock(), pool=Mock(), delegate=Mock(spec=QueueConsumerDelegate)
|
||||||
|
)
|
||||||
|
expected = mocker.call(
|
||||||
|
global_=mocker.ANY,
|
||||||
|
prefetch_count=expected_prefetch_count,
|
||||||
|
prefetch_size=0,
|
||||||
|
)
|
||||||
|
assert connected_queue.connection.channel.basic.qos.call_args_list == [expected]
|
||||||
|
|
||||||
|
|
||||||
|
def test_calling_consume_starts_a_connection(mocker, connected_queue):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
mocked_connection = mocker.Mock(return_value=mocker.Mock())
|
||||||
|
_connect = mocker.patch.object(amqpstorm.Connection, '__new__', mocked_connection)
|
||||||
|
consumer = Mock(spec=QueueConsumerDelegate)
|
||||||
|
assert not _connect.called
|
||||||
|
connected_queue.consume(
|
||||||
|
queue_name="test_queue", pool=Mock(), delegate=consumer
|
||||||
|
)
|
||||||
|
assert _connect.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_calling_consume_notifies_delegate(mocker, connected_queue):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
expected_prefetch_count = 666
|
||||||
|
connected_queue.prefetch_count = expected_prefetch_count
|
||||||
|
delegate = Mock(spec=QueueConsumerDelegate)
|
||||||
|
connected_queue.consume(
|
||||||
|
queue_name="test_queue", pool=Mock(), delegate=delegate
|
||||||
|
)
|
||||||
|
|
||||||
|
delegate.on_before_start_consumption.assert_called_once_with(
|
||||||
|
queue_name="test_queue", queue=connected_queue
|
||||||
|
)
|
||||||
|
delegate.on_consumption_start.assert_called_once_with(
|
||||||
|
consumer_tag="consumer_666", queue=connected_queue
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def handler_method(mocker, connected_queue):
|
||||||
|
properties = SettableMock(name="Properties")
|
||||||
|
delegate = mocker.Mock(spec=QueueConsumerDelegate)
|
||||||
|
consumer_tag = connected_queue.consume(
|
||||||
|
queue_name="test_queue",
|
||||||
|
pool=mocker.Mock(),
|
||||||
|
delegate=delegate,
|
||||||
|
consumer_name='fixture',
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = _ConsumptionHandler(
|
||||||
|
delegate=delegate,
|
||||||
|
queue=connected_queue,
|
||||||
|
queue_name="test_queue",
|
||||||
|
)
|
||||||
|
return properties, delegate, handler, mocker.Mock(name="method", consumer_tag=consumer_tag, delivery_tag='1')
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_calls_on_queue_message_with_the_message_body_wrapped_as_a_AMQPMessage_instance(mocker, connected_queue,
|
||||||
|
handler_method):
|
||||||
|
content = {
|
||||||
|
"artist": "Caetano Veloso",
|
||||||
|
"song": "Não enche",
|
||||||
|
"album": "Livro",
|
||||||
|
}
|
||||||
|
body = json.dumps(content).encode("utf-8")
|
||||||
|
properties, delegate, handler, method = handler_method
|
||||||
|
_handle_callback = mocker.patch.object(handler, "_handle_callback", mocker.Mock())
|
||||||
|
message = mocker.Mock(channel=connected_queue.connection.channel, body=body, method=method, properties=properties,
|
||||||
|
delivery_tag='1')
|
||||||
|
handler.handle_message(
|
||||||
|
message=message
|
||||||
|
)
|
||||||
|
amqp_message = AMQPMessage(
|
||||||
|
connection=connected_queue.connection,
|
||||||
|
channel=connected_queue.connection.channel,
|
||||||
|
queue=connected_queue,
|
||||||
|
properties=properties,
|
||||||
|
delivery_tag=method.delivery_tag,
|
||||||
|
deserialization_method=connected_queue.deserialize,
|
||||||
|
queue_name="test_queue",
|
||||||
|
serialized_data=body,
|
||||||
|
)
|
||||||
|
_handle_callback.assert_called_once_with(
|
||||||
|
handler.delegate.on_queue_message,
|
||||||
|
msg=amqp_message,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_calls_on_message_handle_error_if_message_handler_raises_an_error(
|
||||||
|
mocker, connected_queue, handler_method
|
||||||
|
):
|
||||||
|
content = {
|
||||||
|
"artist": "Caetano Veloso",
|
||||||
|
"song": "Não enche",
|
||||||
|
"album": "Livro",
|
||||||
|
}
|
||||||
|
properties, delegate, handler, method = handler_method
|
||||||
|
error = handler.delegate.on_queue_message.side_effect = KeyError()
|
||||||
|
kwargs = dict(
|
||||||
|
callback=handler.delegate.on_queue_message,
|
||||||
|
channel=connected_queue.connection.channel,
|
||||||
|
body=json.dumps(content),
|
||||||
|
properties=properties,
|
||||||
|
)
|
||||||
|
handler._handle_callback(**kwargs)
|
||||||
|
del kwargs["callback"]
|
||||||
|
handler.delegate.on_message_handle_error.assert_called_once_with(
|
||||||
|
handler_error=error, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ensure_queue(mocker):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
return JsonQueue(
|
||||||
|
"127.0.0.1",
|
||||||
|
"guest",
|
||||||
|
"guest",
|
||||||
|
seconds_between_conn_retry=666,
|
||||||
|
logger=Mock(spec=logging.Logger),
|
||||||
|
connection_fail_callback=Mock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_waits_before_trying_to_reconnect_if_connect_fails(mocker, ensure_queue):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
coro = Mock()
|
||||||
|
sleep = mocker.patch("amqpworker.easyqueue.queue.time.sleep")
|
||||||
|
mocker.patch.object(ensure_queue.connection, '_connect', Mock(side_effect=[ConnectionError, True]))
|
||||||
|
wrapped = _ensure_conn_is_ready(ConnType.CONSUME)(coro)
|
||||||
|
wrapped(ensure_queue, 1, dog="Xablau")
|
||||||
|
sleep.assert_called_once_with(666)
|
||||||
|
ensure_queue.connection._connect.assert_has_calls([mocker.call(), mocker.call()])
|
||||||
|
coro.assert_called_once_with(ensure_queue, 1, dog="Xablau")
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_logs_connection_retries_if_a_logger_istance_is_available(mocker, ensure_queue):
|
||||||
|
Mock = mocker.Mock
|
||||||
|
coro = Mock()
|
||||||
|
mocker.patch("amqpworker.easyqueue.queue.time.sleep")
|
||||||
|
mocker.patch.object(ensure_queue.connection, '_connect', Mock(side_effect=[ConnectionError, True]))
|
||||||
|
wrapped = _ensure_conn_is_ready(ConnType.CONSUME)(coro)
|
||||||
|
wrapped(ensure_queue, 1, dog="Xablau")
|
||||||
|
ensure_queue.logger.error.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_calls_connection_fail_callback_if_connect_fails(mocker, ensure_queue):
|
||||||
|
error = ConnectionError()
|
||||||
|
Mock = mocker.Mock
|
||||||
|
coro = Mock()
|
||||||
|
mocker.patch("amqpworker.easyqueue.queue.time.sleep")
|
||||||
|
mocker.patch.object(ensure_queue.connection, '_connect', Mock(side_effect=[error, True]))
|
||||||
|
wrapped = _ensure_conn_is_ready(ConnType.CONSUME)(coro)
|
||||||
|
wrapped(ensure_queue, 1, dog="Xablau")
|
||||||
|
ensure_queue.connection_fail_callback.assert_called_once_with(
|
||||||
|
error, 1
|
||||||
|
)
|
Loading…
Reference in New Issue