Ejemplo n.º 1
0
def test_twisted_consume_update_callback():
    """Assert a second call to consume updates an existing callback."""
    queue = str(uuid.uuid4())
    queues = {queue: {"auto_delete": False, "arguments": {"x-expires": 60 * 1000}}}
    bindings = [{"queue": queue, "exchange": "amq.topic", "routing_keys": ["#"]}]

    callback1 = defer.Deferred()
    callback2 = defer.Deferred()

    consumers1 = yield api.twisted_consume(
        lambda m: reactor.callFromThread(callback1.callback, m), bindings, queues
    )
    api.publish(message.Message(), "amq.topic")
    _add_timeout(callback1, 10)
    try:
        yield callback1
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Never received message for initial callback")

    consumers2 = yield api.twisted_consume(
        lambda m: reactor.callFromThread(callback2.callback, m), bindings, queues
    )
    api.publish(message.Message(), "amq.topic")
    _add_timeout(callback2, 10)
    try:
        yield callback2
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Never received message for updated callback")

    assert consumers1[0]._tag == consumers2[0]._tag

    yield consumers2[0].cancel()
Ejemplo n.º 2
0
def test_twisted_consume_update_callback(queue_and_binding):
    """Assert a second call to consume updates an existing callback."""
    queues, bindings = queue_and_binding

    callback1 = defer.Deferred()
    callback2 = defer.Deferred()

    consumers1 = yield api.twisted_consume(
        lambda m: reactor.callFromThread(callback1.callback, m), bindings,
        queues)
    yield threads.deferToThread(api.publish, message.Message(), "amq.topic")
    _add_timeout(callback1, 10)
    try:
        yield callback1
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Never received message for initial callback")

    consumers2 = yield api.twisted_consume(
        lambda m: reactor.callFromThread(callback2.callback, m), bindings,
        queues)
    yield threads.deferToThread(api.publish, message.Message(), "amq.topic")
    _add_timeout(callback2, 10)
    try:
        yield callback2
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Never received message for updated callback")

    assert consumers1[0]._tag == consumers2[0]._tag

    yield consumers2[0].cancel()
Ejemplo n.º 3
0
    def test_bindings_list_of_dict(self, mock_service):
        """Assert consume is working(bindings type is dict)"""
        bindings = [{"queue": "q1", "exchange": "e1", "routing_keys": ["#"]}]

        api.twisted_consume(self.dummy_callback, bindings)

        mock_service._service.factory.consume.called_once_with(
            self.dummy_callback, bindings=bindings, queues=config.conf["queues"]
        )
Ejemplo n.º 4
0
    def test_bindings_dict(self, mock_service):
        """Assert consume wraps bindings in a list of just a plain dict"""
        bindings = {"queue": "q1", "exchange": "e1", "routing_keys": ["#"]}

        api.twisted_consume(self.dummy_callback, bindings)

        mock_service._service.factory.consume.called_once_with(
            self.dummy_callback, bindings=[bindings], queues=config.conf["queues"]
        )
Ejemplo n.º 5
0
    def test_wrap_bindings(self, mock_service):
        """Assert bindings are always passed to the factory as a list."""

        def callback(msg):
            pass

        api.twisted_consume(callback)

        mock_service._service.factory.consume.called_once_with(callback, [{}], {})
Ejemplo n.º 6
0
    def test_defaults(self, mock_service):
        """Assert that bindings and queues come from the config if not provided."""

        api.twisted_consume(self.dummy_callback)

        mock_service._service.factory.consume.called_once_with(
            self.dummy_callback,
            bindings=config.conf["bindings"],
            queues=config.conf["queues"],
        )
Ejemplo n.º 7
0
    def test_with_queues(self, mock_service):
        """Assert queues is used over the config if provided."""
        queues = {
            "q1": {
                "durable": True,
                "auto_delete": False,
                "exclusive": False,
                "arguments": {},
            }
        }

        api.twisted_consume(self.dummy_callback, bindings=[], queues=queues)
        mock_service._service.factory.consume.called_once_with(
            self.dummy_callback, bindings=[], queues=queues
        )
