Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ nosetests.xml
.project
.pydevproject
.idea
.pytest_cache
/htmlcov
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this come from? We don't have any HTML in here that needs to be covered, right? :P

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you run pytest --cov (with some extra arguments), you'll get a directory htmlcov which shows you which parts of your code hasn't been ran by tests. I should add this to the readme.

23 changes: 23 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
branches:
only:
- master

language: python

python:
- '2.7'
- '3.4'
- '3.5'
- '3.6'

install:
# install setup.py's dependencies and dev requirements
- pip install -e . -r requirements-dev.txt

script:
- pytest
- prospector

sudo: false

cache: pip
39 changes: 22 additions & 17 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,38 @@ A Python module to facilitate listening for and acting on AMQP events.
Usage
-----

Example:
See `example_listener.py <https://github.com/ByteInternet/amqpconsumer/blob/master/example_listener.py>`_ for a code snippet.

.. code-block:: python
Development
-----------

import logging
We recommend setting up a virtual environment for the project. All the following commands assume you've activated the virtual environment.

from amqpconsumer.events import EventConsumer
Make sure both module and development dependencies are installed:

.. code-block:: bash

def handler(event):
print "Got event:", event
pip install -e . -r requirements-dev.txt

Run tests:

def main():
logging.basicConfig(level=logging.INFO)
consumer = EventConsumer('amqp://guest:guest@localhost:5672/%2f',
'testqueue',
handler)
try:
consumer.run()
except KeyboardInterrupt:
consumer.stop()
.. code-block:: bash

pytest

if __name__ == '__main__':
main()
If you want to generate code coverage statistics:

.. code-block:: bash

pytest --cov amqpconsumer --cov-report html

Then open `htmlcov/index.html` in your favorite web browser.

Run linting and static analysis:

.. code-block:: bash

prospector

=====
About
Expand Down
4 changes: 2 additions & 2 deletions amqpconsumer/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class EventConsumer(object):
# on_bindok (skipped if no exchange is provided) ->
# start_consuming

def __init__(self, amqp_url, queue, handler, exchange=None, exchange_type=None, routing_key=None):
def __init__(self, amqp_url, queue, handler, exchange=None, exchange_type=None, routing_key=None): # pylint: disable=too-many-arguments
"""Create a new instance of the consumer class, passing in the URL
of RabbitMQ, the queue to listen to, and a callable handler that
handles the events.
Expand Down Expand Up @@ -187,7 +187,7 @@ def setup_queue(self):

When completed, the on_queue_declareok method will be invoked by pika.
"""
logger.debug("Declaring queue %s" % self._queue)
logger.debug('Declaring queue %s', self._queue)
self._channel.queue_declare(self.on_queue_declareok, self._queue, durable=True)

def on_queue_declareok(self, _):
Expand Down
3 changes: 2 additions & 1 deletion example_listener.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#!/usr/bin/env python
from __future__ import print_function
import logging

from amqpconsumer.events import EventConsumer


def handler(event):
print "Got event:", event
print("Got event:", event)


def main():
Expand Down
4 changes: 4 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pytest
pytest-cov
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why pytest specifically? All other repo's use nosetests I think. I think we should stick with the same requirements in general.

prospector
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's prospector?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A tool that combines pylint, pyflakes and pycodestyle. Used by https://landscape.io/

mock
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
name='amqpconsumer',
version='1.7',
description='AMQP event listener',
long_description=open('README.rst').read(),
url='https://github.com/ByteInternet/amqpconsumer',
author='Byte B.V.',
author_email='[email protected]',
Expand Down
189 changes: 189 additions & 0 deletions tests/unit_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import mock
from amqpconsumer.events import EventConsumer


def get_consumer(
amqp_url='amqps://fake-url',
queue='fake-queue',
handler=None,
exchange=None,
exchange_type=None,
routing_key=None,
):
consumer = EventConsumer(
amqp_url=amqp_url,
queue=queue,
handler=handler,
exchange=exchange,
exchange_type=exchange_type,
routing_key=routing_key,
)
consumer._connection = mock.MagicMock()
consumer._channel = mock.Mock()
return consumer


def get_consumer_with_exchange():
mock_exchange = mock.Mock()
consumer = get_consumer(exchange=mock_exchange, exchange_type='fake-type',
routing_key='fake-key')
return consumer


def test_connect_calls_pika_selectconnection():
consumer = get_consumer()
with mock.patch('pika.SelectConnection') as mock_select:
consumer.connect()

mock_select.assert_called_once()


