def test_transaction_calls(mockpika): """Test that calls to create, commit, abort transactions are passed to Pika properly.""" pytest.xfail("Transactions not implemented in pika transport yet") transport = PikaTransport() transport.connect() mockconn = mockpika.BlockingConnection mockchannel = mockconn.return_value.channel.return_value mockproperties = mockpika.BasicProperties transport._transaction_begin() mockchannel.tx_select.assert_called_once() transport._send("destination", mock.sentinel.message, transaction=mock.sentinel.txid) args, kwargs = mockchannel.basic_publish.call_args assert not args assert kwargs == { "exchange": "", "routing_key": "destination", "body": mock.sentinel.message, "mandatory": True, "properties": mock.ANY, } assert mockproperties.call_args[1] == {"headers": {}, "delivery_mode": 2} transport._transaction_abort() mockchannel.tx_rollback.assert_called_once() transport._transaction_commit() mockchannel.tx_commit.assert_called_once()
def test_broadcasting_message_with_expiration(mockpika, mock_pikathread): """Test sending a message that expires some time in the future""" message_lifetime = 120 transport = PikaTransport() transport.connect() mockproperties = mockpika.BasicProperties transport._broadcast(str(mock.sentinel.exchange), mock.sentinel.message, expiration=message_lifetime) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert mockproperties.return_value.expiration == str(message_lifetime * 1000) assert kwargs == { "exchange": str(mock.sentinel.exchange), "routing_key": "", "body": mock.sentinel.message, "properties": mock.ANY, "mandatory": False, "transaction_id": None, }
def test_send_message(mockpika, mock_pikathread): """Test the message sending function""" transport = PikaTransport() transport.connect() mockproperties = mockpika.BasicProperties transport._send(mock.sentinel.queue, mock.sentinel.message) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert kwargs == { "exchange": "", "routing_key": str(mock.sentinel.queue), "body": mock.sentinel.message, "mandatory": True, "properties": mock.ANY, "transaction_id": None, } assert mockproperties.call_args[1].get("headers") == {} assert int(mockproperties.call_args[1].get("delivery_mode")) == 2 # Was a test for delayed sending, this is advanced in rabbitMQ and # to be implemented later with pytest.raises(AssertionError): transport._send( str(mock.sentinel.queue), mock.sentinel.message, headers={"hdr": mock.sentinel.header}, delay=123, )
def test_broadcast_status(mockpika, mock_pikathread): """Test the status broadcast function.""" transport = PikaTransport() transport.connect() mockproperties = mockpika.BasicProperties transport.broadcast_status({ "status": str(mock.sentinel.status), "host": "localhost", "workflows": True }) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert int(mockproperties.call_args[1].get("delivery_mode")) == 2 assert int(mockproperties.return_value.expiration) == 1000 * 15 assert kwargs == { "exchange": "transient.status", "routing_key": "", "body": mock.ANY, "properties": mock.ANY, "mandatory": False, "transaction_id": None, } statusdict = json.loads(kwargs.get("body")) assert statusdict["status"] == str(mock.sentinel.status)
def test_instantiate_link_and_connect_to_broker(mock_pikathread): """Test the Pika connection routine.""" transport = PikaTransport() assert not transport.is_connected() transport.connect() mock_pikathread.start.assert_called_once() mock_pikathread.start.side_effect = RuntimeError assert transport.is_connected() with pytest.raises(RuntimeError): transport.connect() mock_pikathread.join.assert_not_called() assert transport.is_connected() transport.disconnect() mock_pikathread.join.assert_called_once() mock_pikathread.connection_alive = False assert not transport.is_connected() transport.disconnect() assert mock_pikathread.join.call_count == 2 assert not transport.is_connected()
def test_messages_are_serialized_for_transport(mock_pikathread): banana = {"entry": [0, "banana"]} banana_str = '{"entry": [0, "banana"]}' transport = PikaTransport() transport.connect() transport.send(str(mock.sentinel.queue1), banana) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert kwargs == { "exchange": "", "routing_key": str(mock.sentinel.queue1), "body": banana_str, "properties": pika.BasicProperties(delivery_mode=2, headers={}), "mandatory": True, "transaction_id": None, } transport.broadcast(str(mock.sentinel.queue2), banana) args, kwargs = mock_pikathread.send.call_args assert not args assert kwargs == { "exchange": str(mock.sentinel.queue2), "routing_key": "", "body": banana_str, "properties": pika.BasicProperties(delivery_mode=2, headers={}), "mandatory": False, "transaction_id": None, } with pytest.raises(TypeError): transport.send(str(mock.sentinel.queue), mock.sentinel.unserializable)
def pikatransport(revert_classvariables, connection_params): # connection_params is unused here, but implements the fixture skipping # logic following a single test, instead of attempting a connection for # every individual test. pt = PikaTransport() pt.connect() yield pt pt.disconnect()
def test_error_handling_when_connecting_to_broker(mockpika, mock_pikathread): """Test the Pika connection routine.""" transport = PikaTransport() mock_pikathread.start.side_effect = pika.exceptions.AMQPConnectionError mockpika.exceptions = pika.exceptions with pytest.raises(workflows.Disconnected): transport.connect() assert transport.is_connected() is mock_pikathread.connection_alive
def test_subscribe_to_queue(mock_pikathread): """Test subscribing to a queue (producer-consumer), callback functions and unsubscribe""" transport = PikaTransport() transport.connect() mock_cb = mock.Mock() transport._subscribe(1, str(mock.sentinel.queue1), mock_cb) mock_pikathread.subscribe_queue.assert_called_once() args, kwargs = mock_pikathread.subscribe_queue.call_args assert not args assert kwargs == { "auto_ack": True, "callback": mock.ANY, "subscription_id": 1, "prefetch_count": 1, "queue": str(mock.sentinel.queue1), "reconnectable": False, } transport._subscribe(2, str(mock.sentinel.queue2), mock_cb) assert mock_pikathread.subscribe_queue.call_count == 2 args, kwargs = mock_pikathread.subscribe_queue.call_args assert not args assert kwargs == { "auto_ack": True, "callback": mock.ANY, "subscription_id": 2, "prefetch_count": 1, "queue": str(mock.sentinel.queue2), "reconnectable": False, } transport._subscribe(3, str(mock.sentinel.queue3), mock_cb, acknowledgement=True) assert mock_pikathread.subscribe_queue.call_count == 3 args, kwargs = mock_pikathread.subscribe_queue.call_args assert not args assert kwargs == { "auto_ack": False, "callback": mock.ANY, "subscription_id": 3, "prefetch_count": 1, "queue": str(mock.sentinel.queue3), "reconnectable": False, } transport._unsubscribe(1) mock_pikathread.unsubscribe.assert_called_once_with(1) transport._unsubscribe(2) mock_pikathread.unsubscribe.assert_called_with(2)
def test_ack_message(mock_pikathread): transport = PikaTransport() transport.connect() transport._ack(mock.sentinel.messageid, mock.sentinel.sub_id) mock_pikathread.ack.assert_called_once_with( mock.sentinel.messageid, mock.sentinel.sub_id, multiple=False, transaction_id=None, )
def test_nack_message(mock_pikathread): """Test that the _nack function call is properly forwarded to pika""" transport = PikaTransport() transport.connect() transport._nack(mock.sentinel.messageid, mock.sentinel.sub_id) mock_pikathread.nack.assert_called_once_with( mock.sentinel.messageid, mock.sentinel.sub_id, multiple=False, requeue=True, transaction_id=None, )
def test_messages_are_not_serialized_for_raw_transport(_mockpika, mock_pikathread): """Test the raw sending methods""" banana = '{"entry": [0, "banana"]}' transport = PikaTransport() transport.connect() transport.raw_send(str(mock.sentinel.queue1), banana) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert kwargs == { "exchange": "", "routing_key": str(mock.sentinel.queue1), "body": banana, "mandatory": True, "properties": mock.ANY, "transaction_id": None, } mock_pikathread.send.reset_mock() transport.raw_broadcast(str(mock.sentinel.queue2), banana) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert kwargs == { "exchange": str(mock.sentinel.queue2), "routing_key": "", "body": banana, "properties": mock.ANY, "mandatory": False, "transaction_id": None, } mock_pikathread.send.reset_mock() transport.raw_send(str(mock.sentinel.queue), mock.sentinel.unserializable) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert kwargs == { "exchange": "", "routing_key": str(mock.sentinel.queue), "body": mock.sentinel.unserializable, "mandatory": True, "properties": mock.ANY, "transaction_id": None, }
def test_send_broadcast(mockpika, mock_pikathread): """Test the broadcast sending function""" transport = PikaTransport() transport.connect() mockproperties = mockpika.BasicProperties transport._broadcast(str(mock.sentinel.exchange), mock.sentinel.message) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args properties = mockproperties.call_args[1] assert properties.get("headers") in (None, {}) assert kwargs == { "exchange": str(mock.sentinel.exchange), "routing_key": "", "body": mock.sentinel.message, "properties": mock.ANY, "mandatory": False, "transaction_id": None, } transport._broadcast( str(mock.sentinel.exchange), mock.sentinel.message, headers=mock.sentinel.headers, ) mock_pikathread.send.call_count == 2 args, kwargs = mock_pikathread.send.call_args assert not args properties = mockproperties.call_args[1] assert properties == {"headers": mock.sentinel.headers, "delivery_mode": 2} assert kwargs == { "exchange": str(mock.sentinel.exchange), "routing_key": "", "body": mock.sentinel.message, "properties": mock.ANY, "mandatory": False, "transaction_id": None, } # Delay not implemented yet with pytest.raises(AssertionError): transport._broadcast(str(mock.sentinel.exchange), mock.sentinel.message, delay=123)
def test_anonymous_connection(mockpika, mock_pikathread, revert_classvariables): """check that a specified configuration file is read, that command line parameters have precedence and are passed on the pika layer""" mockconn = mock.Mock() mockpika.BlockingConnection.return_value = mockconn parser = optparse.OptionParser() transport = PikaTransport() transport.add_command_line_options(parser) parser.parse_args(["--rabbit-user="******"--rabbit-pass="******"", "")
def test_check_config_file_behaviour(mockpika, mock_pikathread, tmp_path, revert_classvariables): """Check that a specified configuration file is read, that command line parameters have precedence and are passed on to the pika layer.""" parser = optparse.OptionParser() transport = PikaTransport() transport.add_command_line_options(parser) cfgfile = tmp_path / "config" cfgfile.write_text(""" # An example pika configuration file # Only lines in the [pika] block will be interpreted [rabbit] host = localhost port = 5672 username = someuser password = somesecret vhost = namespace """) parser.parse_args( ["--rabbit-conf", str(cfgfile), "--rabbit-user", mock.sentinel.user]) transport = PikaTransport() transport.connect() mock_pikathread.start.assert_called_once() mockpika.PlainCredentials.assert_called_once_with(mock.sentinel.user, "somesecret") args, kwargs = mockpika.ConnectionParameters.call_args assert not args assert kwargs == { "host": "localhost", "port": 5672, "virtual_host": "namespace", "credentials": mockpika.PlainCredentials.return_value, } with pytest.raises(workflows.Error): parser.parse_args(["--rabbit-conf", ""])
def test_error_handling_on_broadcast(mockpika): """Unrecoverable errors during broadcasting should lead to one reconnection attempt. Further errors should raise an Exception, further send attempts to try to reconnect.""" pytest.xfail("Don't understand send lifecycle errors yet") transport = PikaTransport() transport.connect() mockconn = mockpika.BlockingConnection mockchannel = mockconn.return_value.channel.return_value mockchannel.basic_publish.side_effect = pika.exceptions.AMQPChannelError() mockpika.exceptions = pika.exceptions assert mockconn.call_count == 1 with pytest.raises(workflows.Disconnected): transport._broadcast(str(mock.sentinel.channel), mock.sentinel.message) assert not transport.is_connected() assert mockconn.call_count == 2 mockchannel.basic_publish.side_effect = None transport._broadcast(str(mock.sentinel.channel), mock.sentinel.message) assert transport.is_connected() assert mockconn.call_count == 3
def test_error_handling_on_subscribing(mockpika, mock_pikathread): """Unrecoverable errors during subscribing should mark the connection as disconnected.""" mock_cb = mock.Mock() transport = PikaTransport() transport.connect() mockpika.exceptions = pika.exceptions mock_pikathread.connection_alive = False mock_pikathread.subscribe_queue.return_value.result.side_effect = ( pika.exceptions.AMQPChannelError) with pytest.raises(workflows.Disconnected): transport._subscribe(1, str(mock.sentinel.queue1), mock_cb) assert not transport.is_connected() # Now try connectionError instead of ChannelError mock_pikathread.subscribe_queue.return_value.result.side_effect = ( pika.exceptions.AMQPConnectionError) with pytest.raises(workflows.Disconnected): transport._subscribe(1, str(mock.sentinel.queue1), mock_cb) assert not transport.is_connected()
def test_subscribe_to_broadcast(mock_pikathread): """Test subscribing to a queue (producer-consumer), callback functions and unsubscribe""" mock_cb = mock.Mock() transport = PikaTransport() transport.connect() transport._subscribe_broadcast(1, str(mock.sentinel.queue1), mock_cb) mock_pikathread.subscribe_broadcast.assert_called_once() args, kwargs = mock_pikathread.subscribe_broadcast.call_args assert not args assert kwargs == { "callback": mock.ANY, "exchange": str(mock.sentinel.queue1), "subscription_id": 1, "reconnectable": False, } transport._subscribe_broadcast( 2, str(mock.sentinel.queue2), mock_cb, ) assert mock_pikathread.subscribe_broadcast.call_count == 2 args, kwargs = mock_pikathread.subscribe_broadcast.call_args assert not args assert kwargs == { "callback": mock.ANY, "exchange": str(mock.sentinel.queue2), "subscription_id": 2, "reconnectable": False, } transport._unsubscribe(1) mock_pikathread.unsubscribe.assert_called_once_with(1) transport._unsubscribe(2) mock_pikathread.unsubscribe.assert_called_with(2)
def test_sending_message_with_expiration(mockpika, mock_pikathread): """Test sending a message that expires some time in the future.""" transport = PikaTransport() transport.connect() mockproperties = mockpika.BasicProperties transport._send(str(mock.sentinel.queue), mock.sentinel.message, expiration=120) mock_pikathread.send.assert_called_once() args, kwargs = mock_pikathread.send.call_args assert not args assert kwargs == { "exchange": "", "routing_key": str(mock.sentinel.queue), "body": mock.sentinel.message, "mandatory": True, "properties": mock.ANY, "transaction_id": None, } assert int(mockproperties.return_value.expiration) == 120 * 1000
def test_messages_are_deserialized_after_transport(mock_pikathread): """Test the message serialization.""" banana = {"entry": [0, "banana"]} banana_str = '{"entry": [0, "banana"]}' transport = PikaTransport() transport.connect() # Test subscriptions callback = mock.Mock() transport.subscribe("queue", callback) mock_properties = mock.Mock() mock_properties.headers = {"mock_header": 1} # Extract the function passed to pikathread, and call it args, kwargs = mock_pikathread.subscribe_queue.call_args message_handler = kwargs["callback"] message_handler( mock.Mock(), mock.Mock(), mock_properties, banana_str, ) callback.assert_called_once() args, kwargs = callback.call_args assert not kwargs assert args[1] == banana message_handler(mock.Mock(), mock.Mock(), mock_properties, mock.sentinel.undeserializable) args, kwargs = callback.call_args assert not kwargs assert args[1] == mock.sentinel.undeserializable # Test broadcast subscriptions callback = mock.Mock() transport.subscribe_broadcast("queue", callback) message_handler = mock_pikathread.subscribe_broadcast.call_args[1][ "callback"] message_handler(mock.Mock(), mock.Mock(), mock_properties, banana_str) callback.assert_called_once() args, kwargs = callback.call_args assert not kwargs assert args[1] == banana message_handler(mock.Mock(), mock.Mock(), mock_properties, mock.sentinel.undeserializable) args, kwargs = callback.call_args assert not kwargs assert args[1] == mock.sentinel.undeserializable # Test subscriptions with mangling disabled callback = mock.Mock() transport.subscribe("queue", callback, disable_mangling=True) message_handler = mock_pikathread.subscribe_queue.call_args[1]["callback"] message_handler(mock.Mock(), mock.Mock(), mock_properties, banana_str) callback.assert_called_once() args, kwargs = callback.call_args assert not kwargs assert args[1] == banana_str # Test broadcast subscriptions with mangling disabled callback = mock.Mock() transport.subscribe_broadcast("queue", callback, disable_mangling=True) message_handler = mock_pikathread.subscribe_broadcast.call_args[1][ "callback"] message_handler(mock.Mock(), mock.Mock(), mock_properties, banana_str) callback.assert_called_once() args, kwargs = callback.call_args assert not kwargs assert args[1] == banana_str