Ejemplo n.º 8
0
def test_no_read_permissions_queue_read_failure(admin_user):
    """
    Assert an errback occurs when unable to read from the queue due to
    permissions. This is a bit weird because the consumer has permission to
    register itself, but not to actually read from the queue so the result is
    what errors back.
    """
    url = "{base}permissions/%2F/{user}".format(base=HTTP_API, user=admin_user)
    body = {"configure": ".*", "write": ".*", "read": ""}
    resp = yield treq.put(url, json=body, auth=HTTP_AUTH, timeout=3)
    assert resp.code == 204

    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }

    amqp_url = "amqp://{user}:guest@localhost:5672/%2F".format(user=admin_user)
    with mock.patch.dict(config.conf, {"amqp_url": amqp_url}):
        consumers = yield api.twisted_consume(lambda x: x, [], queues)
    _add_timeout(consumers[0].result, 5)
    try:
        yield consumers[0].result
        pytest.fail("Call failed to raise an exception")
    except exceptions.PermissionException as e:
        assert e.reason == (
            "ACCESS_REFUSED - access to queue '{}' in vhost '/' refused for user "
            "'{}'".format(queue, admin_user))
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer calling its errback!")
Ejemplo n.º 9
0
def test_twisted_consume_cancel():
    """Assert canceling works with :func:`fedora_messaging.api.twisted_consume` API."""
    queue = str(uuid.uuid4())
    queues = {queue: {"auto_delete": False, "arguments": {"x-expires": 60 * 1000}}}
    bindings = [{"queue": queue, "exchange": "amq.topic", "routing_keys": ["#"]}]

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(lambda m: m, bindings, queues)
    consumers[0].result.addErrback(pytest.fail)

    server_queue = yield get_queue(queue)
    assert server_queue["consumers"] == 1

    try:
        d = consumers[0].cancel()
        _add_timeout(d, 5)
        yield d

        # Assert the consumer.result deferred has called back when the cancel
        # deferred fires.
        assert consumers[0].result.called

        # Finally make sure the server agrees that the consumer is canceled.
        server_queue = yield get_queue(queue)
        assert server_queue["consumers"] == 0
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer halting!")
Ejemplo n.º 10
0
def test_twisted_consume_nack_message(queue_and_binding):
    """Assert raising Nack causes the message to be replaced in the queue."""
    queues, bindings = queue_and_binding
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )
    nacked_messages = []

    def callback(message):
        """Nack the message, then halt."""
        nacked_messages.append(message)
        if len(nacked_messages) == 2:
            raise exceptions.HaltConsumer()
        raise exceptions.Nack()

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(callback, bindings, queues)
    yield threads.deferToThread(api.publish, msg, "amq.topic")

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
    except exceptions.HaltConsumer:
        # Assert the message was delivered, redelivered when Nacked, then acked by HaltConsumer
        server_queue = yield get_queue(queues)
        assert server_queue["consumers"] == 0
        assert server_queue["messages"] == 0
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer halting!")
    finally:
        yield consumers[0].cancel()
Ejemplo n.º 11
0
def test_twisted_consume_invalid_message():
    """Assert messages that fail validation are nacked."""
    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }
    bindings = [{
        "queue": queue,
        "exchange": "amq.topic",
        "routing_keys": ["test_twisted_invalid_message"],
    }]

    consumers = yield api.twisted_consume(
        lambda x: pytest.fail("Message should be nacked"), bindings, queues)

    url = HTTP_API + "exchanges/%2F/amq.topic/publish"
    body = {
        "properties": {},
        "routing_key": "test_twisted_invalid_message",
        "payload": "not json",
        "payload_encoding": "string",
    }
    response = yield treq.post(url, json=body, auth=HTTP_AUTH, timeout=3)
    response = yield response.json()
    assert response["routed"] is True
    yield consumers[0].cancel()