def test_on_connection_open():
consumer = get_consumer()
consumer.on_connection_open('??')

consumer._connection.add_on_close_callback.assert_called_once_with(consumer.on_connection_closed)
consumer._connection.channel.assert_called_once_with(on_open_callback=consumer.on_channel_open)


def test_on_connection_closed_reconnects():
consumer = get_consumer()
consumer.on_connection_closed('??', 'fake-code', 'fake-text')

consumer._connection.add_timeout.assert_called_once_with(5, consumer.reconnect)


def test_on_connection_closed_stops_when_closing():
consumer = get_consumer()
consumer._closing = True
consumer.on_connection_closed('??', 'fake-code', 'fake-text')

consumer._connection.ioloop.stop.assert_called_once_with()


def test_open_channel_calls_connection_channel():
consumer = get_consumer()
consumer.open_channel()

consumer._connection.channel.assert_called_once_with(on_open_callback=consumer.on_channel_open)


def test_on_channel_open():
channel = mock.Mock()
consumer = get_consumer()
consumer.on_channel_open(channel)

channel.add_on_close_callback.assert_called_once_with(consumer.on_channel_closed)
channel.basic_qos.assert_called_once_with(prefetch_count=1, callback=consumer.on_qos_set)


def test_on_qos_set_when_exchange_is_none():
consumer = get_consumer(exchange=None)
consumer.on_qos_set('??')

consumer._channel.queue_declare.assert_called_once_with(
consumer.on_queue_declareok, consumer._queue, durable=True
)


def test_on_exchange_declareok():
consumer = get_consumer_with_exchange()
consumer.on_exchange_declareok('??')

consumer._channel.queue_declare.assert_called_once_with(
consumer.on_queue_declareok, consumer._queue, durable=True
)


def test_on_qos_set_when_exchange_is_something():
consumer = get_consumer_with_exchange()
consumer.on_qos_set('??')

consumer._channel.exchange_declare.assert_called_once_with(
consumer.on_exchange_declareok,
consumer._exchange,
consumer._exchange_type,
durable=True,
)


def test_on_queue_declareok_when_exchange_is_something():
consumer = get_consumer_with_exchange()
consumer.on_queue_declareok('??')

consumer._channel.queue_bind(
consumer.on_bindok, consumer._queue, consumer._exchange, consumer._routing_key
)


def test_on_queue_declareok_when_exchange_is_none():
consumer = get_consumer()
consumer._channel.basic_consume.return_value = 'fake-tag'
consumer.on_queue_declareok('??')

consumer._channel.add_on_cancel_callback.assert_called_once_with(consumer.on_consumer_cancelled)
consumer._channel.basic_consume.assert_called_once_with(consumer.on_message, consumer._queue)
assert consumer._consumer_tag == 'fake-tag'


def test_on_bindok():
consumer = get_consumer()
consumer._channel.basic_consume.return_value = 'fake-tag'
consumer.on_bindok('??')

consumer._channel.add_on_cancel_callback.assert_called_once_with(consumer.on_consumer_cancelled)
consumer._channel.basic_consume.assert_called_once_with(consumer.on_message, consumer._queue)
assert consumer._consumer_tag == 'fake-tag'


def test_on_channel_closed():
consumer = get_consumer()
consumer.on_channel_closed('fake-channel', 'fake-code', 'fake-text')

consumer._connection.close.assert_called_once()


def test_on_consumer_cancelled():
consumer = get_consumer()
consumer.on_consumer_cancelled('fake-frame')

consumer._channel.close.assert_called_once()


def test_on_message():
mock_handler = mock.Mock()
consumer = get_consumer(handler=mock_handler)
body = b'{"foo":"bar"}'
mock_deliver = mock.Mock()
mock_properties = mock.Mock()
consumer.on_message('??', mock_deliver, mock_properties, body)

mock_handler.assert_called_once_with({'foo': 'bar'})
consumer._channel.basic_ack.assert_called_once_with(mock_deliver.delivery_tag)


def test_run():
consumer = get_consumer()
with mock.patch('pika.SelectConnection') as mock_select:
consumer.run()

mock_select.assert_called_once()
consumer._connection.ioloop.start.assert_called_once()


def test_stop():
consumer = get_consumer()
consumer.stop()

assert consumer._closing
consumer._channel.basic_cancel.assert_called_once_with(
consumer.on_cancelok, consumer._consumer_tag
)
consumer._connection.ioloop.start.assert_called_once()


def test_on_cancelok():
consumer = get_consumer()
consumer.on_cancelok('??')

consumer._channel.close.assert_called_once_with()