diff options
author | Matěj Cepl <mcepl@cepl.eu> | 2018-04-22 02:03:06 +0200 |
---|---|---|
committer | Matěj Cepl <mcepl@cepl.eu> | 2018-04-22 02:03:06 +0200 |
commit | ca9285e80b9fa2df82b135b3684f933d05222f69 (patch) | |
tree | 5f78c55a9385cc7726390360abfa436c3731ee6c | |
parent | 75be2c284dc0647765046b55655ecd0dbb64f131 (diff) | |
download | imapArch-ca9285e80b9fa2df82b135b3684f933d05222f69.tar.gz |
Add some more tests.
-rwxr-xr-x | archive_folder.py | 56 | ||||
-rw-r--r-- | test/msg_test.eml | 98 | ||||
-rw-r--r-- | test/test_email_server.py | 82 |
3 files changed, 201 insertions, 35 deletions
diff --git a/archive_folder.py b/archive_folder.py index 744fcba..4b86707 100755 --- a/archive_folder.py +++ b/archive_folder.py @@ -1,11 +1,6 @@ #!/usr/bin/env python3.6 # note http://docs.python.org/lib/module-doctest.html # resp. file:///usr/share/doc/python-docs-*/html/lib/module-doctest.html -# FIXME -# read https://www.toptal.com/python/an-introduction-to-mocking-in-python -# & https://blog.fugue.co/2016-02-11-python-mocking-101.html -# for unittest.mock tutorials -# for testing import argparse import collections import configparser @@ -17,6 +12,7 @@ import locale import logging import imaplib import os +import re import sys from datetime import date, timedelta @@ -34,6 +30,7 @@ class MessageError(IOError): pass Capas = collections.namedtuple('Capas', ['MOVE', 'UIDPLUS']) +SEP_RE = re.compile(r'\s+"([/.])"\s+') class Message(object): @@ -69,14 +66,11 @@ class Message(object): class Folder(object): def __init__(self, box, fld_name, create=False): self.box = box - log.debug('fld_name = %s', fld_name) self.fld_name = fld_name self.selected = False self.__create_missing = create - self.__folder_sep = self.__get_separator() + self.folder_sep = self.__get_separator() - log.debug('create_missing = %s', self.__create_missing) - log.debug('list_folder = %s', self.__list_folder) if self.__create_missing and self.__list_folder() is None: self.__create_folder() @@ -88,13 +82,11 @@ class Folder(object): return self def __create_folder(self): - sep = self.__folder_sep + sep = self.folder_sep split_name = self.fld_name.split(sep) - log.debug('split_name = %s', split_name) target = '' for part in split_name: - log.debug('part = %s', part) - target += '{}{}'.format(part, self.__folder_sep) + target += '{}{}'.format(part, self.folder_sep) if self.__list_folder(target) is None: self.box.create(target) self.box.subscribe(target) @@ -117,14 +109,12 @@ class Folder(object): """ data = self.__list_folder() - log.debug('data = %s (%s)', data, type(data)) - if data is None: data = self.__list_folder('""', '""') - data = data.decode().split(' ') - if len(data) == 3: - return data[1].strip(' "') + parse_data = SEP_RE.search(data.decode()) + if parse_data is not None: + return parse_data.group(1) else: raise ServerError('Cannot find folder separator from %s' % data) @@ -143,26 +133,24 @@ class Folder(object): return self.__emails_search('BEFORE', before_str) def get_archive_folder(self, msg, aroot): - return self.__folder_sep.join((aroot, msg.date.strftime("%Y"), self.fld_name)) + return self.folder_sep.join((aroot, msg.date.strftime("%Y"), self.fld_name)) def move_messages(self, messages): assert self.selected == False, 'Target folder should not be selected.' if self.box.features_present.MOVE: - ok, data = self.box.uid('MOVE', - '%s %s' % (messages, self.fld_name)) + ok, data = self.box.uid('MOVE', messages, self.fld_name) log.debug('MOVE ok = %s, data = %s', ok, data) if ok != 'OK': raise FolderError('Cannot move messages to folder %s' % self.fld_name) elif self.box.features_present.UIDPLUS: - ok, data = self.box.uid('COPY', - '%s %s' % (messages, self.fld_name)) + ok, data = self.box.uid('COPY', messages, self.fld_name) log.debug('COPY ok = %s, data = %s', ok, data) if ok != 'OK': raise FolderError('Cannot copy messages to folder %s' % self.fld_name) ok, data = self.box.uid('STORE', - r'+FLAGS.SILENT (\DELETED) %s' % messages) + r'+FLAGS.SILENT (\DELETED)', messages) log.debug('STORE ok = %s, data = %s', ok, data) if ok != 'OK': raise FolderError('Cannot delete messages-') @@ -171,14 +159,13 @@ class Folder(object): if ok != 'OK': raise FolderError('Cannot expunge messages.') else: - ok, data = self.box.uid('COPY', - '%s %s' % (messages, self.fld_name)) + ok, data = self.box.uid('COPY', messages, self.fld_name) log.debug('COPY ok = %s, data = %s', ok, data) if ok != 'OK': raise FolderError('Cannot copy messages to folder %s' % self.fld_name) ok, data = self.box.uid('STORE', - r'+FLAGS.SILENT (\DELETED) %s' % messages) + r'+FLAGS.SILENT (\DELETED)', messages) log.debug('STORE ok = %s, data = %s', ok, data) if ok != 'OK': raise FolderError('Cannot delete messages-') @@ -191,12 +178,11 @@ class EmailServer(object): else config['general']['account'] self.cfg = dict(config.items(acc_name)) self.archive_root = archive_root - self.__box = self.__login(**self.cfg) + self.box = self.__login(**self.cfg) def __login(self, host='localhost', username=None, password=None, ssl=None): box = imaplib.IMAP4_SSL(host=host) - res = box.login(username, password) - ok, data = res + ok, data = box.login(username, password) if ok != 'OK': raise ServerError('Cannot login with credentials %s' % str((host, username, password,))) @@ -204,9 +190,9 @@ class EmailServer(object): ok, data = box.capability() capas = data[0].decode() box.features_present = Capas._make(['MOVE' in capas, 'UIDPLUS' in capas]) + return box @staticmethod - @functools.lru_cache() def get_config(): # In case the configuration file is missing, only empty list will be # returned @@ -223,7 +209,7 @@ class EmailServer(object): :param: before_date """ copy_cache = {} - fld = Folder(self.__box, folder_name).select() + fld = Folder(self.box, folder_name).select() before_str = before_date.strftime('%d-%b-%Y') for msg in fld.emails_before(before_str): arch_folder = fld.get_archive_folder(msg, self.archive_root) @@ -242,8 +228,8 @@ class EmailServer(object): msg_ids = ','.join([x.uid for x in copy_cache[arch_dir]]) log.debug('arch_dir = %s, msgs = %s', arch_dir, msg_ids) - dir = Folder(self.__box, arch_dir, create=True) - dir.move_messages(msg_ids) + dir = Folder(self.box, arch_dir, create=True) + # dir.move_messages(msg_ids) def __enter__(self): return self @@ -251,7 +237,7 @@ class EmailServer(object): def __exit__(self, *args): if args != (None, None, None): log.warning('args = %s', args) - self.__box.close() + self.box.close() if __name__ == '__main__': diff --git a/test/msg_test.eml b/test/msg_test.eml new file mode 100644 index 0000000..f9dc8e6 --- /dev/null +++ b/test/msg_test.eml @@ -0,0 +1,98 @@ +From ???@??? Wed Jul 18 17:27:27 2001
+Return-Path: <sentto-3642313-6-995471638-cepl=bigfoot.com@returns.onelist.com>
+Delivered-To: cepl@surfbest.net
+Received: from bigfoot.com (litemail.bigfoot.com [208.156.39.208])
+ by server12.safepages.com (Postfix) with SMTP id 5DA8C136099
+ for <cepl@surfbest.net>; Wed, 18 Jul 2001 15:54:52 +0000 (GMT)
+Received: from ci.egroups.com ([64.211.240.235])
+ by BFLITEMAIL1.bigfoot.com (LiteMail v3.01(BFLITEMAIL1)) with SMTP id 18Jul2001_BFLITEMAIL1_13769_126340112;
+ Wed, 18 Jul 2001 12:00:18 -0400 EST
+X-eGroups-Return:sentto-3642313-6-995471638-cepl=bigfoot.com@returns.onelist.com
+Received: from[10.1.4.56] by ci.egroups.com with NNFMP; 18 Jul 2001 15:53:58 -0000
+X-Sender:frederic.amblard@CLERMONT.cemagref.fr
+X-Apparently-To:dynnet@yahoogroups.com
+Received: (EGP: mail-7_2_0); 18 Jul 2001 15:53:57-0000
+Received: (qmail 59967 invoked from network); 18 Jul 2001 15:53:32-0000
+Received: from unknown (10.1.10.26) by l10.egroups.com with QMQP; 18 Jul2001 15:53:32 -0000
+Received: from unknown (HELO lassolas.clermont.cemagref.fr)(195.221.117.5) by mta1 with SMTP; 18 Jul 2001 15:53:32 -0000
+Received: bylassolas.clermont.cemagref.fr with Internet Mail Service (5.5.2448.0) id<M6H72S2M>; Wed, 18 Jul 2001 17:53:32 +0200
+Message-ID:<F17CAFD56F9CD1118E3A006097233920016DF085@lassolas.clermont.cemagref.fr>
+To:"'dynnet@yahoogroups.com'" <dynnet@yahoogroups.com>
+X-Mailer: Internet MailService (5.5.2448.0)
+From: Amblard Frederic<frederic.amblard@CLERMONT.cemagref.fr>
+MIME-Version: 1.0
+Mailing-List: listdynnet@yahoogroups.com; contact dynnet-owner@yahoogroups.com
+Delivered-To:mailing list dynnet@yahoogroups.com
+Precedence: bulk
+List-Unsubscribe:<mailto:dynnet-unsubscribe@yahoogroups.com>
+Date: Wed, 18 Jul 2001 17:53:30+0200
+Reply-To: dynnet@yahoogroups.com
+Subject: TR: [dynnet] Evolution ofnetworks a temptative of bibliography
+Content-Type: text/plain;charset=windows-1252
+Content-Transfer-Encoding: quoted-printable
+X-PMFLAGS:34078848 0 1 P52F70.CNM
+Content-Length: 1633
+Lines: 61
+
+Thank you Scottand please forgive my mistake, it is sometimes hard to
+follow the life ofworking papers ...=20
+in addition here comes the link to the book:=20
+http://www.amazon.com/exec/obidos/ASIN/354041522X/qid=3D995471342/sr=3D1-1/=
+ref=3Ds
+c_b_1/104-8956980-7775125
+andto the one published by Rosaria Conte, Rainer Hegselmann and Pietro
+Terna forthe article of Tom Snijders:=20
+http://www.amazon.com/exec/obidos/ASIN/3540633294/qid=3D995471480/sr=3D1-1/=
+ref=3Ds
+c_b_1/104-8956980-7775125
+
+ATB
+Fred=20
+
+-----Messaged'origine-----
+De : Scott Moss [mailto:s.moss@mmu.ac.uk]
+Envoy=E9 : mercredi18 juillet 2001 17:45
+=C0 : Frederic Amblard
+Objet : Re: [dynnet] Evolutionof networks a temptative of bibliography
+
+
+Hi Frederic
+
+The Axtellpaper has been published in the Springer LNAI series. The
+author, title anddate are the same but the location is Scott Moss and
+Paul Davidsson (eds),Multi-Agent-Based Simulation, Lecture Notes in
+Artificial Intelligence No. 1979,pp. 33-48.
+
+yours,
+Scott
+
+Amblard Frederic wrote:
+
+> Axtell,R., Effects of Interaction Topology and Activation Regime in
+> SeveralMulti-agent Systems. 2000, Center on Social and Economic
+>Dynamics.(http://www.brookings.edu/ES/dynamics/papers/)
+
+--
+ProfessorScott Moss
+Director
+Centre for Policy Modelling
+Manchester MetropolitanUniversity
+Aytoun Building
+Manchester M1 3GH
+UNITEDKINGDOM
+
+telephone: +44 (0)161 247 3886
+fax: +44 (0)161 2476802
+
+http://www.cpm.mmu.ac.uk/~scott
+
+
+To unsubscribe from thisgroup, send an emailto:
+dynnet-unsubscribe@yahoogroups.com
+
+=20
+
+Your use of Yahoo!Groups is subject to http://docs.yahoo.com/info/terms/=20
+
+
+
diff --git a/test/test_email_server.py b/test/test_email_server.py index 8ff8788..737dd68 100644 --- a/test/test_email_server.py +++ b/test/test_email_server.py @@ -1,3 +1,4 @@ +import datetime import logging import unittest from unittest import mock @@ -6,6 +7,13 @@ import archive_folder log = logging.getLogger('test') + +def create_test_message(): + with open('test/msg_test.eml', 'rb') as msg_file: + msg_raw = msg_file.read() + return [(b'13 (UID 13 RFC822 {3776}', msg_raw), b')'] + + class TestEmailServer(unittest.TestCase): @mock.patch.object(archive_folder.imaplib, 'IMAP4_SSL', autospec=True) @mock.patch.object(archive_folder.configparser, 'ConfigParser', autospec=True) @@ -25,3 +33,77 @@ class TestEmailServer(unittest.TestCase): mock_imapobj().login.assert_called_once_with('fakeuser', 'veryverysecret') mock_imapobj().capability.assert_called_once_with() self.assertEqual(mock_imapobj().features_present, (False, True)) + self.assertIsInstance(box, archive_folder.EmailServer) + + +class TestFolder(unittest.TestCase): + @mock.patch.object(archive_folder.imaplib, 'IMAP4_SSL', autospec=True) + def test_folder_initialization(self, mock_imapobj): + mock_imapobj().list.return_value = ('OK', + [b'(\\HasNoChildren) "/" Work/sociology-junk']) + box = archive_folder.imaplib.IMAP4_SSL() + folder = archive_folder.Folder(box, 'INBOX/FakeFolder') + mock_imapobj().list.assert_called_once_with('INBOX/FakeFolder', '*') + self.assertIsInstance(folder, archive_folder.Folder) + self.assertEqual(folder.folder_sep, '/') + self.assertFalse(folder.selected) + + @mock.patch.object(archive_folder.imaplib, 'IMAP4_SSL', autospec=True) + def test_folder_select(self, mock_imapobj): + mock_imapobj().list.return_value = ('OK', + [b'(\\HasNoChildren) "/" Work/sociology-junk']) + mock_imapobj().select.return_value = ('OK', 'FAKE success') + box = archive_folder.imaplib.IMAP4_SSL() + folder = archive_folder.Folder(box, 'INBOX/FakeFolder').select() + mock_imapobj().list.assert_called_once_with('INBOX/FakeFolder', '*') + mock_imapobj().select.assert_called_once_with('INBOX/FakeFolder') + self.assertIsInstance(folder, archive_folder.Folder) + self.assertEqual(folder.folder_sep, '/') + self.assertTrue(folder.selected) + + @mock.patch.object(archive_folder.imaplib, 'IMAP4_SSL', autospec=True) + def test_folder_emails_before(self, mock_imapobj): + def fake_uid(cmd, param): + if cmd == 'FETCH': + return ('OK', create_test_message()) + elif cmd == 'SEARCH': + return ('OK', [b'13']) + + mock_imapobj().list.return_value = ('OK', + [b'(\\HasNoChildren) "/" Work/sociology-junk']) + mock_imapobj().select.return_value = ('OK', 'FAKE success') + mock_imapobj().uid.side_effect = fake_uid + box = archive_folder.imaplib.IMAP4_SSL() + folder = archive_folder.Folder(box, 'INBOX/FakeFolder').select() + msgs = folder.emails_before('25-Feb-1948') + self.assertIsInstance(msgs[0], archive_folder.Message) + + @mock.patch.object(archive_folder.imaplib, 'IMAP4_SSL', autospec=True) + def test_folder_get_archive_folder(self, mock_imapobj): + mock_imapobj().list.return_value = ('OK', + [b'(\\HasNoChildren) "/" Work/sociology-junk']) + mock_imapobj().select.return_value = ('OK', 'FAKE success') + box = archive_folder.imaplib.IMAP4_SSL() + folder = archive_folder.Folder(box, 'INBOX/FakeFolder').select() + + mock_imapobj().uid.return_value = ('OK', create_test_message()) + box = archive_folder.imaplib.IMAP4_SSL() + msg = archive_folder.Message(box, '10') + + arch_fld = folder.get_archive_folder(msg, 'FakeArchive') + self.assertEqual(arch_fld, 'FakeArchive/2001/INBOX/FakeFolder') + + +class TestMessage(unittest.TestCase): + @mock.patch.object(archive_folder.imaplib, 'IMAP4_SSL', autospec=True) + def test_message_initialization(self, mock_imapobj): + mock_imapobj().uid.return_value = ('OK', create_test_message()) + box = archive_folder.imaplib.IMAP4_SSL() + msg = archive_folder.Message(box, '10') + mock_imapobj().uid.assert_called_once_with('FETCH', '10 (RFC822)') + self.assertIsInstance(msg, archive_folder.Message) + self.assertEqual(msg.uid, '10') + self.assertEqual(msg.subject, + 'TR: [dynnet] Evolution ofnetworks a temptative of bibliography') + self.assertEqual(msg.date, datetime.datetime(2001, 7, 18, 17, 53, 30, + tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))) |