def test_consume_from_queue(self): """Verify RMQConsumer can consume from a queue.""" msg_received = None event = threading.Event() def on_msg(msg): event.set() nonlocal msg_received msg_received = msg # Consume consume_key = self.consumer.consume(ConsumeParams(on_msg), queue_params=QueueParams("queue")) self.assertEqual(consume_key, "queue") # Wait for ConsumeOK event.wait(timeout=1.0) self.assertTrue(isinstance(msg_received, ConsumeOK)) # Send a message and verify it is delivered OK event.clear() # clear to re-use event self.producer.publish(b"body", queue_params=QueueParams("queue")) event.wait(timeout=1.0) self.assertEqual(msg_received, b"body")
def test_consume_same_consume_key(self): """ Verify that calling consume once more with the same queue name, exchange name and routing key will result in an exception. """ self.consumer.consume( ConsumeParams(lambda _: ...), QueueParams("queue") ) # Assertions with self.assertRaises(ValueError): self.consumer.consume( ConsumeParams(lambda _: ...), QueueParams("queue") )
def test_on_msg(self): """ Verify on_msg calls result in a call to the consumer's function as well as long as a matching consumer tag entry is found. """ # Prep on_message_callback_called = False def on_message_callback(msg): if not msg == b'body' or isinstance(msg, ConsumeOK): return nonlocal on_message_callback_called on_message_callback_called = True queue_params = QueueParams("queue") self.consumer.consume(ConsumeParams(on_message_callback), queue_params=queue_params) frame_mock = Mock() frame_mock.method.consumer_tag = "123" self.consumer.on_consume_ok(frame_mock, queue_params=queue_params) deliver_mock = Mock() deliver_mock.consumer_tag = "123" # Run test self.consumer.on_msg(Mock(), deliver_mock, Mock(), b"body") # Assertions self.assertTrue(on_message_callback_called)
def test_on_consume_ok(self): """ Verify that the correct actions follows a consume OK. """ # Prep on_message_callback_called = False def on_message_callback(msg): if not isinstance(msg, ConsumeOK): return nonlocal on_message_callback_called on_message_callback_called = True consume_params = ConsumeParams(on_message_callback) queue_params = QueueParams("queue") frame = Mock() self.consumer.consume(consume_params, queue_params=queue_params) # Run test self.consumer.on_consume_ok(frame, queue_params=queue_params) # Assertions consume_instance = self.consumer._consumes[ _gen_consume_key(queue=queue_params.queue) ] self.assertEqual(consume_instance.consumer_tag, frame.method.consumer_tag) self.assertTrue(on_message_callback_called) # Called with ConsumeOK
def test_on_exchange_declared(self): """ Verify that the correct action follows a successful exchange declaration. """ # Prep self.consumer.bind_queue = Mock() exchange_params = ExchangeParams("exchange") consume_params = ConsumeParams(lambda _: ...) queue_params = QueueParams("queue") # Run test self.consumer.on_exchange_declared(exchange_params, None, consume_params=consume_params, queue_params=queue_params) # Assertions call_args = self.consumer.bind_queue.call_args queue_bind_params = call_args.args[0] self.consumer.bind_queue.assert_called_with( queue_bind_params, callback=ANY ) self.assertEqual(queue_bind_params.queue, consume_params.queue) self.assertEqual(queue_bind_params.exchange, exchange_params.exchange) self.assertEqual(queue_bind_params.routing_key, None)
def test_consume_from_exchange_and_queue(self): """ Verify RMQConsumer can consume from an exchange, binding it to a specific queue. """ msg_received = None event = threading.Event() def on_msg(msg): event.set() nonlocal msg_received msg_received = msg # Consume consume_key = self.consumer.consume( ConsumeParams(on_msg), exchange_params=ExchangeParams("exchange_fanout", exchange_type=ExchangeType.fanout), queue_params=QueueParams("queue_fanout_receiver")) self.assertEqual(consume_key, "queue_fanout_receiver|exchange_fanout") # Wait for ConsumeOK event.wait(timeout=1.0) self.assertTrue(isinstance(msg_received, ConsumeOK)) # Send a message and verify it is delivered OK event.clear() self.producer.publish(b"body", exchange_params=ExchangeParams( "exchange_fanout", exchange_type=ExchangeType.fanout)) event.wait(timeout=1.0) self.assertEqual(msg_received, b"body")
def test_consume_queue_exchange_and_routing_key(self): """ Verify possibility to provide both an exchange, routing_key and a queue to the consume method. """ # Prep def on_msg(): pass consume = ConsumeParams(on_msg) queue = QueueParams("queue") exchange = ExchangeParams("exchange") # Run test self.consumer.consume(consume, queue_params=queue, exchange_params=exchange, routing_key="routing_key") # Assertions consume_key = _gen_consume_key(queue=queue.queue, exchange=exchange.exchange, routing_key="routing_key") consume_instance = self.consumer._consumes[consume_key] self.assertEqual(consume_instance.consume_params, consume) self.assertEqual(consume_instance.queue_params, queue) self.assertEqual(consume_instance.exchange_params, exchange) self.assertEqual(consume_instance.routing_key, "routing_key") self.consumer.declare_queue.assert_called_with(queue, callback=ANY)
def set_up_confirmed_consume(self) -> str: """Helper that sets up queue-only confirmed consume.""" queue_params = QueueParams("queue") self.consumer.consume(ConsumeParams(lambda _: ...), queue_params=queue_params) frame_mock = Mock() frame_mock.method.consumer_tag = "123" self.consumer.on_consume_ok(frame_mock, queue_params=queue_params) return "123"
def unpair(hume_uuid): """ Issues an unpairing command to the target HUME. This will lead to the HUME being factory reset, and any device information is lost on the HUME end. """ payload = { "type": MessageType.UNPAIR } global _producer _producer.publish(json.dumps(payload).encode('utf-8'), queue_params=QueueParams(hume_uuid, durable=True))
def detach(hume_uuid, device_uuid): """ Issues a detach command to the a hume for a device. """ payload = { "type": MessageType.DETACH, "device_uuid": device_uuid } global _producer _producer.publish(json.dumps(payload).encode('utf-8'), queue_params=QueueParams(hume_uuid, durable=True))
def test_on_msg_callback_raises_exception(self): """ Verify that exceptions raised from the consumer callback as a result of an on_msg invocation does not lead to an exception in RMQConsumer. """ # Prep def on_message_callback(msg): if isinstance(msg, ConsumeOK): return raise ValueError self.consumer.consume(ConsumeParams(on_message_callback), queue_params=QueueParams("queue")) frame_mock = Mock() frame_mock.method.consumer_tag = "123" self.consumer.on_consume_ok(frame_mock, queue_params=QueueParams("queue")) deliver_mock = Mock() deliver_mock.consumer_tag = "123" # Run test, assertion is that no exception is uncaught self.consumer.on_msg(Mock(), deliver_mock, Mock(), b"body")
def test_publish_using_both_queue_params_and_exchange_params(self): """ Verify that a ValueError is raised if both queue and exchange parameters are provided to publish. """ # Prep queue_params = QueueParams("queue") exchange_params = ExchangeParams("exchange") # Run test + assertions with self.assertRaises(ValueError): self.producer.publish(b"body", exchange_params=exchange_params, queue_params=queue_params)
def discover_devices(hume_uuid: str, message_content: str): """ Issue a Discover devices command to a HUME. :param hume_uuid: UUID of the HUME to send the Discover devices message to. :param message_content: discover devices message content. """ global _producer _producer.publish( json.dumps( { "type": MessageType.DISCOVER_DEVICES, "content": message_content } ).encode('utf-8'), queue_params=QueueParams(hume_uuid, durable=True) )
def attach(hume_uuid: str, identifier: str): """ Issue an attach command for a device to a HUME. :param hume_uuid: UUID of the HUME that discovered the device. :param identifier: identifier of the device to attach. """ global _producer _producer.publish( json.dumps( { "type": MessageType.ATTACH_DEVICE, "identifier": identifier } ).encode('utf-8'), queue_params=QueueParams(hume_uuid, durable=True) )
def test_consume_queue_only(self): """Verify possibility to consume from a queue.""" # Prep def on_msg(): pass consume = ConsumeParams(on_msg) queue = QueueParams("queue") # Run test self.consumer.consume(consume, queue_params=queue) # # Assertions consume_key = _gen_consume_key(queue=queue.queue) consume_instance = self.consumer._consumes[consume_key] self.assertEqual(consume_instance.consume_params, consume) self.assertEqual(consume_instance.queue_params, queue) self.assertEqual(consume_instance.exchange_params, None) self.consumer.declare_queue.assert_called_with(queue, callback=ANY)
def test_on_consume_ok_callback_raises_exception(self): """ Verify that an exception raised from the consume OK notification does not crash the consumer. """ # Prep def crappy_callback(msg): # Random exception chosen, ANY exception shall be possible to # handle. raise ValueError queue_params = QueueParams("queue") self.consumer.consume(ConsumeParams(crappy_callback), queue_params=queue_params) # Run test # Assertion is that no unhandled exception happens :-) self.consumer.on_consume_ok(Mock(), queue_params=queue_params)
def test_publish_using_queue_params(self): """Verify possibility to publish providing only queue parameters.""" # Prep queue_params = QueueParams("queue") frame_mock = Mock() frame_mock.method.queue = "queue" # Run test + assertions self.producer.publish(b"body", queue_params=queue_params) self.producer.declare_queue.assert_called_with(queue_params, callback=ANY) self.producer.on_queue_declared(b"body", frame_mock, publish_params=None) self.producer.basic_publish.assert_called_with( b"body", exchange=DEFAULT_EXCHANGE, routing_key=queue_params.queue, publish_params=None)
def test_on_queue_declared_no_exchange(self): """ Verify that the correct action follows a successful queue declare. """ # Prep self.consumer.basic_consume = Mock() frame = Mock() consume_params = ConsumeParams(lambda _: ...) queue_params = QueueParams("queue") # Run test self.consumer.on_queue_declared(frame, consume_params=consume_params, queue_params=queue_params) # Assertions self.consumer.basic_consume.assert_called_with( consume_params, on_message_callback_override=self.consumer.on_msg, callback=ANY)
def test_declare_queue(self): """ Verify declaring a queue. """ # Prep queue_params = QueueParams("queue") def on_queue_declared(): ... # Test self.conn_imp.declare_queue(queue_params, callback=on_queue_declared) # Assert self.conn_imp._channel.queue_declare.assert_called_with( queue_params.queue, durable=queue_params.durable, exclusive=queue_params.exclusive, auto_delete=queue_params.auto_delete, arguments=queue_params.arguments, callback=on_queue_declared)
def test_on_queue_declared_exchange(self): """ Verify that the correct action follows a successful queue declare when exchange is specified. """ # Prep self.consumer.declare_exchange = Mock() frame = Mock() consume_params = ConsumeParams(lambda _: ...) queue_params = QueueParams("queue") exchange_params = ExchangeParams("exchange") # Run test self.consumer.on_queue_declared(frame, consume_params=consume_params, queue_params=queue_params, exchange_params=exchange_params) # Assertions self.assertTrue(isinstance(consume_params.queue, Mock)) self.consumer.declare_exchange.assert_called_with( exchange_params, callback=ANY)
def test_on_close_marks_consumes_stopped(self): """Verify a call to on_close marks all consumes as stopped.""" queue_consume = ConsumeParams(lambda _: ...) queue = QueueParams("queue") self.consumer.consume(queue_consume, queue_params=queue) exchange_consume = ConsumeParams(lambda _: ...) exchange = ExchangeParams("exchange") self.consumer.consume(exchange_consume, exchange_params=exchange) # Fake having all consumes started for _, consume in self.consumer._consumes.items(): consume.consumer_tag = "some tag" # Run test self.consumer.on_close() # Checks for _, consume in self.consumer._consumes.items(): if consume.consumer_tag == "some tag": self.fail( "a consume did not have its consumer tag cleared when " "on_close was called" )
def send_device_action(hume_uuid: str, device_uuid: str, **kwargs): """ Issues a device action command to a HUME for a specific device. :param hume_uuid: HUME to receive the action. :param device_uuid: device to receive the action. Possible kwargs: For a STATEFUL action, both of these parameters must be provided: group_id: group ID of a pointed out new device state. state_id: new device state. """ payload = { "type": MessageType.ACTION_STATEFUL, "device_uuid": device_uuid, } payload.update(kwargs) global _producer _producer.publish(json.dumps(payload).encode('utf-8'), queue_params=QueueParams(hume_uuid, durable=True))
def test_on_queue_bound(self): """ Verify that the correct action follows a successful queue bind operation. """ # Prep self.consumer.basic_consume = Mock() frame = Mock() consume_params = ConsumeParams(lambda _: ...) queue_params = QueueParams("queue") # Run test self.consumer.on_queue_bound(frame, consume_params=consume_params, queue_params=queue_params, exchange="exchange", routing_key=None) # Assertions self.consumer.basic_consume.assert_called_with( consume_params, on_message_callback_override=self.consumer.on_msg, callback=ANY )
def test_on_ready_starts_consumes(self): """ Verify that a call to on_ready results in re-issuing of all active consumes. """ # Prep self.consumer._handle_consume = Mock() queue_params = QueueParams("queue") exchange_params = ExchangeParams("exchange") self.consumer.consume(ConsumeParams(lambda _: ...), queue_params=queue_params) self.consumer.consume(ConsumeParams(lambda _: ...), exchange_params=exchange_params) # Run test self.consumer.on_ready() # Assertions self.consumer._handle_consume.assert_has_calls( (call(ANY, queue_params, None, None), call(ANY, None, exchange_params, None),) )
def test_publish_is_delayed_until_producer_connection_ready(self): """ Verify that the producer buffers messages that are to be sent when the producer connection is not in a ready state, and that the buffered messages are sent upon the producer becoming ready. """ # Prep exchange_params = ExchangeParams("exchange") frame_mock = Mock() frame_mock.method.queue = "queue" queue_params = QueueParams("queue") # Run test + assertions self.producer.on_close() self.producer.publish(b"body", exchange_params=exchange_params) self.producer.publish(b"body", queue_params=queue_params) self.assertEqual(2, len(self.producer._buffered_messages)) self.producer.on_ready() self.producer.declare_exchange.assert_called_with(exchange_params, callback=ANY) self.producer.declare_queue.assert_called_with(queue_params, callback=ANY)
def ready(self): """ Called once the application is ready. https://docs.djangoproject.com/en/3.1/ref/applications/ """ # Development specific, due to Django code reload ... # DEBUG indicates development, RUN_MAIN indicates it's not the # reloader reaching this block. if settings.DEBUG and not os.environ.get("RUN_MAIN"): return if BrokerConfig.has_started: return BrokerConfig.has_started = True # Set up and start the RabbitMQ client rmq_client_log_level = logging.INFO logger = logging.getLogger("rabbitmq_client") logger.setLevel(rmq_client_log_level) # Create handler to actually print something stream_handler = logging.StreamHandler() logger.addHandler(stream_handler) formatter = logging.Formatter(fmt="{asctime} {levelname:^8} " "{name} - {message}", style="{", datefmt="%d/%m/%Y %H:%M:%S") stream_handler.setFormatter(formatter) stream_handler.setLevel(rmq_client_log_level) credentials = pika.PlainCredentials(settings.HUME_BROKER_USERNAME, settings.HUME_BROKER_PASSWORD) connection_params = pika.ConnectionParameters( host=settings.HUME_BROKER_IP, port=settings.HUME_BROKER_PORT, virtual_host='/', credentials=credentials) hint_master_queue_parameters = QueueParams( settings.MASTER_COMMAND_QUEUE_NAME, durable=True) consumer = RMQConsumer(connection_parameters=connection_params) consumer.consume(ConsumeParams(incoming_message), queue_params=hint_master_queue_parameters) producer = RMQProducer(connection_parameters=connection_params) producer_module.init(producer) consumer.start() producer.start() def stop_func(_s, _f, consumer=None, producer=None): """ :param _s: signal leading to stop :param _f: frame when stop was called :param consumer: rabbitmq_client.RMQConsumer :param producer: rabbitmq_client.RMQProducer """ consumer.stop() producer.stop() stop_callback = functools.partial(stop_func, consumer=consumer, producer=producer) signal.signal(signal.SIGINT, stop_callback) signal.signal(signal.SIGTERM, stop_callback)
handler = logging.StreamHandler() handler.setLevel(logging.INFO) logger.addHandler(handler) producer = RMQProducer() producer.start() def stop(*args): producer.stop() sys.exit(0) signal.signal(signal.SIGINT, stop) producer.publish(b"queue publish 1", queue_params=QueueParams("queue")) producer.publish(b"queue publish 2", queue_params=QueueParams("queue")) producer.publish(b"queue publish 3", queue_params=QueueParams("queue")) producer.publish(b"exchange publish 1", exchange_params=ExchangeParams("direct"), routing_key="rkey") producer.publish(b"exchange publish 2", exchange_params=ExchangeParams( "fanout", exchange_type=ExchangeType.fanout)) time.sleep(0.3) producer.activate_confirm_mode(lambda x: print(x)) producer.publish(b"exchange publish 2", exchange_params=ExchangeParams( "fanout", exchange_type=ExchangeType.fanout)) producer.publish(b"exchange publish 2",