Ejemplo n.º 12
0
def test_no_read_permissions_queue_read_failure_pika1(admin_user,
                                                      queue_and_binding):
    """
    Assert an errback occurs when unable to read from the queue due to
    permissions. This is a bit weird because the consumer has permission to
    register itself, but not to actually read from the queue so the result is
    what errors back.
    """
    queues, bindings = queue_and_binding
    url = "{base}permissions/%2F/{user}".format(base=HTTP_API, user=admin_user)
    body = {"configure": ".*", "write": ".*", "read": ""}
    resp = yield treq.put(url, json=body, auth=HTTP_AUTH, timeout=3)
    assert resp.code == 204

    amqp_url = "amqp://{user}:guest@localhost:5672/%2F".format(user=admin_user)
    with mock.patch.dict(config.conf, {"amqp_url": amqp_url}):
        try:
            consumers = api.twisted_consume(lambda x: x, [], queues)
            _add_timeout(consumers, 5)
            yield consumers
            pytest.fail("Call failed to raise an exception")
        except exceptions.PermissionException as e:
            assert e.reason == (
                "ACCESS_REFUSED - access to queue '{}' in vhost '/' refused for user "
                "'{}'".format(list(queues.keys())[0], admin_user))
        except (defer.TimeoutError, defer.CancelledError):
            pytest.fail(
                "Timeout reached without consumer calling its errback!")
Ejemplo n.º 13
0
def test_no_write_permissions(admin_user):
    """Assert the call to twisted_consume errbacks on write permissions errors."""
    url = "{base}permissions/%2F/{user}".format(base=HTTP_API, user=admin_user)
    body = {"configure": ".*", "write": "", "read": ".*"}
    resp = yield treq.put(url, json=body, auth=HTTP_AUTH, timeout=3)
    assert resp.code == 204

    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }
    bindings = [{
        "queue": queue,
        "exchange": "amq.topic",
        "routing_keys": ["#"]
    }]

    amqp_url = "amqp://{user}:guest@localhost:5672/%2F".format(user=admin_user)
    try:
        with mock.patch.dict(config.conf, {"amqp_url": amqp_url}):
            yield api.twisted_consume(lambda x: x, bindings, queues)
        pytest.fail("Call failed to raise an exception")
    except exceptions.BadDeclaration as e:
        assert e.reason.args[0] == 403
        assert e.reason.args[1] == (
            "ACCESS_REFUSED - access to queue '{}' in vhost '/' refused for user"
            " '{}'".format(queue, admin_user))
Ejemplo n.º 14
0
def test_no_vhost_permissions(admin_user):
    """Assert a hint is given if the user doesn't have any access to the vhost"""
    url = "{base}permissions/%2F/{user}".format(base=HTTP_API, user=admin_user)
    resp = yield treq.delete(url, auth=HTTP_AUTH, timeout=3)
    assert resp.code == 204

    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }

    amqp_url = "amqp://{user}:guest@localhost:5672/%2F".format(user=admin_user)
    with mock.patch.dict(config.conf, {"amqp_url": amqp_url}):
        try:
            yield api.twisted_consume(lambda x: x, [], queues)
        except exceptions.ConnectionException as e:
            assert e.reason == (
                "The TCP connection appears to have started, but the TLS or AMQP "
                "handshake with the broker failed; check your connection and "
                "authentication parameters and ensure your user has permission "
                "to access the vhost")
Ejemplo n.º 15
0
def test_twisted_consume_serverside_cancel():
    """
    Assert the consumer halts and ``consumer.result`` errbacks when the server
    explicitly cancels the consumer (by deleting the queue).
    """
    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }
    bindings = [{
        "queue": queue,
        "exchange": "amq.topic",
        "routing_keys": ["#"]
    }]

    consumers = yield api.twisted_consume(lambda x: x, bindings, queues)

    # Delete the queue and assert the consumer errbacks
    url = "{base}queues/%2F/{queue}".format(base=HTTP_API, queue=queue)
    yield treq.delete(url, auth=HTTP_AUTH, timeout=3)

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
        pytest.fail("Consumer did not errback!")
    except exceptions.ConsumerCanceled:
        pass
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer calling its errback!")
Ejemplo n.º 16
0
def test_twisted_consume_halt_consumer_requeue(queue_and_binding):
    """Assert raising HaltConsumer with requeue=True re-queues the message."""
    queues, bindings = queue_and_binding
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )

    def callback(message):
        """Count to 3 and quit."""
        raise exceptions.HaltConsumer(exit_code=1, requeue=True)

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(callback, bindings, queues)
    yield threads.deferToThread(api.publish, msg, "amq.topic")

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
    except exceptions.HaltConsumer as e:
        # Assert there are no consumers for the queue, and that there's a ready message
        assert e.exit_code == 1

        server_queue = yield get_queue(queues)
        assert server_queue["consumers"] == 0
        assert server_queue["messages_ready"] == 1
    except (defer.TimeoutError, defer.CancelledError):
        yield consumers[0].cancel()
        pytest.fail("Timeout reached without consumer halting!")
