def test_build_protocol_different_delegates(self): """Test building AMQClient getting different delegates.""" address = IPv4Address("TCP", "127.0.0.1", 5672) factory = AMQFactory() client1 = factory.buildProtocol(address) client2 = factory.buildProtocol(address) self.assertIsNot(client2.delegate, client1.delegate)
def setUp(self): super(NotificationSourceTest, self).setUp() self.useFixture(CaptureTwistedLogs()) self.logger = Logger() self.clock = Clock() self.factory = AMQFactory(clock=self.clock, spec=AMQP0_8_SPEC_PATH) self.connector = FakeConnector(self.factory, logger=self.logger) self.source = NotificationSource(self.connector, clock=self.clock)
def test_build_protocol(self): """Test building AMQClient instances with default parameters.""" address = IPv4Address("TCP", "127.0.0.1", 5672) factory = AMQFactory() client = factory.buildProtocol(address) self.assertIsInstance(client, AMQClient) self.assertEqual("/", client.vhost) self.assertEqual(DEFAULT_SPEC, client.spec.file) self.assertEqual(0, client.heartbeatInterval)
def setUp(self): super(NotificationSourceTest, self).setUp() self.useFixture(CaptureTwistedLogs()) self.logger = Logger() self.clock = Clock() self.factory = AMQFactory(clock=self.clock, spec=AMQP0_8_SPEC_PATH) self.connector = FakeConnector(self.factory, logger=self.logger) self.source = NotificationSource(self.connector, clock=self.clock)
def test_build_protocol_custom_parameters(self): """Test building AMQClient instances with custom parameters.""" address = IPv4Address("TCP", "127.0.0.1", 5672) spec = "../specs/rabbitmq/amqp0-8.stripped.rabbitmq.xml" clock = Clock() factory = AMQFactory(spec=spec, clock=clock) factory.set_vhost("foo") factory.set_heartbeat(1) client = factory.buildProtocol(address) self.assertEqual("foo", client.vhost) self.assertEqual(spec, client.spec.file) self.assertEqual(1, client.heartbeatInterval) self.assertEqual(1, len(clock.getDelayedCalls()))
def test_connect(self): """ The endpoint returns a connected and authenticated client. """ factory = AMQFactory(spec=self.spec) endpoint = AMQEndpoint(reactor, self.host, self.port, username=self.user, password=self.password, vhost=self.vhost) client = yield endpoint.connect(factory) channel = yield client.channel(1) yield channel.channel_open() yield client.close()
def setUp(self): super(NotificationSourceIntegrationTest, self).setUp() self.endpoint = AMQEndpoint(reactor, self.rabbit.config.hostname, self.rabbit.config.port, username="******", password="******", heartbeat=1) self.policy = backoffPolicy(initialDelay=0) self.factory = AMQFactory(spec=AMQP0_8_SPEC_PATH) self.service = ClientService(self.endpoint, self.factory, retryPolicy=self.policy) self.connector = NotificationConnector(self.service) self.source = NotificationSource(self.connector) self.client = yield self.endpoint.connect(self.factory) self.channel = yield self.client.channel(1) yield self.channel.channel_open() yield self.channel.queue_declare(queue="uuid") self.service.startService()
class NotificationSourceTest(TestCase): def setUp(self): super(NotificationSourceTest, self).setUp() self.useFixture(CaptureTwistedLogs()) self.logger = Logger() self.clock = Clock() self.factory = AMQFactory(clock=self.clock, spec=AMQP0_8_SPEC_PATH) self.connector = FakeConnector(self.factory, logger=self.logger) self.source = NotificationSource(self.connector, clock=self.clock) def test_get(self): """ A new Notification is fired as soon as available on the queue matching the given UUID. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_queue_timeout(self): """ If the configured timeout expires while waiting for a message, from the subscribed queued, d Timeout exception is raised. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_timeout()) def test_get_with_retry_loop_timeout(self): """ The retry loop gets interrupted if hits the configured timeout, and a Timeout exception is raised. """ deferred = self.source.get("uuid", 1) # Let some time elapse and fail the first try simulating a broker # shutdown. channel = self.connector.transport.channel(1) self.clock.advance(self.source.timeout / 2) channel.connection_close(reply_code=320) # Let some more time elapse and fail the second try too (this time # with a queue timeout). channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout / 2) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_timeout()) def test_get_with_retry_after_connection_lost(self): """ The retry loop gets interrupted it hits the configured timeout, and a Timeout exceptionis raised. """ deferred = self.source.get("uuid", 1) # Let some time elapse and fail the first try. channel = self.connector.transport.channel(1) self.clock.advance(self.source.timeout / 2) channel.connection_close(reply_code=320) # Let some more time elapse and fail the second try too (this time # with a queue timeout). channel = self.connector.transport.channel(1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout / 2) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_timeout()) def test_get_with_heartbeat_check_failure(self): """ If the connection gets dropped because of a heartbeat check failure, we keep trying again. """ self.factory.setHeartbeat(1) deferred = self.source.get("uuid", 1) self.clock.advance(1) self.clock.advance(1) self.clock.advance(1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_transport_error(self): """ If the connection gets dropped because of a transport failure (e.g. the TCP socket got closed), we keep retrying. """ deferred = self.source.get("uuid", 1) self.connector.transport.loseConnection() channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_connection_closed_unexpected(self): """ If we got a connection-closed message from the broker with an unexpected error code, we raise an error. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.connection_close(reply_code=501) self.assertThat(deferred, fires_with_connection_closed()) def test_get_with_timeout_and_pending_message(self): """ If the configured timeout expires, but a message arrives while we are cancelling the consumer, the notification will be still fired. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout) channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_queue_closed(self): """ If the queue we're consuming from gets closed for whatever reason (for example the client got disconnected), we try again. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") # Simulate the broker being stopped channel.connection_close(reply_code=320, reply_text="shutdown") channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_queue_not_found(self): """ If we try to consume from a queue that doesn't exist, NotFound is raised. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.channel_close(reply_code=404, reply_text="not found") channel = self.connector.transport.channel(0) channel.connection_close_ok() self.assertThat(deferred, fires_with_not_found()) def test_get_with_queue_not_found_unclean_close(self): """ If when hitting 404 we fail to shutdown the AMQP connection cleanly within 5 seconds, the client just forces a close. """ client = yield self.connector() deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.channel_close(reply_code=404, reply_text="not found") self.clock.advance(5) self.assertThat(deferred, fires_with_not_found()) self.assertTrue(self.successResultOf(client.disconnected.wait())) def test_get_with_concurrent_consume_calls(self): """ Calls to basic_consume get serialized, and in case of a 404 failure the ones not affected get retried. """ deferred1 = self.source.get("uuid1", 1) deferred2 = self.source.get("uuid2", 1) # Make the first call fail with 404 channel = self.connector.transport.channel(1) channel.channel_close(reply_code=404, reply_text="not found") channel = self.connector.transport.channel(0) channel.connection_close_ok() self.assertThat(deferred1, fires_with_not_found()) # The second call will be retried self.assertEqual(2, self.connector.connections) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid2.1") channel.deliver("foo", consumer_tag='uuid2.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid2.1") self.assertThat(deferred2, fires_with_payload("foo")) def test_notification_ack(self): """ Calling Notification.ack() acknowledges a notification. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid.1") notification = extract_result(deferred) self.connector.transport.outgoing.clear() notification.ack() [frame] = self.connector.transport.outgoing[1] self.assertEqual("ack", frame.payload.method.name) self.assertEqual((1, False), frame.payload.args) def test_notification_ack_is_serialized(self): """ Calls to Notification.ack() are serialized, so they don't get spurious errors from unrelated channel commands. """ deferred = self.source.get("uuid1", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid1.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") notification = extract_result(deferred) # Simulate a concurrent get() call locking the channel during its # basic-consume. self.source.get("uuid2", 1) # Calling Notification.ack() now will not result in any outgoing frame, # since the call will wait for the basic-consume above to complete. self.connector.transport.outgoing.clear() notification.ack() self.assertEqual({}, self.connector.transport.outgoing) # As soon as the channel lock is released, the frame is sent. channel.basic_consume_ok(consumer_tag="uuid2.1") [frame] = self.connector.transport.outgoing[1] self.assertEqual("ack", frame.payload.method.name) self.assertEqual((1, False), frame.payload.args) def test_notification_reject(self): """ Calling Notification.reject() rejects a notification. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid.1") notification = extract_result(deferred) self.connector.transport.outgoing.clear() notification.reject() [frame] = self.connector.transport.outgoing[1] self.assertEqual("reject", frame.payload.method.name) self.assertEqual((1, True), frame.payload.args) def test_notification_bounced(self): """ If an error happens while ack'ing a Notification, a Bounced exception is raised. """ deferred = self.source.get("uuid1", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid1.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") notification = extract_result(deferred) # Simulate the broker shutting down. channel.connection_close(reply_code=320, reply_text="shutdown") self.assertRaises(Bounced, extract_result, notification.ack())
class NotificationSourceTest(TestCase): def setUp(self): super(NotificationSourceTest, self).setUp() self.useFixture(CaptureTwistedLogs()) self.logger = Logger() self.clock = Clock() self.factory = AMQFactory(clock=self.clock, spec=AMQP0_8_SPEC_PATH) self.connector = FakeConnector(self.factory, logger=self.logger) self.source = NotificationSource(self.connector, clock=self.clock) def test_get(self): """ A new Notification is fired as soon as available on the queue matching the given UUID. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_queue_timeout(self): """ If the configured timeout expires while waiting for a message, from the subscribed queued, d Timeout exception is raised. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_timeout()) def test_get_with_retry_loop_timeout(self): """ The retry loop gets interrupted if hits the configured timeout, and a Timeout exception is raised. """ deferred = self.source.get("uuid", 1) # Let some time elapse and fail the first try simulating a broker # shutdown. channel = self.connector.transport.channel(1) self.clock.advance(self.source.timeout / 2) channel.connection_close(reply_code=320) # Let some more time elapse and fail the second try too (this time # with a queue timeout). channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout / 2) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_timeout()) def test_get_with_retry_after_connection_lost(self): """ The retry loop gets interrupted it hits the configured timeout, and a Timeout exceptionis raised. """ deferred = self.source.get("uuid", 1) # Let some time elapse and fail the first try. channel = self.connector.transport.channel(1) self.clock.advance(self.source.timeout / 2) channel.connection_close(reply_code=320) # Let some more time elapse and fail the second try too (this time # with a queue timeout). channel = self.connector.transport.channel(1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout / 2) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_timeout()) def test_get_with_heartbeat_check_failure(self): """ If the connection gets dropped because of a heartbeat check failure, we keep trying again. """ self.factory.setHeartbeat(1) deferred = self.source.get("uuid", 1) self.clock.advance(1) self.clock.advance(1) self.clock.advance(1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_transport_error(self): """ If the connection gets dropped because of a transport failure (e.g. the TCP socket got closed), we keep retrying. """ deferred = self.source.get("uuid", 1) self.connector.transport.loseConnection() channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertEqual(2, self.connector.connections) self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_connection_closed_unexpected(self): """ If we got a connection-closed message from the broker with an unexpected error code, we raise an error. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.connection_close(reply_code=501) self.assertThat(deferred, fires_with_connection_closed()) def test_get_with_timeout_and_pending_message(self): """ If the configured timeout expires, but a message arrives while we are cancelling the consumer, the notification will be still fired. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") self.clock.advance(self.source.timeout) channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_queue_closed(self): """ If the queue we're consuming from gets closed for whatever reason (for example the client got disconnected), we try again. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") # Simulate the broker being stopped channel.connection_close(reply_code=320, reply_text="shutdown") channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") self.assertThat(deferred, fires_with_payload("foo")) def test_get_with_queue_not_found(self): """ If we try to consume from a queue that doesn't exist, NotFound is raised. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.channel_close(reply_code=404, reply_text="not found") channel = self.connector.transport.channel(0) channel.connection_close_ok() self.assertThat(deferred, fires_with_not_found()) def test_get_with_queue_not_found_unclean_close(self): """ If when hitting 404 we fail to shutdown the AMQP connection cleanly within 5 seconds, the client just forces a close. """ client = yield self.connector() deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.channel_close(reply_code=404, reply_text="not found") self.clock.advance(5) self.assertThat(deferred, fires_with_not_found()) self.assertTrue(self.successResultOf(client.disconnected.wait())) def test_get_with_concurrent_consume_calls(self): """ Calls to basic_consume get serialized, and in case of a 404 failure the ones not affected get retried. """ deferred1 = self.source.get("uuid1", 1) deferred2 = self.source.get("uuid2", 1) # Make the first call fail with 404 channel = self.connector.transport.channel(1) channel.channel_close(reply_code=404, reply_text="not found") channel = self.connector.transport.channel(0) channel.connection_close_ok() self.assertThat(deferred1, fires_with_not_found()) # The second call will be retried self.assertEqual(2, self.connector.connections) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid2.1") channel.deliver("foo", consumer_tag='uuid2.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid2.1") self.assertThat(deferred2, fires_with_payload("foo")) def test_notification_ack(self): """ Calling Notification.ack() acknowledges a notification. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid.1") notification = extract_result(deferred) self.connector.transport.outgoing.clear() notification.ack() [frame] = self.connector.transport.outgoing[1] self.assertEqual("ack", frame.payload.method.name) self.assertEqual((1, False), frame.payload.args) def test_notification_ack_is_serialized(self): """ Calls to Notification.ack() are serialized, so they don't get spurious errors from unrelated channel commands. """ deferred = self.source.get("uuid1", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid1.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") notification = extract_result(deferred) # Simulate a concurrent get() call locking the channel during its # basic-consume. self.source.get("uuid2", 1) # Calling Notification.ack() now will not result in any outgoing frame, # since the call will wait for the basic-consume above to complete. self.connector.transport.outgoing.clear() notification.ack() self.assertEqual({}, self.connector.transport.outgoing) # As soon as the channel lock is released, the frame is sent. channel.basic_consume_ok(consumer_tag="uuid2.1") [frame] = self.connector.transport.outgoing[1] self.assertEqual("ack", frame.payload.method.name) self.assertEqual((1, False), frame.payload.args) def test_notification_reject(self): """ Calling Notification.reject() rejects a notification. """ deferred = self.source.get("uuid", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid.1") channel.deliver("foo", consumer_tag='uuid.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid.1") notification = extract_result(deferred) self.connector.transport.outgoing.clear() notification.reject() [frame] = self.connector.transport.outgoing[1] self.assertEqual("reject", frame.payload.method.name) self.assertEqual((1, True), frame.payload.args) def test_notification_bounced(self): """ If an error happens while ack'ing a Notification, a Bounced exception is raised. """ deferred = self.source.get("uuid1", 1) channel = self.connector.transport.channel(1) channel.basic_consume_ok(consumer_tag="uuid1.1") channel.deliver("foo", consumer_tag='uuid1.1', delivery_tag=1) channel.basic_cancel_ok(consumer_tag="uuid1.1") notification = extract_result(deferred) # Simulate the broker shutting down. channel.connection_close(reply_code=320, reply_text="shutdown") self.assertRaises(Bounced, extract_result, notification.ack())
def setUp(self): super(NotificationConnectorTest, self).setUp() self.clock = Clock() self.factory = AMQFactory(spec=AMQP0_8_SPEC_PATH, clock=self.clock) self.service = FakeClientService(self.factory) self.connector = NotificationConnector(self.service, clock=self.clock)
def setUp(self): super(AMQEndpointTest, self).setUp() self.reactor = MemoryReactorClock() self.factory = AMQFactory(clock=Clock())