Example #1
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()
Example #2
0
    def test_proper_message_multiple(self):
        """Assert proper json is returned"""
        test_topic = "test topic"
        test_body = {"test_key": "test_value"}
        test_queue = "test queue"
        test_id = "test id"
        test_headers = {
            "fedora_messaging_schema": "base.message",
            "fedora_messaging_severity": message.WARNING,
        }
        test_properties = pika.BasicProperties(
            content_type="application/json",
            content_encoding="utf-8",
            delivery_mode=2,
            headers=test_headers,
            message_id=test_id,
        )
        test_msg = message.Message(
            body=test_body, topic=test_topic, properties=test_properties
        )
        test_msg2 = message.Message(
            body=test_body, topic=test_topic, properties=test_properties
        )
        test_msg.queue = test_queue
        test_msg2.queue = test_queue
        expected_json = (
            '{"body": {"test_key": "test_value"}, "headers": {"fedora_messaging_schema": '
            '"base.message", "fedora_messaging_severity": 30}, "id": "test id", "queue": '
            '"test queue", "topic": "test topic"}\n'
            '{"body": {"test_key": "test_value"}, "headers": {"fedora_messaging_schema": '
            '"base.message", "fedora_messaging_severity": 30}, "id": "test id", "queue": '
            '"test queue", "topic": "test topic"}\n'
        )

        self.assertEqual(expected_json, message.dumps([test_msg, test_msg2]))
Example #3
0
    def test_equality_different_sent_at(self):
        """Assert the "sent-at" key is not included in the equality check."""
        m1 = message.Message(topic="test.topic", body={"my": "key"})
        m2 = message.Message(topic="test.topic", body={"my": "key"})
        m2._headers["sent-at"] = datetime.datetime(1970, 1, 2).isoformat()

        self.assertEqual(m1, m2)
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()
Example #5
0
 def delayed_publish():
     """Publish, break the channel, and publish again."""
     yield threads.deferToThread(api.publish, message.Message(),
                                 "amq.topic")
     protocol = yield api._twisted_service._service.factory.when_connected()
     yield protocol._publish_channel.close()
     yield threads.deferToThread(api.publish, message.Message(),
                                 "amq.topic")
 def test_equality(self):
     """
     Assert two messages of the same class with the same topic, headers, and
     body are equivalent.
     """
     self.assertEqual(
         message.Message(topic='test.topic', body={'my': 'key'}),
         message.Message(topic='test.topic', body={'my': 'key'}))
Example #7
0
 def test_equality(self):
     """
     Assert two messages of the same class with the same topic, headers, and
     body are equivalent.
     """
     self.assertEqual(
         message.Message(topic="test.topic", body={"my": "key"}),
         message.Message(topic="test.topic", body={"my": "key"}),
     )
Example #8
0
    def test_missing_severity(self):
        """Assert the default severity is INFO if it's not in the headers."""
        msg = message.Message(severity=message.ERROR)
        del msg._headers["fedora_messaging_severity"]

        recv_msg = message.get_message("", msg._properties, b"{}")
        self.assertEqual(recv_msg.severity, message.INFO)
Example #9
0
 def test_repr(self):
     """Assert the message produces a valid representation of the message."""
     msg = message.Message(topic="test.topic", body={"my": "key"})
     expected = "Message(id='{}', topic='test.topic', body={{'my': 'key'}})".format(
         msg.id
     )
     self.assertEqual(expected, repr(msg))
Example #10
0
 def test_signed_implicit_cert(self):
     """Assert signing certificate is properly autodetected."""
     zmq_bridge = bridges.AmqpToZmq()
     msg = message.Message(topic="my.topic", body={"my": "message"})
     hostname = socket.gethostname().split(".", 1)[0]
     base_conf = {"sign_messages": True, "ssldir": FIXTURES_DIR}
     sign_configs = [
         {
             "name": "fedmsg",
             "certnames": {
                 "fedmsg": "fedmsg"
             }
         },
         {
             "cert_prefix": "fedmsg",
             "certnames": {
                 "fedmsg.{}".format(hostname): "fedmsg"
             },
         },
     ]
     for sign_config in sign_configs:
         conf = base_conf.copy()
         conf.update(sign_config)
         with mock.patch.dict(
                 "fedmsg_migration_tools.bridges.fedmsg_config.conf", conf):
             with mock.patch(
                     "fedmsg_migration_tools.bridges.fedmsg.crypto.sign"
             ) as mock_sign:
                 mock_sign.side_effect = lambda *a, **kw: a[0]
                 zmq_bridge(msg)
         sign_call_kw = mock_sign.call_args_list[-1][1]
         self.assertIn("certname", sign_call_kw)
         self.assertEqual(sign_call_kw["certname"], "fedmsg")