Ejemplo n.º 17
0
def test_twisted_consume_cancel(queue_and_binding):
    """Assert canceling works with :func:`fedora_messaging.api.twisted_consume` API."""
    queues, bindings = queue_and_binding

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(lambda m: m, bindings, queues)
    consumers[0].result.addErrback(pytest.fail)

    server_queue = yield get_queue(queues)
    assert server_queue["consumers"] == 1

    try:
        d = consumers[0].cancel()
        _add_timeout(d, 5)
        yield d

        # Assert the consumer.result deferred has called back when the cancel
        # deferred fires.
        assert consumers[0].result.called

        # Finally make sure the server agrees that the consumer is canceled.
        server_queue = yield get_queue(queues)
        assert server_queue["consumers"] == 0
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer halting!")
Ejemplo n.º 18
0
def test_twisted_consume_drop_message():
    """Assert raising Drop causes the message to be dropped, but processing continues."""
    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }
    bindings = [{
        "queue": queue,
        "exchange": "amq.topic",
        "routing_keys": ["#"]
    }]
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )
    dropped_messages = []

    def callback(message):
        """Drop 1 message and then halt on the second message."""
        dropped_messages.append(message)
        if len(dropped_messages) == 2:
            raise exceptions.HaltConsumer()
        raise exceptions.Drop()

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(callback, bindings, queues)
    api.publish(msg, "amq.topic")
    api.publish(msg, "amq.topic")

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
    except exceptions.HaltConsumer:
        # Assert both messages are delivered, no messages are un-acked, and only one
        # message got a positive acknowledgment.
        server_queue = yield task.deferLater(
            reactor,
            5,
            treq.get,
            HTTP_API + "queues/%2F/" + queue,
            auth=HTTP_AUTH,
            timeout=3,
        )
        server_queue = yield server_queue.json()
        assert server_queue["consumers"] == 0
        assert server_queue["messages"] == 0
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer halting!")
    finally:
        yield consumers[0].cancel()
Ejemplo n.º 19
0
def test_twisted_consume_general_exception():
    """
    Assert if the callback raises an unhandled exception, it is passed on to the
    consumer.result and the message is re-queued.
    """
    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }
    bindings = [{
        "queue": queue,
        "exchange": "amq.topic",
        "routing_keys": ["#"]
    }]
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )

    def callback(message):
        """An *exceptionally* useless callback"""
        raise Exception("Oh the huge manatee")

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(callback, bindings, queues)
    api.publish(msg, "amq.topic")

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
        pytest.fail("Expected an exception to be raised.")
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer halting!")
    except Exception as e:
        # Assert the message was delivered and re-queued when the consumer crashed.
        assert e.args[0] == "Oh the huge manatee"
        server_queue = yield task.deferLater(
            reactor,
            10,
            treq.get,
            HTTP_API + "queues/%2F/" + queue,
            auth=HTTP_AUTH,
            timeout=3,
        )
        server_queue = yield server_queue.json()
        assert server_queue["consumers"] == 0
        assert server_queue["messages"] == 1
    finally:
        yield consumers[0].cancel()
