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