From 06ac408dbd7629d387dd7b311a26c144ee56631e Mon Sep 17 00:00:00 2001 From: mnandri Date: Thu, 17 Dec 2015 18:41:58 +0100 Subject: extracted a leap attachment store, handling all attachment responsibilities, including saving attachments. Issue #548 --- .../mailstore/test_leap_attachment_store.py | 119 +++++++++++++++++++++ .../unit/adapter/mailstore/test_leap_mailstore.py | 39 ------- service/test/unit/adapter/test_mail_service.py | 5 +- .../unit/resources/test_attachments_resource.py | 24 +++-- 4 files changed, 139 insertions(+), 48 deletions(-) create mode 100644 service/test/unit/adapter/mailstore/test_leap_attachment_store.py (limited to 'service/test/unit') diff --git a/service/test/unit/adapter/mailstore/test_leap_attachment_store.py b/service/test/unit/adapter/mailstore/test_leap_attachment_store.py new file mode 100644 index 00000000..172cceb6 --- /dev/null +++ b/service/test/unit/adapter/mailstore/test_leap_attachment_store.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2015 ThoughtWorks, Inc. +# +# Pixelated is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pixelated is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Pixelated. If not, see . +import json +from uuid import uuid4 + +from leap.mail.adaptors.soledad_indexes import MAIL_INDEXES +from leap.soledad.common.document import SoledadDocument +from mockito import mock, when, verify +import test.support.mockito +from twisted.internet import defer +from twisted.trial.unittest import TestCase +from leap.mail.adaptors.soledad import SoledadMailAdaptor, MailboxWrapper, ContentDocWrapper + +from pixelated.adapter.mailstore.leap_attachment_store import LeapAttachmentStore + + +class TestLeapAttachmentStore(TestCase): + def setUp(self): + self.soledad = mock() + self.mbox_uuid = str(uuid4()) + self.doc_by_id = {} + self.mbox_uuid_by_name = {} + self.mbox_soledad_docs = [] + + when(self.soledad).get_from_index('by-type', 'mbox').thenAnswer(lambda: defer.succeed(self.mbox_soledad_docs)) + self._mock_get_mailbox('INBOX') + + @defer.inlineCallbacks + def test_get_mail_attachment(self): + attachment_id = 'AAAA9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' + doc = SoledadDocument(json=json.dumps({'content_type': 'foo/bar', 'raw': 'asdf'})) + when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([doc])) + store = LeapAttachmentStore(self.soledad) + + attachment = yield store.get_mail_attachment(attachment_id) + + self.assertEqual({'content-type': 'foo/bar', 'content': bytearray('asdf')}, attachment) + + @defer.inlineCallbacks + def test_store_attachment(self): + content = 'this is some attachment content' + content_type = 'text/plain' + cdoc_serialized = {'content_transfer_encoding': 'base64', 'lkf': [], 'content_disposition': 'attachment', + 'ctype': '', 'raw': 'dGhpcyBpcyBzb21lIGF0dGFjaG1lbnQgY29udGVudA==', + 'phash': '9863729729D2E2EE8E52F0A7115CE33AD18DDA4B58E49AE08DD092D1C8E699B0', + 'content_type': 'text/plain', 'type': 'cnt'} + + store = LeapAttachmentStore(self.soledad) + + attachment_id = yield store.add_attachment(content, content_type) + + self.assertEqual('9863729729D2E2EE8E52F0A7115CE33AD18DDA4B58E49AE08DD092D1C8E699B0', attachment_id) + + verify(self.soledad).create_doc(cdoc_serialized, doc_id=attachment_id) + + @defer.inlineCallbacks + def test_get_mail_attachment_different_content_encodings(self): + attachment_id = '1B0A9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' + encoding_examples = [('', 'asdf', 'asdf'), + ('base64', 'asdf', 'YXNkZg=='), + ('quoted-printable', 'äsdf', '=C3=A4sdf')] + + for transfer_encoding, data, encoded_data in encoding_examples: + doc = SoledadDocument(json=json.dumps({'content_type': 'foo/bar', 'raw': encoded_data, + 'content_transfer_encoding': transfer_encoding})) + when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([doc])) + store = LeapAttachmentStore(self.soledad) + + attachment = yield store.get_mail_attachment(attachment_id) + + self.assertEqual(bytearray(data), attachment['content']) + + @defer.inlineCallbacks + def test_get_mail_attachment_throws_exception_if_attachment_does_not_exist(self): + attachment_id = '1B0A9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' + when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([])) + store = LeapAttachmentStore(self.soledad) + try: + yield store.get_mail_attachment(attachment_id) + self.fail('ValueError exception expected') + except ValueError: + pass + + def _mock_get_mailbox(self, mailbox_name, create_new_uuid=False): + mbox_uuid = self.mbox_uuid if not create_new_uuid else str(uuid4()) + when(self.soledad).list_indexes().thenReturn(defer.succeed(MAIL_INDEXES)).thenReturn( + defer.succeed(MAIL_INDEXES)) + doc_id = str(uuid4()) + mbox = MailboxWrapper(doc_id=doc_id, mbox=mailbox_name, uuid=mbox_uuid) + soledad_doc = SoledadDocument(doc_id, json=json.dumps(mbox.serialize())) + when(self.soledad).get_from_index('by-type-and-mbox', 'mbox', mailbox_name).thenReturn(defer.succeed([soledad_doc])) + self._mock_get_soledad_doc(doc_id, mbox) + + self.mbox_uuid_by_name[mailbox_name] = mbox_uuid + self.mbox_soledad_docs.append(soledad_doc) + + return mbox, soledad_doc + + def _mock_get_soledad_doc(self, doc_id, doc): + soledad_doc = SoledadDocument(doc_id, json=json.dumps(doc.serialize())) + + # when(self.soledad).get_doc(doc_id).thenReturn(defer.succeed(soledad_doc)) + when(self.soledad).get_doc(doc_id).thenAnswer(lambda: defer.succeed(soledad_doc)) + + self.doc_by_id[doc_id] = soledad_doc diff --git a/service/test/unit/adapter/mailstore/test_leap_mailstore.py b/service/test/unit/adapter/mailstore/test_leap_mailstore.py index 4eabc144..b5b6a742 100644 --- a/service/test/unit/adapter/mailstore/test_leap_mailstore.py +++ b/service/test/unit/adapter/mailstore/test_leap_mailstore.py @@ -134,45 +134,6 @@ class TestLeapMailStore(TestCase): self.assertEqual(expeted_body, mail.body) - @defer.inlineCallbacks - def test_get_mail_attachment(self): - attachment_id = 'AAAA9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' - doc = SoledadDocument(json=json.dumps({'content_type': 'foo/bar', 'raw': 'asdf'})) - when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([doc])) - store = LeapMailStore(self.soledad) - - attachment = yield store.get_mail_attachment(attachment_id) - - self.assertEqual({'content-type': 'foo/bar', 'content': bytearray('asdf')}, attachment) - - @defer.inlineCallbacks - def test_get_mail_attachment_different_content_encodings(self): - attachment_id = '1B0A9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' - encoding_examples = [('', 'asdf', 'asdf'), - ('base64', 'asdf', 'YXNkZg=='), - ('quoted-printable', 'äsdf', '=C3=A4sdf')] - - for transfer_encoding, data, encoded_data in encoding_examples: - doc = SoledadDocument(json=json.dumps({'content_type': 'foo/bar', 'raw': encoded_data, - 'content_transfer_encoding': transfer_encoding})) - when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([doc])) - store = LeapMailStore(self.soledad) - - attachment = yield store.get_mail_attachment(attachment_id) - - self.assertEqual(bytearray(data), attachment['content']) - - @defer.inlineCallbacks - def test_get_mail_attachment_throws_exception_if_attachment_does_not_exist(self): - attachment_id = '1B0A9AAD9E153D24265395203C53884506ABA276394B9FEC02B214BF9E77E48E' - when(self.soledad).get_from_index('by-type-and-payloadhash', 'cnt', attachment_id).thenReturn(defer.succeed([])) - store = LeapMailStore(self.soledad) - try: - yield store.get_mail_attachment(attachment_id) - self.fail('ValueError exception expected') - except ValueError: - pass - @defer.inlineCallbacks def test_update_mail(self): mdoc_id, fdoc_id = self._add_mail_fixture_to_soledad_from_file('mbox00000000') diff --git a/service/test/unit/adapter/test_mail_service.py b/service/test/unit/adapter/test_mail_service.py index d14a0eb0..27c80fde 100644 --- a/service/test/unit/adapter/test_mail_service.py +++ b/service/test/unit/adapter/test_mail_service.py @@ -28,6 +28,7 @@ class TestMailService(unittest.TestCase): def setUp(self): self.drafts = mock() self.mail_store = mock() + self.attachment_store = mock() self.mailboxes = mock() self.mailboxes.drafts = defer.succeed(self.drafts) @@ -37,7 +38,7 @@ class TestMailService(unittest.TestCase): self.mail_sender = mock() self.search_engine = mock() - self.mail_service = MailService(self.mail_sender, self.mail_store, self.search_engine, 'acount@email') + self.mail_service = MailService(self.mail_sender, self.mail_store, self.search_engine, 'acount@email', self.attachment_store) def tearDown(self): unstub() @@ -158,7 +159,7 @@ class TestMailService(unittest.TestCase): @defer.inlineCallbacks def test_get_attachment(self): attachment_dict = {'content': bytearray('data'), 'content-type': 'text/plain'} - when(self.mail_store).get_mail_attachment('some attachment id').thenReturn(defer.succeed(attachment_dict)) + when(self.attachment_store).get_mail_attachment('some attachment id').thenReturn(defer.succeed(attachment_dict)) attachment = yield self.mail_service.attachment('some attachment id') diff --git a/service/test/unit/resources/test_attachments_resource.py b/service/test/unit/resources/test_attachments_resource.py index 837f5324..2afa208c 100644 --- a/service/test/unit/resources/test_attachments_resource.py +++ b/service/test/unit/resources/test_attachments_resource.py @@ -2,6 +2,7 @@ import json import unittest from mock import patch, MagicMock +from mockito import mock, when, verify from twisted.internet import defer from twisted.web.test.requesthelper import DummyRequest @@ -12,19 +13,22 @@ from test.unit.resources import DummySite class AttachmentsResourceTest(unittest.TestCase): def setUp(self): - self.mail_service = MagicMock() + self.mail_service = mock() self.mails_resource = AttachmentsResource(self.mail_service) self.mails_resource.isLeaf = True self.web = DummySite(self.mails_resource) - @patch('twisted.internet.defer.maybeDeferred') @patch('cgi.FieldStorage') - def test_post_new_attachment(self, mock_fields, mock_maybe_deferred): + def test_post_new_attachment(self, mock_fields): request = DummyRequest(['/attachment']) request.method = 'POST' request.content = 'mocked' attachment_id = 'B5B4ED80AC3B894523D72E375DACAA2FC6606C18EDF680FE95903086C8B5E14A' - mock_maybe_deferred.return_value = defer.succeed(attachment_id) + _file = MagicMock() + _file.value = 'some mocked value' + _file.type = 'some mocked type' + mock_fields.return_value = {'attachment': _file} + when(self.mail_service).save_attachment('some mocked value', 'some mocked type').thenReturn(defer.succeed(attachment_id)) d = self.web.get(request) @@ -32,24 +36,30 @@ class AttachmentsResourceTest(unittest.TestCase): self.assertEqual(201, request.code) self.assertEqual('/attachment/%s' % attachment_id, request.headers['Location']) self.assertEqual({'attachment_id': attachment_id}, json.loads(request.written[0])) + verify(self.mail_service).save_attachment('some mocked value', 'some mocked type') d.addCallback(assert_response) return d - @patch('twisted.internet.defer.maybeDeferred') @patch('cgi.FieldStorage') - def test_post_attachment_fails(self, mock_fields, mock_maybe_deferred): - mock_maybe_deferred.return_value = defer.fail(Exception) + def test_post_attachment_fails(self, mock_fields): request = DummyRequest(['/attachment']) request.method = 'POST' request.content = 'mocked' + _file = MagicMock() + _file.value = 'some mocked value' + _file.type = 'some mocked type' + mock_fields.return_value = {'attachment': _file} + when(self.mail_service).save_attachment('some mocked value', 'some mocked type').thenReturn(defer.fail(Exception)) + d = self.web.get(request) def assert_response(_): self.assertEqual(500, request.code) self.assertFalse('Location' in request.headers) self.assertEqual({"message": "Something went wrong. Attachement not saved."}, json.loads(request.written[0])) + verify(self.mail_service).save_attachment('some mocked value', 'some mocked type') d.addCallback(assert_response) return d -- cgit v1.2.3