diff --git a/.gitignore b/.gitignore index d6c621b..ca8b277 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ docs/build/ .tox/ build/ share/ + +# local live test settings +livetest_settings.py diff --git a/.travis.yml b/.travis.yml index 2ff3e6f..2819c50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,5 +6,5 @@ install: - pip install --use-mirrors imapclient unittest2 flake8 mock - python setup.py develop script: - - unit2 discover + - unit2 discover mailbot.tests - flake8 mailbot diff --git a/Makefile b/Makefile index 6a107aa..69e7a4e 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,10 @@ test: bin/python bin/pip install tox bin/tox +livetest: bin/python + bin/pip install tox + bin/tox -e py26-live,py27-live + docs: bin/pip install sphinx SPHINXBUILD=../bin/sphinx-build $(MAKE) -C docs html $^ diff --git a/README.rst b/README.rst index db01d31..a5998a1 100644 --- a/README.rst +++ b/README.rst @@ -32,3 +32,32 @@ on all the supported python and Django versions: :: make test + +There's also a live test suite, that you may run using the following command: + +:: + + make livetest + +Please note that to run live tests, you need to create a +``livetest_settings.py`` file with the following content: + +:: + + # mandatory + HOST = 'your host here' + USERNAME = 'your username here' + PASSWORD = 'your password here' + + # optional + # check http://imapclient.readthedocs.org/en/latest/#imapclient.IMAPClient) + PORT = 143 # port number, usually 143 or 993 if ssl is enabled + USE_UID = True + SSL = False + STREAM = False + +For convenience, you can copy the provided sample, and modify it: + +:: + + $ cp livetest_settings.py.sample livetest_settings.py diff --git a/docs/source/index.rst b/docs/source/index.rst index df77169..8de86fc 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -42,6 +42,28 @@ From github:: pip install -e http://github.com/novagile/mailbot/ +Usage +----- + +You first need to instantiate MailBot, giving it informations to connect to +your IMAP server. MailBot uses `IMAPClient +`_, and as such +takes the same parameters. + +You also need to provide the username and password. Here's an simple example: + +.. code-block:: python + + from mailbot import MailBot + + + mailbot = MailBot('imap.myserver.com', 'username', 'password') + + +Once that's done, you may register the callbacks and rules that will be tested +on each mail received when calling ``mailbot.process_messages``. + + Registering callbacks --------------------- diff --git a/livetest_settings.py.sample b/livetest_settings.py.sample new file mode 100644 index 0000000..dd72945 --- /dev/null +++ b/livetest_settings.py.sample @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- + +"""Settings used by the live tests to connect and login to an IMAP server.""" + +# mandatory +HOST = 'your host here' +USERNAME = 'your username here' +PASSWORD = 'your password here' + +# optional +# check http://imapclient.readthedocs.org/en/latest/#imapclient.IMAPClient +PORT = None +USE_UID = True +SSL = False +STREAM = False diff --git a/mailbot/livetests/__init__.py b/mailbot/livetests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mailbot/livetests/test_mail_received.py b/mailbot/livetests/test_mail_received.py new file mode 100644 index 0000000..5e068ce --- /dev/null +++ b/mailbot/livetests/test_mail_received.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- + +from email import message_from_string +from imapclient import FLAGGED + +from .. import register, MailBot +from ..tests import MailBotTestCase + +try: + import livetest_settings as settings +except ImportError: + raise ImportError('Please create a livetest_settings.py file following ' + 'the example given here in the README ') + + +class MailReceivedTest(MailBotTestCase): + + def setUp(self): + super(MailReceivedTest, self).setUp() + self.mb = MailBot(settings.HOST, settings.USERNAME, settings.PASSWORD, + port=settings.PORT, use_uid=settings.USE_UID, + ssl=settings.SSL, stream=settings.STREAM) + self.home_folder = '__mailbot' + self._delete_folder() + self.mb.client.create_folder(self.home_folder) + self.mb.client.select_folder(self.home_folder) + + def tearDown(self): + super(MailReceivedTest, self).tearDown() + self._delete_folder() + + def _delete_folder(self): + """Delete an IMAP folder, if it exists.""" + if self.mb.client.folder_exists(self.home_folder): + self.mb.client.delete_folder(self.home_folder) + + def test_get_message_ids(self): + self.assertEqual(self.mb.get_message_ids(), []) + + self.mb.client.append(self.home_folder, + message_from_string('').as_string()) + self.assertEqual(self.mb.get_message_ids(), [1]) + + self.mb.client.append(self.home_folder, + message_from_string('').as_string()) + self.assertEqual(self.mb.get_message_ids(), [1, 2]) + + def test_get_messages(self): + self.assertEqual(self.mb.get_messages(), {}) + + self.mb.client.append(self.home_folder, + message_from_string('').as_string()) + self.assertEqual( + self.mb.get_messages(), + {1: {'FLAGS': ('\\Seen',), 'SEQ': 1, 'RFC822': '\r\n'}}) + + self.mb.client.append(self.home_folder, + message_from_string('').as_string()) + self.assertEqual( + self.mb.get_messages(), + {1: {'SEQ': 1, 'RFC822': '\r\n'}, + 2: {'FLAGS': ('\\Seen',), 'SEQ': 2, 'RFC822': '\r\n'}}) + + def test_mark_processed(self): + self.mb.client.append(self.home_folder, + message_from_string('').as_string()) + ids = self.mb.client.search(['UNDELETED']) + self.assertEqual(ids, [1]) + + self.mb.mark_processed(1) + + self.assertEquals(self.mb.client.get_flags([1]), {1: (FLAGGED,)}) + ids = self.mb.client.search(['FLAGGED']) + self.assertEqual(ids, [1]) + + ids = self.mb.client.search(['UNFLAGGED']) + self.assertEqual(ids, []) diff --git a/mailbot/mailbot.py b/mailbot/mailbot.py index fa94a92..53779a7 100644 --- a/mailbot/mailbot.py +++ b/mailbot/mailbot.py @@ -2,7 +2,7 @@ from email import message_from_string -from imapclient import IMAPClient +from imapclient import IMAPClient, FLAGGED class MailBot(object): @@ -13,6 +13,7 @@ class MailBot(object): against the registered rules for each of them. """ + home_folder = 'INBOX' imapclient = IMAPClient message_constructor = message_from_string # easier for testing @@ -21,6 +22,7 @@ class MailBot(object): self.client = self.imapclient(host, port=port, use_uid=use_uid, ssl=ssl, stream=stream) self.client.login(username, password) + self.client.select_folder(self.home_folder) def get_message_ids(self): """Return the list of IDs of messages to process.""" @@ -50,4 +52,4 @@ class MailBot(object): def mark_processed(self, uid): """Mark the message corresponding to uid as processed.""" - self.client.set_flags([uid], ['FLAGGED']) + self.client.add_flags([uid], [FLAGGED]) diff --git a/mailbot/tests/test_mailbot.py b/mailbot/tests/test_mailbot.py index 64cca2b..a3ca6a1 100644 --- a/mailbot/tests/test_mailbot.py +++ b/mailbot/tests/test_mailbot.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from imapclient import FLAGGED from mock import patch, sentinel, Mock, DEFAULT, call from . import MailBotTestCase @@ -22,8 +23,8 @@ class MailBotClientTest(MailBotTestCase): class MailBotTest(MailBotClientTest): @patch.multiple('imapclient.imapclient.IMAPClient', - login=DEFAULT, __init__=DEFAULT) - def test_init(self, login, __init__): + login=DEFAULT, __init__=DEFAULT, select_folder=DEFAULT) + def test_init(self, login, __init__, select_folder): __init__.return_value = None kwargs = {'port': sentinel.port, @@ -34,6 +35,7 @@ class MailBotTest(MailBotClientTest): __init__.assert_called_once_with('somehost', **kwargs) login.assert_called_once_with('john', 'doe') + select_folder.assert_called_once_with(self.bot.home_folder) def test_get_message_ids(self): self.bot.client.search.return_value = sentinel.id_list @@ -103,5 +105,5 @@ class MailBotTest(MailBotClientTest): def test_mark_processed(self): self.bot.mark_processed(sentinel.id) - self.bot.client.set_flags.assert_called_once_with([sentinel.id], - ['FLAGGED']) + self.bot.client.add_flags.assert_called_once_with([sentinel.id], + [FLAGGED]) diff --git a/tox.ini b/tox.ini index 975017f..571c6ac 100644 --- a/tox.ini +++ b/tox.ini @@ -5,8 +5,8 @@ envlist = py26, py27 [testenv] commands = python setup.py develop - coverage run --branch --source=mailbot bin/unit2 discover - coverage report -m --omit=mailbot/tests/* + coverage run --branch --source=mailbot bin/unit2 discover mailbot.tests + coverage report -m --omit=mailbot/tests/*,mailbot/livetests/* flake8 mailbot deps = imapclient @@ -14,3 +14,13 @@ deps = mock flake8 coverage + +[testenv:py26-live] +commands = + python setup.py develop + unit2 discover mailbot.livetests + +[testenv:py27-live] +commands = + python setup.py develop + unit2 discover mailbot.livetests