Ejemplo n.º 20
0
def test_twisted_consume_halt_consumer():
    """
    Assert raising HaltConsumer works with :func:`fedora_messaging.api.twisted_consume`
    API.
    """
    queue = str(uuid.uuid4())
    queues = {queue: {"auto_delete": False, "arguments": {"x-expires": 60 * 1000}}}
    bindings = [{"queue": queue, "exchange": "amq.topic", "routing_keys": ["#"]}]
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )
    expected_headers = {
        u"fedora_messaging_severity": 20,
        u"fedora_messaging_schema": u"base.message",
        u"niceness": u"very",
    }
    messages_received = []

    def callback(message):
        """Count to 3 and quit."""
        messages_received.append(message)
        if len(messages_received) == 3:
            raise exceptions.HaltConsumer()

    consumers = yield api.twisted_consume(callback, bindings, queues)

    # Assert the server reports a consumer
    server_queue = yield get_queue(queue)
    assert server_queue["consumers"] == 1

    for _ in range(0, 3):
        api.publish(msg, "amq.topic")

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
    except exceptions.HaltConsumer as e:
        assert len(messages_received) == 3
        assert e.exit_code == 0
        for m in messages_received:
            assert u"nice.message" == m.topic
            assert {u"encouragement": u"You're doing great!"} == m.body
            assert "sent-at" in m._headers
            del m._headers["sent-at"]
            assert expected_headers == m._headers
        server_queue = yield get_queue(queue)
        assert server_queue["consumers"] == 0
    except (defer.TimeoutError, defer.CancelledError):
        yield consumers[0].cancel()
        pytest.fail("Timeout reached without consumer halting!")
Ejemplo n.º 21
0
def test_twisted_stop_service():
    """
    Assert stopping the service, which happens when the reactor shuts down, waits
    for consumers to finish processing and then cancels them before closing the connection.
    """
    queue = str(uuid.uuid4())
    queues = {
        queue: {
            "auto_delete": False,
            "arguments": {
                "x-expires": 60 * 1000
            }
        }
    }
    bindings = [{
        "queue": queue,
        "exchange": "amq.topic",
        "routing_keys": ["#"]
    }]
    message_received, message_processed = defer.Deferred(), defer.Deferred()

    def callback(message):
        """Callback when the message is received, introduce a delay, then callback again."""
        reactor.callFromThread(message_received.callback, None)
        time.sleep(5)
        reactor.callFromThread(message_processed.callback, None)

    consumers = yield api.twisted_consume(callback, bindings, queues)
    api.publish(message.Message(), "amq.topic")

    _add_timeout(consumers[0].result, 10)
    _add_timeout(message_received, 10)
    try:
        yield message_received
    except (defer.TimeoutError, defer.CancelledError):
        yield consumers[0].cancel()
        pytest.fail("Timeout reached without consumer receiving message!")

    assert not message_processed.called
    deferred_stop = api._twisted_service.stopService()

    _add_timeout(message_processed, 10)
    try:
        yield message_processed
        # The request to stop should wait on the message to be processed
        assert deferred_stop.called is False
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer processing message")
    yield deferred_stop
def watch_finished_messages(cond, registered, request_type, request_id,
                            environment):
    def callback(message):
        if 'request_id' not in message.body or message.body[
                'request_id'] != request_id:
            return
        with cond:
            global request_state
            request_state = message.body
            cond.notify()

    queue = str(uuid.uuid4())

    def registered_cb(consumers):
        for consumer in consumers:
            if consumer.queue == queue:
                registered.set()
                break

    def error_cb(failure):
        print(f"Consumer hit failure {failure}")
        reactor.stop()  # pylint: disable=E1101

    # use the public config for this; see related comment in send_message()
    conf.load_config(FEDORA_MESSAGING_PUBLIC_CONF[environment])

    bindings = [{
        'exchange':
        'amq.topic',
        'queue':
        queue,
        'routing_keys':
        [get_request_finished_topic(request_type, environment)]
    }]
    queues = {
        queue: {
            "durable": False,
            "auto_delete": True,
            "exclusive": True,
            "arguments": {}
        }
    }

    consumers = twisted_consume(callback, bindings=bindings, queues=queues)
    consumers.addCallback(registered_cb)
    consumers.addErrback(error_cb)
    reactor.run(installSignalHandlers=False)  # pylint: disable=E1101