def test_drop_handled():
    """Assert raising Drop in a consumer works and messages are not re-delivered"""
    queue = str(uuid.uuid4())
    messages = []
    serv = service.FedoraMessagingService(amqp_url="amqp://")
    serv.startService()
    client = yield serv.getFactory().whenConnected()
    queues = [
        {"queue": queue, "auto_delete": True, "arguments": {"x-expires": 60 * 1000}}
    ]
    yield client.declare_queues(queues)
    yield client.bind_queues(
        [{"queue": queue, "exchange": "amq.topic", "routing_key": "#"}]
    )

    def callback(message):
        messages.append(message)
        raise exceptions.Drop()

    yield client.consume(callback, queue)
    assert len(client._consumers) == 1

    yield client.publish(message.Message(), "amq.topic")
    yield task.deferLater(reactor, 3.0, lambda: True)  # Just wait a few seconds

    assert len(messages) == 1
    assert len(client._consumers) == 1
    yield client.cancel(queue)
    serv.stopService()
Example #12
0
    def test_signed(self):
        """Assert messages are signed if fedmsg is configured for signatures."""
        year = datetime.datetime.utcnow().year
        zmq_bridge = bridges.AmqpToZmq()
        msg = message.Message(topic="my.topic", body={"my": "message"})
        expected = {
            "topic": "my.topic",
            "msg": {
                "my": "message"
            },
            "timestamp": 101,
            "msg_id": "{}-{}".format(year, msg.id),
            "i": 1,
            "username": "******",
            "crypto": "x509",
        }
        conf = {
            "sign_messages": True,
            "ssldir": FIXTURES_DIR,
            "certname": "fedmsg"
        }

        with mock.patch.dict(
                "fedmsg_migration_tools.bridges.fedmsg_config.conf", conf):
            zmq_bridge(msg)

        body = json.loads(
            zmq_bridge.pub_socket.send_multipart.call_args_list[0][0][0]
            [1].decode("utf-8"))
        self.assertIn("signature", body)
        self.assertIn("certificate", body)
        del body["signature"]
        del body["certificate"]

        self.assertEqual(body, expected)
Example #13
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!")
Example #14
0
    def test_msg_becomes_body(self):
        """Assert the "msg" key of a fedmsg is the fedora-messaging body."""
        expected = message.Message(body={"hello": "world"}, topic="hi")
        zmq_message = b'{"msg": {"hello": "world"}, "msg_id": "abc123"}'

        with fml_testing.mock_sends(expected):
            bridges._convert_and_maybe_publish(b"hi", zmq_message, "amq.topic")
Example #15
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()
Example #16
0
def test_consume_halt_with_exitcode(callback, exit_code, msg, queue):
    """Assert user execution halt with reason and exit_code is reported."""
    args = [
        "fedora-messaging",
        "--conf={}".format(CLI_CONF),
        "consume",
        "--callback=fedora_messaging.tests.integration.test_cli:{}".format(
            callback),
        "--queue-name={}".format(queue),
        "--exchange=amq.topic",
        "--routing-key=#",
    ]

    process = subprocess.Popen(args,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)
    time.sleep(5)

    api.publish(message.Message())
    for _ in range(5):
        time.sleep(1)
        if process.poll() is not None:
            break
    else:
        process.kill()
        pytest.fail("Process never stopped!: {}".format(process.stdout.read()))

    assert process.returncode == exit_code
    assert msg in process.stdout.read()
Example #17
0
    def test_blank_headers(self):
        """Assert ZMQ messages with blank headers still get the defaults."""
        expected = message.Message(body={"hello": "world"}, topic="hi")
        zmq_message = b'{"headers": {}, "msg": {"hello": "world"}, "msg_id": "abc123"}'

        with fml_testing.mock_sends(expected):
            bridges._convert_and_maybe_publish(b"hi", zmq_message, "amq.topic")
Example #18
0
    def test_invalid_severity(self):
        """Assert the invalid severity fails validation."""
        msg = message.Message()
        msg._headers["fedora_messaging_severity"] = 42

        self.assertRaises(exceptions.ValidationError, message.get_message, "",
                          msg._properties, b"{}")
Example #19
0
    def test_dump(self):
        """Assert proper dict is generated on Message._dump."""
        test_topic = "test topic"
        test_body = {"test_key": "test_value"}
        test_queue = "test queue"
        test_id = "test id"
        test_headers = {
            "fedora_messaging_schema": "base.message",
            "fedora_messaging_severity": message.WARNING,
        }
        test_properties = pika.BasicProperties(
            content_type="application/json",
            content_encoding="utf-8",
            delivery_mode=2,
            headers=test_headers,
            message_id=test_id,
        )
        expected_dict = {
            "topic": test_topic,
            "headers": test_headers,
            "id": test_id,
            "body": test_body,
            "queue": test_queue,
        }
        test_msg = message.Message(body=test_body,
                                   topic=test_topic,
                                   properties=test_properties)
        test_msg.queue = test_queue

        test_msg_dict = test_msg._dump()
        self.assertEqual(expected_dict, test_msg_dict)
