def test_release_ordering(self): """ Test order of released messages is as expected """ session = self.session session.queue_declare(queue = "q", exclusive=True, auto_delete=True) for i in range (1, 11): session.message_transfer(message=Message(session.delivery_properties(routing_key="q"), "released message %s" % (i))) session.message_subscribe(queue = "q", destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") queue = session.incoming("a") first = queue.get(timeout = 1) for i in range(2, 10): msg = queue.get(timeout = 1) self.assertEquals("released message %s" % (i), msg.body) last = queue.get(timeout = 1) self.assertEmpty(queue) released = RangedSet() released.add(first.id, last.id) session.message_release(released) #TODO: may want to clean this up... session.receiver._completed.add(first.id, last.id) session.channel.session_completed(session.receiver._completed) for i in range(1, 11): self.assertEquals("released message %s" % (i), queue.get(timeout = 1).body)
def test_credit_window_after_messagestop(self): """ Tests that the broker's credit window size doesnt exceed the requested value when completing previous messageTransfer commands after a message_stop and message_flow. """ session = self.session # create queue session.queue_declare(queue=self.test_queue_name, exclusive=True, auto_delete=True) # send 11 messages for i in range(1, 12): session.message_transfer( message=Message(session.delivery_properties(routing_key=self.test_queue_name), "message-%d" % (i)) ) # subscribe: session.message_subscribe(queue=self.test_queue_name, destination="a") a = session.incoming("a") session.message_set_flow_mode(flow_mode=1, destination="a") session.message_flow(unit=session.credit_unit.byte, value=0xFFFFFFFFL, destination="a") # issue 5 message credits session.message_flow(unit=session.credit_unit.message, value=5, destination="a") # get 5 messages ids = RangedSet() for i in range(1, 6): msg = a.get(timeout=1) self.assertEquals("message-%d" % (i), msg.body) ids.add(msg.id) # now try and read a 6th message. we expect this to fail due to exhausted message credit. try: extra = a.get(timeout=1) self.fail("Got unexpected message: " + extra.body) except Empty: None session.message_stop(destination="a") session.message_flow(unit=session.credit_unit.byte, value=0xFFFFFFFFL, destination="a") session.message_flow(unit=session.credit_unit.message, value=5, destination="a") # complete earlier messages after setting the window to 5 message credits session.channel.session_completed(ids) # Now continue to read the next 5 messages for i in range(6, 11): msg = a.get(timeout=1) self.assertEquals("message-%d" % (i), msg.body) # now try and read the 11th message. we expect this to fail due to exhausted message credit. If we receive an # 11th this indicates the broker is not respecting the client's requested window size. try: extra = a.get(timeout=1) self.fail("Got unexpected message: " + extra.body) except Empty: None
def test_auto_rollback(self): """ Test that a session closed with an open transaction is effectively rolled back """ session = self.session self.declare_queues( ["tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c"]) session.message_subscribe(queue="tx-autorollback-a", destination="qa") session.message_subscribe(queue="tx-autorollback-b", destination="qb") session.message_subscribe(queue="tx-autorollback-c", destination="qc") session2 = self.conn.session("worker", 2) queue_a, queue_b, queue_c, ignore = self.perform_txn_work( session2, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c") for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) self.fail("Got unexpected message: " + extra.body) except Empty: None session2.close() session.tx_select() self.enable_flow("qa") queue_a = session.incoming("qa") self.enable_flow("qb") queue_b = session.incoming("qb") self.enable_flow("qc") queue_c = session.incoming("qc") #check results for i in range(1, 5): msg = queue_a.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) session.message_accept(RangedSet(msg.id)) msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.body) session.message_accept(RangedSet(msg.id)) msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.body) session.message_accept(RangedSet(msg.id)) for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup session.tx_commit()
def test_commit(self): """ Test that commited publishes are delivered and commited acks are not re-delivered """ session = self.session #declare queues and create subscribers in the checking session #to ensure that the queues are not auto-deleted too early: self.declare_queues(["tx-commit-a", "tx-commit-b", "tx-commit-c"]) session.message_subscribe(queue="tx-commit-a", destination="qa") session.message_subscribe(queue="tx-commit-b", destination="qb") session.message_subscribe(queue="tx-commit-c", destination="qc") #use a separate session for actual work session2 = self.conn.session("worker", 2) self.perform_txn_work(session2, "tx-commit-a", "tx-commit-b", "tx-commit-c") session2.tx_commit() session2.close() session.tx_select() self.enable_flow("qa") queue_a = session.incoming("qa") self.enable_flow("qb") queue_b = session.incoming("qb") self.enable_flow("qc") queue_c = session.incoming("qc") #check results for i in range(1, 5): msg = queue_c.get(timeout=1) self.assertEqual("TxMessage %d" % i, msg.body) session.message_accept(RangedSet(msg.id)) msg = queue_b.get(timeout=1) self.assertEqual("TxMessage 6", msg.body) session.message_accept(RangedSet(msg.id)) msg = queue_a.get(timeout=1) self.assertEqual("TxMessage 7", msg.body) session.message_accept(RangedSet(msg.id)) for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup session.tx_commit()
def test_implicit_end(self): """ Test that an association is implicitly ended when the session is closed (whether by exception or explicit client request) and the transaction in question is marked as rollback only. """ session1 = self.session session2 = self.conn.session("other", 2) #setup: session2.queue_declare(queue="dummy", exclusive=True, auto_delete=True) session2.message_transfer( self.createMessage(session2, "dummy", "a", "whatever")) tx = self.xid("dummy") session2.dtx_select() session2.dtx_start(xid=tx) session2.message_subscribe(queue="dummy", destination="dummy") session2.message_flow(destination="dummy", unit=session2.credit_unit.message, value=1) session2.message_flow(destination="dummy", unit=session2.credit_unit.byte, value=0xFFFFFFFFL) msg = session2.incoming("dummy").get(timeout=1) session2.message_accept(RangedSet(msg.id)) session2.message_cancel(destination="dummy") session2.message_transfer( self.createMessage(session2, "dummy", "b", "whatever")) session2.close() self.assertEqual(self.XA_RBROLLBACK, session1.dtx_prepare(xid=tx).status) session1.dtx_rollback(xid=tx)
def test_ack_and_no_ack(self): """ First, this test tries to receive a message with a no-ack consumer. Second, this test tries to explicitly receive and acknowledge a message with an acknowledging consumer. """ session = self.session session.queue_declare(queue = "myqueue", exclusive=True, auto_delete=True) # No ack consumer ctag = "tag1" session.message_subscribe(queue = "myqueue", destination = ctag) session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFFL) session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFFL) body = "test no-ack" session.message_transfer(message=Message(session.delivery_properties(routing_key="myqueue"), body)) msg = session.incoming(ctag).get(timeout = 5) self.assert_(msg.body == body) # Acknowledging consumer session.queue_declare(queue = "otherqueue", exclusive=True, auto_delete=True) ctag = "tag2" session.message_subscribe(queue = "otherqueue", destination = ctag, accept_mode = 1) session.message_flow(destination=ctag, unit=session.credit_unit.message, value=0xFFFFFFFFL) session.message_flow(destination=ctag, unit=session.credit_unit.byte, value=0xFFFFFFFFL) body = "test ack" session.message_transfer(message=Message(session.delivery_properties(routing_key="otherqueue"), body)) msg = session.incoming(ctag).get(timeout = 5) session.message_accept(RangedSet(msg.id)) self.assert_(msg.body == body)
def test_ranged_ack(self): """ Test acking of messages ranges """ session = self.conn.session("alternate-session", timeout=10) session.queue_declare(queue = "q", auto_delete=True) delivery_properties = session.delivery_properties(routing_key="q") for i in range (1, 11): session.message_transfer(message=Message(delivery_properties, "message %s" % (i))) session.message_subscribe(queue = "q", destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") queue = session.incoming("a") ids = [] for i in range (1, 11): msg = queue.get(timeout = 1) self.assertEquals("message %s" % (i), msg.body) ids.append(msg.id) self.assertEmpty(queue) #ack all but the fourth message (command id 2) accepted = RangedSet() accepted.add(ids[0], ids[2]) accepted.add(ids[4], ids[9]) session.message_accept(accepted) #subscribe from second session here to ensure queue is not #auto-deleted when alternate session closes (no need to ack on these): self.session.message_subscribe(queue = "q", destination = "checker") #now close the session, and see that the unacked messages are #then redelivered to another subscriber: session.close(timeout=10) session = self.session session.message_flow(destination="checker", unit=session.credit_unit.message, value=0xFFFFFFFFL) session.message_flow(destination="checker", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("checker") self.assertEquals("message 4", queue.get(timeout = 1).body) self.assertEmpty(queue)
def test_rollback(self): """ Test that rolled back publishes are not delivered and rolled back acks are re-delivered """ session = self.session queue_a, queue_b, queue_c, consumed = self.perform_txn_work( session, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c") for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) self.fail("Got unexpected message: " + extra.body) except Empty: None session.tx_rollback() #need to release messages to get them redelivered now: session.message_release(consumed) #check results for i in range(1, 5): msg = queue_a.get(timeout=1) self.assertEqual("Message %d" % i, msg.body) session.message_accept(RangedSet(msg.id)) msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.body) session.message_accept(RangedSet(msg.id)) msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.body) session.message_accept(RangedSet(msg.id)) for q in [queue_a, queue_b, queue_c]: try: extra = q.get(timeout=1) self.fail("Got unexpected message: " + extra.body) except Empty: None #cleanup session.tx_commit()
def dump_queue(queue): content = "" # Content of the last message read final = "That's all, folks!" # In a message body, signals the last message message = 0 while content != final: try: message = queue.get(timeout=10) content = message.body session.message_accept(RangedSet(message.id)) print content except Empty: print "No more messages!" return
def test_reject_no_match(self): """ Test that on rejecting a message, if the queues own alternate exchange cannot find a match for the message, the alternate-exchange of that exchange will be tried. Note: though the spec rules out going to the alternate-exchanges alternate exchange when sending to an exchange, it does not cover this case. """ session = self.session dlq = self.setup_dlq() #setu up an 'intermediary' exchange session.exchange_declare(exchange="my-exchange", type="direct", alternate_exchange="dlq") #create a queue using the intermediary as its alternate exchange: session.queue_declare(queue="delivery-queue", alternate_exchange="my-exchange", auto_delete=True) #send it some messages: dp = self.session.delivery_properties(routing_key="delivery-queue") for m in ["One", "Two", "Three"]: session.message_transfer(message=Message(dp, m)) #get and reject those messages: session.message_subscribe(destination="a", queue="delivery-queue") session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFFL) session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFFL) incoming = session.incoming("a") for m in ["One", "Two", "Three"]: msg = incoming.get(timeout=1) self.assertEqual(m, msg.body) session.message_reject(RangedSet(msg.id)) session.message_cancel(destination="a") #check the messages were delivered to the dlq: for m in ["One", "Two", "Three"]: self.assertEqual(m, dlq.get(timeout=1).body) self.assertEmpty(dlq) #cleanup: session.exchange_delete(exchange="my-exchange") session.exchange_delete(exchange="dlq")
def swap(self, session, src, dest): #consume from src: session.message_subscribe(destination="temp-swap", queue=src) session.message_flow(destination="temp-swap", unit=session.credit_unit.message, value=1) session.message_flow(destination="temp-swap", unit=session.credit_unit.byte, value=0xFFFFFFFFL) msg = session.incoming("temp-swap").get(timeout=1) session.message_cancel(destination="temp-swap") session.message_accept(RangedSet(msg.id)) #todo: also complete at this point? #re-publish to dest: dp = session.delivery_properties(routing_key=dest) mp = session.message_properties( correlation_id=self.getMessageProperty(msg, 'correlation_id')) session.message_transfer(message=Message(dp, mp, msg.body))
def test_ack_message_from_deleted_queue(self): session = self.session session.auto_sync = False #create queue session.queue_declare(queue = "q", auto_delete=True, durable=True) #send message dp = session.delivery_properties(routing_key="q", delivery_mode=2) session.message_transfer(message=Message(dp, "my-message")) #create consumer session.message_subscribe(queue = "q", destination = "a", accept_mode = 1, acquire_mode=0) session.message_flow(unit = session.credit_unit.byte, value = 0xFFFFFFFFL, destination = "a") session.message_flow(unit = session.credit_unit.message, value = 10, destination = "a") queue = session.incoming("a") #consume the message, cancel subscription (triggering auto-delete), then ack it msg = queue.get(timeout = 5) session.message_cancel(destination = "a") session.message_accept(RangedSet(msg.id))
def test_end(self): """ Verify that the association is terminated by end and subsequent operations are non-transactional """ guard = self.keepQueuesAlive(["tx-queue"]) session = self.conn.session("alternate", 1) session.queue_declare(queue="tx-queue", exclusive=True, auto_delete=True) #publish a message under a transaction session.dtx_select() tx = self.xid("dummy") session.dtx_start(xid=tx) session.message_transfer( self.createMessage(session, "tx-queue", "one", "DtxMessage")) session.dtx_end(xid=tx) #now that association with txn is ended, publish another message session.message_transfer( self.createMessage(session, "tx-queue", "two", "DtxMessage")) #check the second message is available, but not the first self.assertMessageCount(1, "tx-queue") self.subscribe(session, queue="tx-queue", destination="results") msg = session.incoming("results").get(timeout=1) self.assertEqual("two", self.getMessageProperty(msg, 'correlation_id')) session.message_cancel(destination="results") #ack the message then close the session session.message_accept(RangedSet(msg.id)) session.close() session = self.session #commit the transaction and check that the first message (and #only the first message) is then delivered session.dtx_commit(xid=tx, one_phase=True) self.assertMessageCount(1, "tx-queue") self.assertMessageId("one", "tx-queue")
def message_accept(self, message): try: self.session.message_accept(RangedSet(message.id)) except SessionClosed: log.debug("Accepted message on closed session: %s" % message.id) pass
def test_example(self): """ An example test. Note that test functions must start with 'test_' to be recognized by the test framework. """ # By inheriting TestBase, self.client is automatically connected # and self.session is automatically opened as session(1) # Other session methods mimic the protocol. session = self.session # Now we can send regular commands. If you want to see what the method # arguments mean or what other commands are available, you can use the # python builtin help() method. For example: #help(chan) #help(chan.exchange_declare) # If you want browse the available protocol methods without being # connected to a live server you can use the amqp-doc utility: # # Usage amqp-doc [<options>] <spec> [<pattern_1> ... <pattern_n>] # # Options: # -e, --regexp use regex instead of glob when matching # Now that we know what commands are available we can use them to # interact with the server. # Here we use ordinal arguments. session.exchange_declare("test", "direct") # Here we use keyword arguments. session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True) session.exchange_bind(queue="test-queue", exchange="test", binding_key="key") # Call Session.subscribe to register as a consumer. # All the protocol methods return a message object. The message object # has fields corresponding to the reply method fields, plus a content # field that is filled if the reply includes content. In this case the # interesting field is the consumer_tag. session.message_subscribe(queue="test-queue", destination="consumer_tag") session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFFL) session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFFL) # We can use the session.incoming(...) method to access the messages # delivered for our consumer_tag. queue = session.incoming("consumer_tag") # Now lets publish a message and see if our consumer gets it. To do # this we need to import the Message class. delivery_properties = session.delivery_properties(routing_key="key") sent = Message(delivery_properties, "Hello World!") session.message_transfer(destination="test", message=sent) # Now we'll wait for the message to arrive. We can use the timeout # argument in case the server hangs. By default queue.get() will wait # until a message arrives or the connection to the server dies. msg = queue.get(timeout=10) # And check that we got the right response with assertEqual self.assertEqual(sent.body, msg.body) # Now acknowledge the message. session.message_accept(RangedSet(msg.id))
session.queue_delete(queue="delete-me-2", if_empty=True) self.fail("Expected delete if_empty to fail for non-empty queue") except SessionException, e: self.assertEquals(406, e.args[0].error_code) #need new session now: session = self.conn.session("replacement", 2) #empty queue: session.message_subscribe(destination="consumer_tag", queue="delete-me-2") session.message_flow(destination="consumer_tag", unit=session.credit_unit.message, value=0xFFFFFFFFL) session.message_flow(destination="consumer_tag", unit=session.credit_unit.byte, value=0xFFFFFFFFL) queue = session.incoming("consumer_tag") msg = queue.get(timeout=1) self.assertEqual("message", msg.body) session.message_accept(RangedSet(msg.id)) session.message_cancel(destination="consumer_tag") #retry deletion on empty queue: session.queue_delete(queue="delete-me-2", if_empty=True) #check that it has gone by declaring passively: try: session.queue_declare(queue="delete-me-2", passive=True) self.fail("Queue has not been deleted") except SessionException, e: self.assertEquals(404, e.args[0].error_code) def test_delete_ifunused(self): """ Test that if_unused field of queue_delete is honoured
def perform_txn_work(self, session, name_a, name_b, name_c): """ Utility method that does some setup and some work under a transaction. Used for testing both commit and rollback """ #setup: self.declare_queues([name_a, name_b, name_c]) key = "my_key_" + name_b topic = "my_topic_" + name_c session.exchange_bind(queue=name_b, exchange="amq.direct", binding_key=key) session.exchange_bind(queue=name_c, exchange="amq.topic", binding_key=topic) dp = session.delivery_properties(routing_key=name_a) for i in range(1, 5): mp = session.message_properties(message_id="msg%d" % i) session.message_transfer(message=Message(dp, mp, "Message %d" % i)) dp = session.delivery_properties(routing_key=key) mp = session.message_properties(message_id="msg6") session.message_transfer(destination="amq.direct", message=Message(dp, mp, "Message 6")) dp = session.delivery_properties(routing_key=topic) mp = session.message_properties(message_id="msg7") session.message_transfer(destination="amq.topic", message=Message(dp, mp, "Message 7")) session.tx_select() #consume and ack messages acked = RangedSet() self.subscribe(session, queue=name_a, destination="sub_a") queue_a = session.incoming("sub_a") for i in range(1, 5): msg = queue_a.get(timeout=1) acked.add(msg.id) self.assertEqual("Message %d" % i, msg.body) self.subscribe(session, queue=name_b, destination="sub_b") queue_b = session.incoming("sub_b") msg = queue_b.get(timeout=1) self.assertEqual("Message 6", msg.body) acked.add(msg.id) sub_c = self.subscribe(session, queue=name_c, destination="sub_c") queue_c = session.incoming("sub_c") msg = queue_c.get(timeout=1) self.assertEqual("Message 7", msg.body) acked.add(msg.id) session.message_accept(acked) dp = session.delivery_properties(routing_key=topic) #publish messages for i in range(1, 5): mp = session.message_properties(message_id="tx-msg%d" % i) session.message_transfer(destination="amq.topic", message=Message(dp, mp, "TxMessage %d" % i)) dp = session.delivery_properties(routing_key=key) mp = session.message_properties(message_id="tx-msg6") session.message_transfer(destination="amq.direct", message=Message(dp, mp, "TxMessage 6")) dp = session.delivery_properties(routing_key=name_a) mp = session.message_properties(message_id="tx-msg7") session.message_transfer(message=Message(dp, mp, "TxMessage 7")) return queue_a, queue_b, queue_c, acked
def fetch(self, timeout=None): message = self.__queue.get(timeout=timeout) self.__session.message_accept(RangedSet(message.id)) return message