Ejemplo n.º 23
0
def test_no_write_permissions(admin_user, queue_and_binding):
    """Assert the call to twisted_consume errbacks on write permissions errors."""
    queues, bindings = queue_and_binding
    url = "{base}permissions/%2F/{user}".format(base=HTTP_API, user=admin_user)
    body = {"configure": ".*", "write": "", "read": ".*"}
    resp = yield treq.put(url, json=body, auth=HTTP_AUTH, timeout=3)
    assert resp.code == 204

    amqp_url = "amqp://{user}:guest@localhost:5672/%2F".format(user=admin_user)
    try:
        with mock.patch.dict(config.conf, {"amqp_url": amqp_url}):
            yield api.twisted_consume(lambda x: x, bindings, queues)
        pytest.fail("Call failed to raise an exception")
    except exceptions.BadDeclaration as e:
        assert e.reason.args[0] == 403
        assert e.reason.args[1] == (
            "ACCESS_REFUSED - access to queue '{}' in vhost '/' refused for user"
            " '{}'".format(list(queues.keys())[0], admin_user))
Ejemplo n.º 24
0
def test_twisted_consume_invalid_message(queue_and_binding):
    """Assert messages that fail validation are nacked."""
    queues, bindings = queue_and_binding
    bindings[0]["routing_keys"] = ["test_twisted_invalid_message"]

    consumers = yield api.twisted_consume(
        lambda x: pytest.fail("Message should be nacked"), bindings, queues)

    url = HTTP_API + "exchanges/%2F/amq.topic/publish"
    body = {
        "properties": {},
        "routing_key": "test_twisted_invalid_message",
        "payload": "not json",
        "payload_encoding": "string",
    }
    response = yield treq.post(url, json=body, auth=HTTP_AUTH, timeout=3)
    response = yield response.json()
    assert response["routed"] is True
    yield consumers[0].cancel()
Ejemplo n.º 25
0
def test_twisted_consume_serverside_cancel(queue_and_binding):
    """
    Assert the consumer halts and ``consumer.result`` errbacks when the server
    explicitly cancels the consumer (by deleting the queue).
    """
    queues, bindings = queue_and_binding

    consumers = yield api.twisted_consume(lambda x: x, bindings, queues)

    # Delete the queue and assert the consumer errbacks
    url = "{base}queues/%2F/{queue}".format(base=HTTP_API, queue=list(queues.keys())[0])
    yield treq.delete(url, auth=HTTP_AUTH, timeout=3)

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
        pytest.fail("Consumer did not errback!")
    except exceptions.ConsumerCanceled:
        pass
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer calling its errback!")
Ejemplo n.º 26
0
def test_twisted_consume_drop_message(queue_and_binding):
    """Assert raising Drop causes the message to be dropped, but processing continues."""
    queues, bindings = queue_and_binding
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )
    dropped_messages = []

    def callback(message):
        """Drop 1 message and then halt on the second message."""
        dropped_messages.append(message)
        if len(dropped_messages) == 2:
            raise exceptions.HaltConsumer()
        raise exceptions.Drop()

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(callback, bindings, queues)
    yield threads.deferToThread(api.publish, msg, "amq.topic")
    yield threads.deferToThread(api.publish, msg, "amq.topic")

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
    except exceptions.HaltConsumer:
        # Assert both messages are delivered, no messages are un-acked, and only one
        # message got a positive acknowledgment.
        server_queue = yield get_queue(queues)
        assert server_queue["consumers"] == 0
        assert server_queue["messages"] == 0
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer halting!")
    finally:
        yield consumers[0].cancel()
Ejemplo n.º 27
0
def test_twisted_consume_general_exception(queue_and_binding):
    """
    Assert if the callback raises an unhandled exception, it is passed on to the
    consumer.result and the message is re-queued.
    """
    queues, bindings = queue_and_binding
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )

    def callback(message):
        """An *exceptionally* useless callback"""
        raise Exception("Oh the huge manatee")

    # Assert that the number of consumers we think we started is the number the
    # server things we started. This will fail if other tests don't clean up properly.
    # If it becomes problematic perhaps each test should have a vhost.
    consumers = yield api.twisted_consume(callback, bindings, queues)
    yield threads.deferToThread(api.publish, msg, "amq.topic")

    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
        pytest.fail("Expected an exception to be raised.")
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer halting!")
    except Exception as e:
        # Assert the message was delivered and re-queued when the consumer crashed.
        assert e.args[0] == "Oh the huge manatee"
        server_queue = yield get_queue(queues)
        assert server_queue["consumers"] == 0
        assert server_queue["messages"] == 1
    finally:
        yield consumers[0].cancel()