Example #20
0
 def test_headers(self):
     msg = message.Message(headers={"foo": "bar"})
     self.assertIn("foo", msg._properties.headers)
     self.assertEqual(msg._properties.headers["foo"], "bar")
     # The fedora_messaging_schema key must also be added when headers are given.
     self.assertEqual(msg._properties.headers["fedora_messaging_schema"],
                      "base.message")
Example #21
0
def test_unhandled_exception_cancels_consumer():
    """Assert any unhandled Exception results in the consumer being canceled."""
    queue = str(uuid.uuid4())
    queues = [{
        "queue": queue,
        "auto_delete": True,
        "durable": False,
        "exclusive": False,
        "arguments": {
            "x-expires": 60 * 1000
        },
    }]
    serv = service.FedoraMessagingService(amqp_url="amqp://")

    serv.startService()
    client = yield serv.getFactory().whenConnected()
    yield client.declare_queues(queues)
    yield client.bind_queues([{
        "queue": queue,
        "exchange": "amq.topic",
        "routing_key": "#"
    }])

    def callback(message):
        raise Exception("Panic!")

    yield client.consume(callback, queue)
    assert len(client._consumers) == 1

    yield client.publish(message.Message(), "amq.topic")
    yield task.deferLater(reactor, 3.0, lambda: True)
    assert len(client._consumers) == 0
    yield serv.stopService()
    def test_pub_sub_default_settings(self):
        """
        Assert publishing and subscribing works with the default configuration.

        This should work because the publisher uses the 'amq.topic' exchange by
        default and the consumer also uses the 'amq.topic' exchange with its
        auto-named queue and a default subscription key of '#'.
        """

        # Consumer setup
        def counting_callback(message, storage=defaultdict(int)):
            storage[message.topic] += 1
            if storage[message.topic] == 3:
                raise exceptions.HaltConsumer()

        consumer_process = multiprocessing.Process(
            target=api.consume, args=(counting_callback,))
        msg = message.Message(topic=u'nice.message', headers={u'niceness': u'very'},
                              body={u'encouragement': u"You're doing great!"})

        consumer_process.start()
        # Allow the consumer time to create the queues and bindings
        time.sleep(5)

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

        consumer_process.join(timeout=30)
        self.assertEqual(0, consumer_process.exitcode)
 def test_repr(self):
     """Assert the message produces a valid representation of the message."""
     Message = message.Message  # noqa
     expected = "Message(body={'my': 'key'}, headers={}, topic='test.topic')"
     msg = message.Message(topic='test.topic', body={'my': 'key'})
     self.assertEqual(expected, repr(msg))
     self.assertEqual(msg, eval(repr(msg)))
Example #24
0
    def test_missing_headers(self):
        """Assert missing headers results in a default message."""
        msg = message.Message()
        msg._headers = None

        received_msg = message.get_message(msg._encoded_routing_key,
                                           msg._properties, msg._encoded_body)
        self.assertIsInstance(received_msg, message.Message)
Example #25
0
 def test_publish_topic_prefix(self):
     # Check that the topic prefix is correctly prepended to outgoing messages.
     with mock.patch.dict(config.conf, {"topic_prefix": "prefix"}):
         msg = message.Message(topic="test.topic")
         self.publisher.publish(msg)
     self.publisher_channel_publish.assert_called_once()
     publish_call = self.publisher_channel_publish.call_args_list[0][1]
     self.assertEqual(publish_call["routing_key"], b"prefix.test.topic")
Example #26
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()
Example #27
0
 def test_str(self):
     """Assert calling str on a message produces a human-readable result."""
     msg = message.Message(topic='test.topic', body={'my': 'key'})
     expected_headers = json.dumps(msg._headers, sort_keys=True, indent=4)
     expected = ('Id: {}\nTopic: test.topic\n'
                 'Headers: {}'
                 '\nBody: {{\n    "my": "key"\n}}').format(
                     msg.id, expected_headers)
     self.assertEqual(expected, str(msg))
Example #28
0
 def test_properties_default(self):
     msg = message.Message()
     self.assertEqual(msg._properties.content_type, "application/json")
     self.assertEqual(msg._properties.content_encoding, "utf-8")
     self.assertEqual(msg._properties.delivery_mode, 2)
     self.assertIn("sent-at", msg._properties.headers)
     self.assertIn("fedora_messaging_schema", msg._properties.headers)
     self.assertEqual(msg._properties.headers["fedora_messaging_schema"],
                      "base.message")
Example #29
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()
Example #30
0
    def test_sent_at(self):
        """Assert a timestamp is inserted and contains explicit timezone information."""
        mock_datetime = mock.Mock()
        mock_datetime.utcnow.return_value = datetime.datetime(1970, 1, 1, 0, 0, 0)

        with mock.patch("datetime.datetime", mock_datetime):
            msg = message.Message()

        self.assertEqual("1970-01-01T00:00:00+00:00", msg._headers["sent-at"])