Ejemplo n.º 28
0
def test_twisted_stop_service(queue_and_binding):
    """
    Assert stopping the service, which happens when the reactor shuts down, waits
    for consumers to finish processing and then cancels them before closing the connection.
    """
    queues, bindings = queue_and_binding
    message_received, message_processed = defer.Deferred(), defer.Deferred()

    def callback(message):
        """Callback when the message is received, introduce a delay, then callback again."""
        reactor.callFromThread(message_received.callback, None)
        time.sleep(5)
        reactor.callFromThread(message_processed.callback, None)

    consumers = yield api.twisted_consume(callback, bindings, queues)
    yield threads.deferToThread(api.publish, message.Message(), "amq.topic")

    _add_timeout(consumers[0].result, 10)
    _add_timeout(message_received, 10)
    try:
        yield message_received
    except (defer.TimeoutError, defer.CancelledError):
        yield consumers[0].cancel()
        pytest.fail("Timeout reached without consumer receiving message!")

    assert not message_processed.called
    deferred_stop = api._twisted_service.stopService()

    _add_timeout(message_processed, 10)
    try:
        yield message_processed
        # The request to stop should wait on the message to be processed
        assert deferred_stop.called is False
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without consumer processing message")
    yield deferred_stop
Ejemplo n.º 29
0
def test_twisted_consume_connection_reset(queue_and_binding):
    """
    Assert consuming works across connections and handles connection resets.

    This test sets up a queue, publishes 2 messages to itself, then kills all active
    connections on the broker. It then sends a third message and asserts the consumer
    gets it.
    """
    queues, bindings = queue_and_binding
    msg = message.Message(
        topic=u"nice.message",
        headers={u"niceness": u"very"},
        body={u"encouragement": u"You're doing great!"},
    )
    messages_received = []
    two_received = defer.Deferred()  # Fired by the callback on 2 messages

    def callback(message):
        """Count to, 2, fire a deferred, then count to 3 and quit."""
        messages_received.append(message)
        if len(messages_received) == 2:
            two_received.callback(None)
        if len(messages_received) == 3:
            raise exceptions.HaltConsumer()

    consumers = yield api.twisted_consume(callback, bindings, queues)

    # Wait for two messages to get through, kill the connection, and then send
    # the third and wait for the consumer to finish
    yield threads.deferToThread(api.publish, msg, "amq.topic")
    yield threads.deferToThread(api.publish, msg, "amq.topic")
    _add_timeout(two_received, 10)
    try:
        yield two_received
    except (defer.TimeoutError, defer.CancelledError):
        pytest.fail("Timeout reached without receiving first two messages")

    for _ in range(10):
        conns = yield task.deferLater(reactor,
                                      1,
                                      treq.get,
                                      HTTP_API + "connections",
                                      auth=HTTP_AUTH,
                                      timeout=3)
        conns = yield conns.json()
        this_conn = [
            c for c in conns
            if "app" in c["client_properties"] and c["client_properties"]
            ["app"] == "test_twisted_consume_connection_reset"
        ]
        if this_conn:
            cname = six.moves.urllib.parse.quote(this_conn[0]["name"])
            yield treq.delete(HTTP_API + "connections/" + cname,
                              auth=HTTP_AUTH,
                              timeout=3)
            break
    else:
        pytest.fail("Unable to find and kill connection!")

    # The consumer should receive this third message after restarting its connection
    # and then it should exit gracefully.
    yield threads.deferToThread(api.publish, msg, "amq.topic")
    _add_timeout(consumers[0].result, 10)
    try:
        yield consumers[0].result
    except exceptions.HaltConsumer:
        assert len(messages_received) == 3
    except (defer.TimeoutError, defer.CancelledError):
        yield consumers[0].cancel()
        pytest.fail("Timeout reached without consumer halting!")