diff options
Diffstat (limited to 'service/test')
34 files changed, 562 insertions, 263 deletions
diff --git a/service/test/functional/features/compose_save_draft_and_send.feature b/service/test/functional/features/compose_save_draft_and_send.feature index 10fa1aa2..b24d4c51 100644 --- a/service/test/functional/features/compose_save_draft_and_send.feature +++ b/service/test/functional/features/compose_save_draft_and_send.feature @@ -15,16 +15,18 @@ # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. Feature: compose mail, save draft and send mail + As a user of Pixelated + I want to save drafts + So I can review and send them later Scenario: user composes and email, save the draft, later sends the draft and checks the sent message - Given I compose a message with + When I compose a message with | subject | body | | Pixelated rocks! | You should definitely use it. Cheers, User. | - # And for the 'To' field I type 'ab' and chose the first contact that shows - And for the 'To' field I enter 'pixelated@friends.org' - And I save the draft + And for the 'To' field I enter 'pixelated@friends.org' + And I save the draft When I open the saved draft and send it Then I see that mail under the 'sent' tag When I open that mail Then I see that the subject reads 'Pixelated rocks!' - And I see that the body reads 'You should definitely use it. Cheers, User.' + And I see that the body reads 'You should definitely use it. Cheers, User.' diff --git a/service/test/functional/features/environment.py b/service/test/functional/features/environment.py index 5e93c840..5969120a 100644 --- a/service/test/functional/features/environment.py +++ b/service/test/functional/features/environment.py @@ -14,11 +14,11 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. import logging -import time -from test.support.dispatcher.proxy import Proxy +from test.support.dispatcher.proxy import Proxy from test.support.integration import AppTestClient from selenium import webdriver + from pixelated.resources.features_resource import FeaturesResource @@ -50,10 +50,6 @@ def after_feature(context, feature): context.browser.quit() -def take_screenshot(context): - context.browser.save_screenshot('/tmp/screenshot.jpeg') - - def save_source(context): with open('/tmp/source.html', 'w') as out: out.write(context.browser.page_source.encode('utf8')) diff --git a/service/test/functional/features/forward_trash_archive.feature b/service/test/functional/features/forward_trash_archive.feature index 91e078ea..85c422d9 100644 --- a/service/test/functional/features/forward_trash_archive.feature +++ b/service/test/functional/features/forward_trash_archive.feature @@ -14,18 +14,19 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -Feature: forward_trash_archive +Feature: forward and deletion + As a user of Pixelated + I want to forward emails using CC and Bcc features + So I can take actions Scenario: User forwards a mail, add CC and BCC address, later trash the mail Given I have a mail in my inbox When I open the first mail in the 'inbox' - Then I choose to forward this mail - # And for the 'CC' field I type 'ab' and chose the first contact that shows - # And for the 'Bcc' field I type 'fr' and chose the first contact that shows - Given for the 'CC' field I enter 'pixelated@friends.org' - And for the 'Bcc' field I enter 'pixelated@family.org' - Then I forward this mail + And I choose to forward this mail + When for the 'CC' field I enter 'pixelated@friends.org' + And for the 'Bcc' field I enter 'pixelated@family.org' + And I forward this mail When I open the first mail in the 'sent' Then I see the mail has a cc and a bcc recipient - And I choose to trash + When I choose to trash Then I see that mail under the 'trash' tag diff --git a/service/test/functional/features/search_and_destroy.feature b/service/test/functional/features/search_and_destroy.feature index 6efeae8b..4ce37b78 100644 --- a/service/test/functional/features/search_and_destroy.feature +++ b/service/test/functional/features/search_and_destroy.feature @@ -16,15 +16,16 @@ # XXX: must implement with HTML content -Feature: search mail and destroy +Feature: search mail and deletion + As a user of pixelated + I want to search for emails + So I can manage them - Scenario: User searches for a mail and deletes it' + Scenario: User searches for a mail and deletes it Given I have a mail in my inbox When I search for a mail with the words "the body of this message" When I open the first mail in the mail list Then I see one or more mails in the search results -# Then I see if the mail has html content When I try to delete the first mail - # Then I learn that the mail was deleted When I select the tag 'trash' Then the deleted mail is there diff --git a/service/test/functional/features/steps/__init__.py b/service/test/functional/features/steps/__init__.py index e69de29b..2756a319 100644 --- a/service/test/functional/features/steps/__init__.py +++ b/service/test/functional/features/steps/__init__.py @@ -0,0 +1,15 @@ +# +# Copyright (c) 2014 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 <http://www.gnu.org/licenses/>. diff --git a/service/test/functional/features/steps/common.py b/service/test/functional/features/steps/common.py index 558361d0..7848089b 100644 --- a/service/test/functional/features/steps/common.py +++ b/service/test/functional/features/steps/common.py @@ -20,8 +20,8 @@ from selenium.common.exceptions import TimeoutException from hamcrest import * -def wait_until_element_is_invisible_by_locator(context, locator_tuple): - wait = WebDriverWait(context.browser, 10) +def wait_until_element_is_invisible_by_locator(context, locator_tuple, timeout=10): + wait = WebDriverWait(context.browser, timeout) wait.until(EC.invisibility_of_element_located(locator_tuple)) @@ -30,18 +30,18 @@ def wait_until_element_is_deleted(context, locator_tuple, timeout=10): wait.until(lambda s: len(s.find_elements(locator_tuple[0], locator_tuple[1])) == 0) -def wait_for_user_alert_to_disapear(context): - wait_until_element_is_invisible_by_locator(context, (By.ID, 'user-alerts')) +def wait_for_user_alert_to_disapear(context, timeout=10): + wait_until_element_is_invisible_by_locator(context, (By.ID, 'user-alerts'), timeout) -def wait_until_elements_are_visible_by_locator(context, locator_tuple): - wait = WebDriverWait(context.browser, 10) +def wait_until_elements_are_visible_by_locator(context, locator_tuple, timeout=10): + wait = WebDriverWait(context.browser, timeout) wait.until(EC.presence_of_all_elements_located(locator_tuple)) return context.browser.find_elements(locator_tuple[0], locator_tuple[1]) -def wait_until_element_is_visible_by_locator(context, locator_tuple): - wait = WebDriverWait(context.browser, 10) +def wait_until_element_is_visible_by_locator(context, locator_tuple, timeout=10): + wait = WebDriverWait(context.browser, timeout) wait.until(EC.visibility_of_element_located(locator_tuple)) return context.browser.find_element(locator_tuple[0], locator_tuple[1]) @@ -93,8 +93,8 @@ def element_should_have_content(context, css_selector, content): assert_that(e.text, equal_to(content)) -def wait_until_button_is_visible(context, title): - wait = WebDriverWait(context.browser, 10) +def wait_until_button_is_visible(context, title, timeout=10): + wait = WebDriverWait(context.browser, timeout) locator_tuple = (By.XPATH, ("//%s[contains(.,'%s')]" % ('button', title))) wait.until(EC.visibility_of_element_located(locator_tuple)) diff --git a/service/test/functional/features/steps/compose.py b/service/test/functional/features/steps/compose.py index cf75979e..aeef11c4 100644 --- a/service/test/functional/features/steps/compose.py +++ b/service/test/functional/features/steps/compose.py @@ -20,7 +20,7 @@ from common import * from hamcrest import * -@given('I compose a message with') +@when('I compose a message with') def impl(context): take_screenshot(context, '/tmp/screenshot.jpeg') toggle = context.browser.find_element_by_id('compose-mails-trigger') @@ -31,31 +31,19 @@ def impl(context): fill_by_xpath(context, '//*[@id="text-box"]', row['body']) -@given("for the '{recipients_field}' field I type '{to_type}' and chose the first contact that shows") -def choose_impl(context, recipients_field, to_type): - browser = context.browser - browser.find_element_by_css_selector('#recipients-to-area span input.tt-input').click() - recipients_field = recipients_field.lower() - css_selector = '#recipients-%s-area' % recipients_field - recipients_element = browser.find_element_by_css_selector(css_selector) - recipients_element.find_element_by_css_selector('.tt-input').send_keys(to_type) - wait_until_element_is_visible_by_locator(context, (By.CSS_SELECTOR, '.tt-dropdown-menu div div')) - browser.find_element_by_css_selector('.tt-dropdown-menu div div').click() - - -@given("for the '{recipients_field}' field I enter '{to_type}'") +@when("for the '{recipients_field}' field I enter '{to_type}'") def enter_address_impl(context, recipients_field, to_type): _enter_recipient(context, recipients_field, to_type + "\n") -@then("for the '{recipients_field}' field I type '{to_type}' and chose the first contact that shows") +@when("for the '{recipients_field}' field I type '{to_type}' and chose the first contact that shows") def choose_impl(context, recipients_field, to_type): _enter_recipient(context, recipients_field, to_type) sleep(1) find_element_by_css_selector(context, '.tt-dropdown-menu div div').click() -@given('I save the draft') +@when('I save the draft') def save_impl(context): context.browser.find_element_by_id('draft-button').click() diff --git a/service/test/functional/features/steps/mail_list.py b/service/test/functional/features/steps/mail_list.py index 6a764568..4122f065 100644 --- a/service/test/functional/features/steps/mail_list.py +++ b/service/test/functional/features/steps/mail_list.py @@ -54,7 +54,7 @@ def impl(context, tag): context.execute_steps(u'When I open the first mail in the mail list') -@then('I open the mail I previously tagged') +@when('I open the mail I previously tagged') def impl(context): open_current_mail(context) @@ -68,3 +68,9 @@ def impl(context): @then('the deleted mail is there') def impl(context): check_current_mail_is_visible(context) + + +@given('I have mails') +def impl(context): + elements = wait_until_elements_are_visible_by_locator(context, (By.XPATH, '//*[@id="mail-list"]//a')) + assert len(elements) > 0 diff --git a/service/test/functional/features/steps/mail_view.py b/service/test/functional/features/steps/mail_view.py index ca0d68dc..98591aa4 100644 --- a/service/test/functional/features/steps/mail_view.py +++ b/service/test/functional/features/steps/mail_view.py @@ -46,9 +46,10 @@ def impl(context, tag): e = wait_until_element_is_visible_by_locator(context, (By.ID, 'new-tag-input')) e.send_keys(tag) e.send_keys(Keys.ENTER) + wait_until_element_is_visible_by_locator(context, (By.XPATH, '//li[@data-tag="%s"]' % tag)) -@then('I reply to it') +@when('I reply to it') def impl(context): click_button(context, 'Reply') click_button(context, 'Send') @@ -72,20 +73,20 @@ def impl(context): assert_that(e.text, equal_to('Your message was moved to trash!')) -@then('I choose to forward this mail') +@when('I choose to forward this mail') def impl(context): wait_until_button_is_visible(context, 'Forward') click_button(context, 'Forward') -@then('I forward this mail') +@when('I forward this mail') def impl(context): - context.execute_steps(u'Given I save the draft') # FIXME: this won't be necessary after #89 is done + context.execute_steps(u'When I save the draft') # FIXME: this won't be necessary after #89 is done wait_until_button_is_visible(context, 'Send') click_button(context, 'Send') -@then('I remove all tags') +@when('I remove all tags') def impl(context): e = find_element_by_css_selector(context, '.tagsArea') tags = e.find_elements_by_css_selector('.tag') @@ -94,7 +95,7 @@ def impl(context): tag.click() -@then('I choose to trash') +@when('I choose to trash') def impl(context): context.browser.execute_script("$('button#view-more-actions').click()") click_button(context, 'Delete this message', 'span') diff --git a/service/test/functional/features/steps/tag_list.py b/service/test/functional/features/steps/tag_list.py index 62b2571f..348b121a 100644 --- a/service/test/functional/features/steps/tag_list.py +++ b/service/test/functional/features/steps/tag_list.py @@ -21,10 +21,25 @@ def click_first_element_with_class(context, classname): elements[0].click() +def is_side_nax_expanded(context): + e = context.browser.find_elements_by_class_name('content')[0].get_attribute('class').count(u'move-right') == 1 + return e + + +def expand_side_nav(context): + if is_side_nax_expanded(context): + return + + toggle = context.browser.find_elements_by_class_name('side-nav-toggle')[0] + toggle.click() + + @when('I select the tag \'{tag}\'') def impl(context, tag): wait_for_user_alert_to_disapear(context) - click_first_element_with_class(context, 'fake-left-off-canvas-toggle') - context.browser.execute_script("window.scrollBy(0, -200)") + expand_side_nav(context) + + wait_until_element_is_visible_by_locator(context, (By.ID, 'tag-%s' % tag), 20) + e = find_element_by_id(context, 'tag-%s' % tag.lower()) e.click() diff --git a/service/test/functional/features/tag_and_reply.feature b/service/test/functional/features/tag_and_reply.feature index 8fe4cf84..450cb92d 100644 --- a/service/test/functional/features/tag_and_reply.feature +++ b/service/test/functional/features/tag_and_reply.feature @@ -14,14 +14,17 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -Feature: tagging and replying +Feature: Tag and reply + As a user of Pixelated + I want to tag my emails + So that I can easily find them Scenario: User tags a mail, replies to it then checks that mail is in the right tag Given I have a mail in my inbox When I open the first mail in the 'inbox' When I add the tag 'website' to that mail Then I see that mail under the 'website' tag - And I open the mail I previously tagged + When I open the mail I previously tagged And I reply to it When I select the tag 'sent' Then I see the mail I sent diff --git a/service/test/integration/test_contacts.py b/service/test/integration/test_contacts.py index 3a510346..f9cde9e5 100644 --- a/service/test/integration/test_contacts.py +++ b/service/test/integration/test_contacts.py @@ -22,7 +22,7 @@ class ContactsTest(SoledadTestBase): def test_TO_CC_and_BCC_fields_are_being_searched(self): input_mail = MailBuilder().with_tags(['important']).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) d = self.get_contacts(query='recipient') @@ -35,7 +35,7 @@ class ContactsTest(SoledadTestBase): def test_FROM_address_is_being_searched(self): input_mail = MailBuilder().with_tags(['important']).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) d = self.get_contacts(query='Sender') @@ -45,10 +45,10 @@ class ContactsTest(SoledadTestBase): return d def test_trash_and_drafts_mailboxes_are_being_ignored(self): - self.client.add_multiple_to_mailbox(1, mailbox='INBOX', to='recipient@inbox.com') - self.client.add_multiple_to_mailbox(1, mailbox='DRAFTS', to='recipient@drafts.com') - self.client.add_multiple_to_mailbox(1, mailbox='SENT', to='recipient@sent.com') - self.client.add_multiple_to_mailbox(1, mailbox='TRASH', to='recipient@trash.com') + self.add_multiple_to_mailbox(1, mailbox='INBOX', to='recipient@inbox.com') + self.add_multiple_to_mailbox(1, mailbox='DRAFTS', to='recipient@drafts.com') + self.add_multiple_to_mailbox(1, mailbox='SENT', to='recipient@sent.com') + self.add_multiple_to_mailbox(1, mailbox='TRASH', to='recipient@trash.com') d = self.get_contacts(query='recipient') @@ -69,8 +69,8 @@ class ContactsTest(SoledadTestBase): formatted_input_mail.with_bcc('Recipient Carbon <recipient@bcc.com>') formatted_input_mail = formatted_input_mail.build_input_mail() - self.client.add_mail_to_inbox(input_mail) - self.client.add_mail_to_inbox(formatted_input_mail) + self.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(formatted_input_mail) d = self.get_contacts(query='Recipient') @@ -84,17 +84,17 @@ class ContactsTest(SoledadTestBase): def test_bounced_addresses_are_ignored(self): to_be_bounced = MailBuilder().with_to('this_mail_was_bounced@domain.com').build_input_mail() - self.client.add_mail_to_inbox(to_be_bounced) + self.add_mail_to_inbox(to_be_bounced) bounced_mail_template = MailBuilder().build_input_mail() - bounced_mail = self.client.mailboxes.inbox().add(bounced_mail_template) + bounced_mail = self.mailboxes.inbox().add(bounced_mail_template) bounced_mail.hdoc.content = self._bounced_mail_hdoc_content() bounced_mail.save() - self.client.search_engine.index_mail(bounced_mail) + self.search_engine.index_mail(bounced_mail) not_bounced_mail = MailBuilder( ).with_tags(['important']).with_to('this_mail_was_not@bounced.com').build_input_mail() - self.client.add_mail_to_inbox(not_bounced_mail) + self.add_mail_to_inbox(not_bounced_mail) d = self.get_contacts(query='this') diff --git a/service/test/integration/test_delete_mail.py b/service/test/integration/test_delete_mail.py index 91dc0e9e..987cf307 100644 --- a/service/test/integration/test_delete_mail.py +++ b/service/test/integration/test_delete_mail.py @@ -14,14 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from test.support.integration import * +from test.support.integration import SoledadTestBase, MailBuilder class DeleteMailTest(SoledadTestBase): def test_move_mail_to_trash_when_deleting(self): input_mail = MailBuilder().with_subject('Mail with tags').build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) inbox_mails = self.get_mails_by_tag('inbox') self.assertEquals(1, len(inbox_mails)) @@ -34,7 +34,7 @@ class DeleteMailTest(SoledadTestBase): self.assertEquals(1, len(trash_mails)) def test_delete_mail_when_trashing_mail_from_trash_mailbox(self): - mails = self.client.add_multiple_to_mailbox(1, 'trash') + mails = self.add_multiple_to_mailbox(1, 'trash') self.delete_mails([mails[0].ident]) trash_mails = self.get_mails_by_tag('trash') @@ -42,7 +42,7 @@ class DeleteMailTest(SoledadTestBase): self.assertEqual(0, len(trash_mails)) def test_move_mail_to_trash_when_delete_multiple(self): - mails = self.client.add_multiple_to_mailbox(5, 'inbox') + mails = self.add_multiple_to_mailbox(5, 'inbox') mail_idents = [m.ident for m in mails] self.delete_mails(mail_idents) @@ -51,7 +51,7 @@ class DeleteMailTest(SoledadTestBase): self.assertEquals(0, len(inbox)) def test_delete_permanently_when_mails_are_in_trash(self): - mails = self.client.add_multiple_to_mailbox(5, 'trash') + mails = self.add_multiple_to_mailbox(5, 'trash') self.delete_mails([m.ident for m in mails]) trash = self.get_mails_by_tag('trash') diff --git a/service/test/integration/test_drafts.py b/service/test/integration/test_drafts.py index a5901b67..3a0f120b 100644 --- a/service/test/integration/test_drafts.py +++ b/service/test/integration/test_drafts.py @@ -14,8 +14,8 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from test.support.integration import * -from mockito import * +from test.support.integration import SoledadTestBase, MailBuilder +from mockito import unstub, when, any from twisted.internet.defer import Deferred @@ -27,7 +27,7 @@ class DraftsTest(SoledadTestBase): def test_post_sends_mail_and_deletes_previous_draft_if_it_exists(self): # act is if sending the mail by SMTP succeeded sendmail_deferred = Deferred() - when(self.client.mail_sender).sendmail(any()).thenReturn(sendmail_deferred) + when(self.mail_sender).sendmail(any()).thenReturn(sendmail_deferred) # creates one draft first_draft = MailBuilder().with_subject('First draft').build_json() @@ -56,7 +56,7 @@ class DraftsTest(SoledadTestBase): def test_post_sends_mail_even_when_draft_does_not_exist(self): # act is if sending the mail by SMTP succeeded sendmail_deferred = Deferred() - when(self.client.mail_sender).sendmail(any()).thenReturn(sendmail_deferred) + when(self.mail_sender).sendmail(any()).thenReturn(sendmail_deferred) first_draft = MailBuilder().with_subject('First draft').build_json() deferred_res = self.post_mail(first_draft) @@ -74,7 +74,7 @@ class DraftsTest(SoledadTestBase): return deferred_res def post_mail(self, data): - deferred_res, req = self.client.post('/mails', data) + deferred_res, req = self.post('/mails', data) deferred_res.callback(None) return deferred_res diff --git a/service/test/integration/test_mark_as_read_unread.py b/service/test/integration/test_mark_as_read_unread.py index cc09acec..6119f121 100644 --- a/service/test/integration/test_mark_as_read_unread.py +++ b/service/test/integration/test_mark_as_read_unread.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from test.support.integration import * +from test.support.integration import SoledadTestBase, MailBuilder from pixelated.adapter.model.status import Status @@ -22,7 +22,7 @@ class MarkAsReadUnreadTest(SoledadTestBase): def test_mark_single_as_read(self): input_mail = MailBuilder().build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) mails = self.get_mails_by_tag('inbox') self.assertNotIn('read', mails[0].status) @@ -34,7 +34,7 @@ class MarkAsReadUnreadTest(SoledadTestBase): def test_mark_single_as_unread(self): input_mail = MailBuilder().with_status([Status.SEEN]).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) self.mark_many_as_unread([input_mail.ident]) mail = self.get_mails_by_tag('inbox')[0] @@ -45,8 +45,8 @@ class MarkAsReadUnreadTest(SoledadTestBase): input_mail = MailBuilder().with_status([Status.SEEN]).build_input_mail() input_mail2 = MailBuilder().with_status([Status.SEEN]).build_input_mail() - self.client.add_mail_to_inbox(input_mail) - self.client.add_mail_to_inbox(input_mail2) + self.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail2) self.mark_many_as_unread([input_mail.ident, input_mail2.ident]) @@ -59,8 +59,8 @@ class MarkAsReadUnreadTest(SoledadTestBase): input_mail = MailBuilder().build_input_mail() input_mail2 = MailBuilder().build_input_mail() - self.client.add_mail_to_inbox(input_mail) - self.client.add_mail_to_inbox(input_mail2) + self.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail2) mails = self.get_mails_by_tag('inbox') @@ -79,8 +79,8 @@ class MarkAsReadUnreadTest(SoledadTestBase): input_mail = MailBuilder().build_input_mail() input_mail2 = MailBuilder().with_status([Status.SEEN]).build_input_mail() - self.client.add_mail_to_inbox(input_mail) - self.client.add_mail_to_inbox(input_mail2) + self.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail2) mails = self.get_mails_by_tag('inbox') diff --git a/service/test/integration/test_retrieve_attachment.py b/service/test/integration/test_retrieve_attachment.py index c81b684a..2c446b42 100644 --- a/service/test/integration/test_retrieve_attachment.py +++ b/service/test/integration/test_retrieve_attachment.py @@ -28,7 +28,7 @@ class RetrieveAttachmentTest(SoledadTestBase): 'phash': ident, 'content-type': 'text/plain; charset=US-ASCII; name="attachment_pequeno.txt"'} - self.client.add_document_to_soledad(attachment_dict) + self.add_document_to_soledad(attachment_dict) d = self.get_attachment(ident, 'base64') diff --git a/service/test/integration/test_search.py b/service/test/integration/test_search.py index 1de45967..f90ed80f 100644 --- a/service/test/integration/test_search.py +++ b/service/test/integration/test_search.py @@ -14,14 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from test.support.integration import * +from test.support.integration import SoledadTestBase, MailBuilder class SearchTest(SoledadTestBase): def test_that_tags_returns_all_tags(self): input_mail = MailBuilder().with_tags(['important']).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) d = self.get_tags() @@ -37,7 +37,7 @@ class SearchTest(SoledadTestBase): def test_that_tags_are_filtered_by_query(self): input_mail = MailBuilder().with_tags(['ateu', 'catoa', 'luat', 'zuado']).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) d = self.get_tags(q=["at"], skipDefaultTags=["true"]) @@ -53,7 +53,7 @@ class SearchTest(SoledadTestBase): def test_tags_with_multiple_words_are_searchable(self): input_mail = MailBuilder().with_tags(['one tag four words']).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) first_page = self.get_mails_by_tag('"one tag four words"', page=1, window=1) @@ -61,7 +61,7 @@ class SearchTest(SoledadTestBase): def test_that_default_tags_are_ignorable(self): input_mail = MailBuilder().with_tags(['sometag']).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) d = self.get_tags(skipDefaultTags=["true"]) @@ -73,10 +73,10 @@ class SearchTest(SoledadTestBase): return d def test_tags_count(self): - self.client.add_multiple_to_mailbox(num=10, mailbox='inbox', flags=['\\Recent']) - self.client.add_multiple_to_mailbox(num=5, mailbox='inbox', flags=['\\Seen']) - self.client.add_multiple_to_mailbox(num=3, mailbox='inbox', flags=['\\Recent'], tags=['important', 'later']) - self.client.add_multiple_to_mailbox(num=1, mailbox='inbox', flags=['\\Seen'], tags=['important']) + self.add_multiple_to_mailbox(num=10, mailbox='inbox', flags=['\\Recent']) + self.add_multiple_to_mailbox(num=5, mailbox='inbox', flags=['\\Seen']) + self.add_multiple_to_mailbox(num=3, mailbox='inbox', flags=['\\Recent'], tags=['important', 'later']) + self.add_multiple_to_mailbox(num=1, mailbox='inbox', flags=['\\Seen'], tags=['important']) d = self.get_tags() @@ -91,8 +91,8 @@ class SearchTest(SoledadTestBase): def test_search_mails_different_window(self): input_mail = MailBuilder().build_input_mail() input_mail2 = MailBuilder().build_input_mail() - self.client.add_mail_to_inbox(input_mail) - self.client.add_mail_to_inbox(input_mail2) + self.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail2) first_page = self.get_mails_by_tag('inbox', page=1, window=1) @@ -101,8 +101,8 @@ class SearchTest(SoledadTestBase): def test_search_mails_with_multiple_pages(self): input_mail = MailBuilder().build_input_mail() input_mail2 = MailBuilder().build_input_mail() - self.client.add_mail_to_inbox(input_mail) - self.client.add_mail_to_inbox(input_mail2) + self.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail2) first_page = self.get_mails_by_tag('inbox', page=1, window=1) second_page = self.get_mails_by_tag('inbox', page=2, window=1) @@ -114,7 +114,7 @@ class SearchTest(SoledadTestBase): def test_page_zero_fetches_first_page(self): input_mail = MailBuilder().build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) page = self.get_mails_by_tag('inbox', page=0, window=1) self.assertEqual(page[0].ident, input_mail.ident) @@ -127,8 +127,8 @@ class SearchTest(SoledadTestBase): input_mail = MailBuilder().with_date('2014-10-15T15:15').build_input_mail() input_mail2 = MailBuilder().with_date('2014-10-15T15:16').build_input_mail() - self.client.add_mail_to_inbox(input_mail) - self.client.add_mail_to_inbox(input_mail2) + self.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail2) results = self.get_mails_by_tag('inbox') self.assertEqual(results[0].ident, input_mail2.ident) @@ -137,7 +137,7 @@ class SearchTest(SoledadTestBase): def test_search_base64_body(self): body = u'bl\xe1' input_mail = MailBuilder().with_body(body.encode('utf-8')).build_input_mail() - self.client.add_mail_to_inbox(input_mail) + self.add_mail_to_inbox(input_mail) results = self.search(body) self.assertGreater(len(results), 0, 'No results returned from search') diff --git a/service/test/integration/test_soledad_querier.py b/service/test/integration/test_soledad_querier.py index 9c7f8a81..f4c23961 100644 --- a/service/test/integration/test_soledad_querier.py +++ b/service/test/integration/test_soledad_querier.py @@ -17,7 +17,7 @@ import copy import time -from test.support.integration import * +from test.support.integration import SoledadTestBase, MailBuilder from leap.mail.imap.fields import WithMsgFields @@ -25,9 +25,7 @@ class SoledadQuerierTest(SoledadTestBase, WithMsgFields): def setUp(self): SoledadTestBase.setUp(self) - self.soledad = self.client.soledad self.maxDiff = None - self.soledad_querier = self.client.soledad_querier def _get_empty_mailbox(self): return copy.deepcopy(self.EMPTY_MBOX) @@ -42,7 +40,7 @@ class SoledadQuerierTest(SoledadTestBase, WithMsgFields): return [m for m in self.soledad.get_from_index('by-type', 'mbox') if m.content['mbox'] == mailbox_name] def test_remove_dup_mailboxes_keeps_the_one_with_the_highest_last_uid(self): - self.client.add_multiple_to_mailbox(3, 'INBOX') # by now we already have one inbox with 3 mails + self.add_multiple_to_mailbox(3, 'INBOX') # by now we already have one inbox with 3 mails self._create_mailbox('INBOX') # now we have a duplicate # make sure we have two @@ -77,7 +75,7 @@ class SoledadQuerierTest(SoledadTestBase, WithMsgFields): self.assertEqual(1, len(mails)) def test_get_mails_by_chash(self): - mails = self.client.add_multiple_to_mailbox(3, 'INBOX') + mails = self.add_multiple_to_mailbox(3, 'INBOX') chashes = [mail.ident for mail in mails] fetched_mails = self.soledad_querier.mails(chashes) diff --git a/service/test/integration/test_tags.py b/service/test/integration/test_tags.py index ad723067..976b6d96 100644 --- a/service/test/integration/test_tags.py +++ b/service/test/integration/test_tags.py @@ -15,8 +15,8 @@ # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. import json -from test.support.integration import * -from pixelated.adapter.services.tag_service import TagService +from test.support.integration import SoledadTestBase, MailBuilder +from pixelated.adapter.services.tag_service import SPECIAL_TAGS class TagsTest(SoledadTestBase): @@ -26,7 +26,7 @@ class TagsTest(SoledadTestBase): def test_add_tag_to_an_inbox_mail_and_query(self): mail = MailBuilder().with_subject('Mail with tags').build_input_mail() - self.client.add_mail_to_inbox(mail) + self.add_mail_to_inbox(mail) self.post_tags(mail.ident, self._tags_json(['IMPORTANT'])) @@ -38,13 +38,13 @@ class TagsTest(SoledadTestBase): def test_use_old_casing_when_same_tag_with_different_casing_is_posted(self): mail = MailBuilder().with_subject('Mail with tags').build_input_mail() - self.client.add_mail_to_inbox(mail) + self.add_mail_to_inbox(mail) self.post_tags(mail.ident, self._tags_json(['ImPoRtAnT'])) mails = self.get_mails_by_tag('ImPoRtAnT') self.assertEquals({'ImPoRtAnT'}, set(mails[0].tags)) another_mail = MailBuilder().with_subject('Mail with tags').build_input_mail() - self.client.add_mail_to_inbox(another_mail) + self.add_mail_to_inbox(another_mail) self.post_tags(another_mail.ident, self._tags_json(['IMPORTANT'])) mails = self.get_mails_by_tag('IMPORTANT') self.assertEquals(0, len(mails)) @@ -55,7 +55,7 @@ class TagsTest(SoledadTestBase): def test_tags_are_case_sensitive(self): mail = MailBuilder().with_subject('Mail with tags').build_input_mail() - self.client.add_mail_to_inbox(mail) + self.add_mail_to_inbox(mail) self.post_tags(mail.ident, self._tags_json(['ImPoRtAnT'])) @@ -70,7 +70,7 @@ class TagsTest(SoledadTestBase): def test_empty_tags_are_not_allowed(self): mail = MailBuilder().with_subject('Mail with tags').build_input_mail() - self.client.add_mail_to_inbox(mail) + self.add_mail_to_inbox(mail) self.post_tags(mail.ident, self._tags_json(['tag1', ' '])) @@ -80,11 +80,11 @@ class TagsTest(SoledadTestBase): def test_addition_of_reserved_tags_is_not_allowed(self): mail = MailBuilder().with_subject('Mail with tags').build_input_mail() - self.client.add_mail_to_inbox(mail) + self.add_mail_to_inbox(mail) - for tag in TagService.SPECIAL_TAGS: + for tag in SPECIAL_TAGS: response = self.post_tags(mail.ident, self._tags_json([tag.name.upper()])) self.assertEquals("None of the following words can be used as tags: %s" % tag.name, response) - mail = self.client.mailboxes.inbox().mail(mail.ident) + mail = self.mailboxes.inbox().mail(mail.ident) self.assertNotIn('drafts', mail.tags) diff --git a/service/test/perf/search/test_Search.py b/service/test/perf/search/test_Search.py index 63636789..5e646edd 100644 --- a/service/test/perf/search/test_Search.py +++ b/service/test/perf/search/test_Search.py @@ -14,24 +14,25 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. import unittest -import json from funkload.FunkLoadTestCase import FunkLoadTestCase from funkload.utils import Data from test.support.integration import AppTestClient +CLIENT = AppTestClient() + + class Search(FunkLoadTestCase): def setUpBench(self): - client = AppTestClient() # setup data - client.add_multiple_to_mailbox(10, 'INBOX', to='to@inbox.com', cc='cc@inbox.com', bcc='bcc@inbox.com', tags=['inbox']) - client.add_multiple_to_mailbox(10, 'TRASH', to='to@trash.com', cc='cc@trash.com', bcc='bcc@trash.com', tags=['trash']) - client.add_multiple_to_mailbox(10, 'DRAFTS', to='to@drafts.com', cc='cc@drafts.com', bcc='bcc@drafts.com', tags=['drafts']) + CLIENT.add_multiple_to_mailbox(10, 'INBOX', to='to@inbox.com', cc='cc@inbox.com', bcc='bcc@inbox.com', tags=['inbox']) + CLIENT.add_multiple_to_mailbox(10, 'TRASH', to='to@trash.com', cc='cc@trash.com', bcc='bcc@trash.com', tags=['trash']) + CLIENT.add_multiple_to_mailbox(10, 'DRAFTS', to='to@drafts.com', cc='cc@drafts.com', bcc='bcc@drafts.com', tags=['drafts']) - self.call_to_terminate = client.run_on_a_thread(logfile='results/app.log') + self.call_to_terminate = CLIENT.run_on_a_thread(logfile='results/app.log') def tearDownBench(self): self.call_to_terminate() @@ -42,7 +43,7 @@ class Search(FunkLoadTestCase): self.mails_by_tag_url = self.server_url + '/mails?q=%%22tag:%s%%22&w=25&p=0' def idents_by_tag(self, tag): - return list(mail['ident'] for mail in json.loads(self.get(self.mails_by_tag_url % tag, description='Query mails by tag').body)['mails']) + return [mail.ident for mail in CLIENT.get_mails_by_tag(tag)] def test_search(self): """ Query contacts and tags. Write a new tag, updating index. Query again. """ diff --git a/service/test/support/integration/app_test_client.py b/service/test/support/integration/app_test_client.py index 474e5fd3..5e52732b 100644 --- a/service/test/support/integration/app_test_client.py +++ b/service/test/support/integration/app_test_client.py @@ -35,20 +35,23 @@ from pixelated.adapter.services.draft_service import DraftService from pixelated.adapter.services.mail_service import MailService from pixelated.adapter.services.mailboxes import Mailboxes from pixelated.adapter.soledad.soledad_querier import SoledadQuerier -from pixelated.adapter.services.tag_service import TagService from pixelated.config import App from pixelated.resources.root_resource import RootResource from test.support.integration.model import MailBuilder from test.support.test_helper import request_mock +from test.support.integration.model import ResponseMail -class AppTestClient: +class AppTestClient(object): INDEX_KEY = '\xde3?\x87\xff\xd9\xd3\x14\xf0\xa7>\x1f%C{\x16.\\\xae\x8c\x13\xa7\xfb\x04\xd4]+\x8d_\xed\xd1\x8d\x0bI' \ '\x8a\x0e\xa4tm\xab\xbf\xb4\xa5\x99\x00d\xd5w\x9f\x18\xbc\x1d\xd4_W\xd2\xb6\xe8H\x83\x1b\xd8\x9d\xad' ACCOUNT = 'test' MAIL_ADDRESS = 'test@pixelated.org' def __init__(self): + self.start_client() + + def start_client(self): soledad_test_folder = self._generate_soledad_test_folder_name() SearchEngine.DEFAULT_INDEX_HOME = soledad_test_folder @@ -125,8 +128,9 @@ class AppTestClient: def add_mail_to_inbox(self, input_mail): mail = self.mailboxes.inbox().add(input_mail) - mail.update_tags(input_mail.tags) - self.search_engine.index_mail(mail) + if input_mail.tags: + mail.update_tags(input_mail.tags) + self.search_engine.index_mail(mail) def add_multiple_to_mailbox(self, num, mailbox='', flags=[], tags=[], to='recipient@to.com', cc='recipient@cc.com', bcc='recipient@bcc.com'): mails = [] @@ -134,8 +138,8 @@ class AppTestClient: input_mail = MailBuilder().with_status(flags).with_tags(tags).with_to(to).with_cc(cc).with_bcc(bcc).build_input_mail() mail = self.mailboxes._create_or_get(mailbox).add(input_mail) mails.append(mail) - mail.update_tags(input_mail.tags) - self.search_engine.index_mail(mail) + mail.update_tags(input_mail.tags) if tags else None + self.search_engine.index_mails(mails) if tags else None return mails def _create_soledad_querier(self, soledad, index_key): @@ -149,13 +153,64 @@ class AppTestClient: return mail_sender def _create_mail_service(self, mailboxes, mail_sender, soledad_querier, search_engine): - tag_service = TagService() - mail_service = MailService(mailboxes, mail_sender, tag_service, soledad_querier, search_engine) + mail_service = MailService(mailboxes, mail_sender, soledad_querier, search_engine) return mail_service def _generate_soledad_test_folder_name(self, soledad_test_folder='/tmp/soledad-test/test'): return os.path.join(soledad_test_folder, str(uuid.uuid4())) + def get_mails_by_tag(self, tag, page=1, window=100): + tags = 'tag:%s' % tag + return self.search(tags, page, window) + + def search(self, query, page=1, window=100): + res, req = self.get("/mails", { + 'q': [query], + 'w': [str(window)], + 'p': [str(page)] + }) + return [ResponseMail(m) for m in res['mails']] + + def get_attachment(self, ident, encoding): + res, req = self.get("/attachment/%s" % ident, {'encoding': [encoding]}, as_json=False) + return res + + def put_mail(self, data): + res, req = self.put('/mails', data) + return res, req + + def post_tags(self, mail_ident, tags_json): + res, req = self.post("/mail/%s/tags" % mail_ident, tags_json) + return res + + def get_tags(self, **kwargs): + res, req = self.get('/tags', kwargs) + return res + + def get_mail(self, mail_ident): + res, req = self.get('/mail/%s' % mail_ident) + return res + + def delete_mail(self, mail_ident): + res, req = self.delete("/mail/%s" % mail_ident) + return req + + def delete_mails(self, idents): + res, req = self.post("/mails/delete", json.dumps({'idents': idents})) + return req + + def mark_many_as_unread(self, idents): + res, req = self.post('/mails/unread', json.dumps({'idents': idents})) + return req + + def mark_many_as_read(self, idents): + res, req = self.post('/mails/read', json.dumps({'idents': idents})) + return req + + def get_contacts(self, query): + res, req = self.get('/contacts', get_args={'q': query}) + return res + def initialize_soledad(tempdir): if os.path.isdir(tempdir): diff --git a/service/test/support/integration/soledad_test_base.py b/service/test/support/integration/soledad_test_base.py index 2c8bb023..c49de00a 100644 --- a/service/test/support/integration/soledad_test_base.py +++ b/service/test/support/integration/soledad_test_base.py @@ -14,70 +14,16 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. from twisted.trial import unittest -from pixelated.resources import * from test.support.integration.app_test_client import AppTestClient -from test.support.integration.model import ResponseMail -class SoledadTestBase(unittest.TestCase): +class SoledadTestBase(unittest.TestCase, AppTestClient): # these are so long because our CI is so slow at the moment. DEFERRED_TIMEOUT = 120 DEFERRED_TIMEOUT_LONG = 300 def setUp(self): - self.client = AppTestClient() + self.start_client() def tearDown(self): - self.client.cleanup() - - def get_mails_by_tag(self, tag, page=1, window=100): - tags = 'tag:%s' % tag - return self.search(tags, page, window) - - def search(self, query, page=1, window=100): - res, req = self.client.get("/mails", { - 'q': [query], - 'w': [str(window)], - 'p': [str(page)] - }) - return [ResponseMail(m) for m in res['mails']] - - def get_attachment(self, ident, encoding): - res, req = self.client.get("/attachment/%s" % ident, {'encoding': [encoding]}, as_json=False) - return res - - def put_mail(self, data): - res, req = self.client.put('/mails', data) - return res, req - - def post_tags(self, mail_ident, tags_json): - res, req = self.client.post("/mail/%s/tags" % mail_ident, tags_json) - return res - - def get_tags(self, **kwargs): - res, req = self.client.get('/tags', kwargs) - return res - - def get_mail(self, mail_ident): - res, req = self.client.get('/mail/%s' % mail_ident) - return res - - def delete_mail(self, mail_ident): - res, req = self.client.delete("/mail/%s" % mail_ident) - return req - - def delete_mails(self, idents): - res, req = self.client.post("/mails/delete", json.dumps({'idents': idents})) - return req - - def mark_many_as_unread(self, idents): - res, req = self.client.post('/mails/unread', json.dumps({'idents': idents})) - return req - - def mark_many_as_read(self, idents): - res, req = self.client.post('/mails/read', json.dumps({'idents': idents})) - return req - - def get_contacts(self, query): - res, req = self.client.get('/contacts', get_args={'q': query}) - return res + self.cleanup() diff --git a/service/test/support/test_helper.py b/service/test/support/test_helper.py index 54685008..c37c1408 100644 --- a/service/test/support/test_helper.py +++ b/service/test/support/test_helper.py @@ -15,8 +15,9 @@ # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. from datetime import datetime import io +from twisted.web.test.test_web import DummyRequest -from pixelated.adapter.model.mail import InputMail +from pixelated.adapter.model.mail import InputMail, PixelatedMail LEAP_FLAGS = ['\\Seen', @@ -68,6 +69,12 @@ def leap_mail(uid=0, flags=LEAP_FLAGS, headers=None, extra_headers={}, mbox='INB return (fdoc, hdoc, bdoc) +def pixelated_mail(uid=0, flags=LEAP_FLAGS, headers=None, extra_headers={}, mbox='INBOX', body='body', chash='chash'): + fdoc, hdoc, bdoc = leap_mail(uid, flags, headers, extra_headers, mbox, body, chash) + + return PixelatedMail.from_soledad(fdoc, hdoc, bdoc) + + def input_mail(): mail = InputMail() mail.fdoc = TestDoc({}) @@ -82,9 +89,6 @@ class TestRequest: self.json = json -from twisted.web.test.test_web import DummyRequest - - class PixRequestMock(DummyRequest): def __init__(self, path): DummyRequest.__init__(self, path) diff --git a/service/test/unit/adapter/search/__init__.py b/service/test/unit/adapter/search/__init__.py new file mode 100644 index 00000000..2756a319 --- /dev/null +++ b/service/test/unit/adapter/search/__init__.py @@ -0,0 +1,15 @@ +# +# Copyright (c) 2014 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 <http://www.gnu.org/licenses/>. diff --git a/service/test/unit/adapter/search/test_search.py b/service/test/unit/adapter/search/test_search.py new file mode 100644 index 00000000..d57b8227 --- /dev/null +++ b/service/test/unit/adapter/search/test_search.py @@ -0,0 +1,89 @@ +# +# Copyright (c) 2014 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 <http://www.gnu.org/licenses/>. + + +import unittest +from mockito import mock, when +from pixelated.adapter.search import SearchEngine +from tempdir import TempDir +from test.support import test_helper + +INDEX_KEY = '\xde3?\x87\xff\xd9\xd3\x14\xf0\xa7>\x1f%C{\x16.\\\xae\x8c\x13\xa7\xfb\x04\xd4]+\x8d_\xed\xd1\x8d\x0bI' \ + '\x8a\x0e\xa4tm\xab\xbf\xb4\xa5\x99\x00d\xd5w\x9f\x18\xbc\x1d\xd4_W\xd2\xb6\xe8H\x83\x1b\xd8\x9d\xad' + + +class LockStub(object): + def __init__(self): + self.called = False + + def __enter__(self): + self.called = True + return self + + def __exit__(self, type, value, traceback): + return False + + +class SearchEngineTest(unittest.TestCase): + def setUp(self): + self.tempdir = TempDir() + self.agent_home = self.tempdir.name + + def tearDown(self): + self.tempdir.dissolve() + + def test_index_mail_secured_by_lock(self): + # given + soledad_querier = mock() + lock_stub = LockStub() + when(soledad_querier).get_index_masterkey().thenReturn(INDEX_KEY) + + self.assertEqual(INDEX_KEY, soledad_querier.get_index_masterkey()) + se = SearchEngine(soledad_querier, self.agent_home) + se._write_lock = lock_stub + + headers = { + 'From': 'from@bar.tld', + 'To': 'to@bar.tld', + 'Subject': 'Some test mail', + } + + # when + se.index_mail(test_helper.pixelated_mail(extra_headers=headers)) + + # then + self.assertTrue(lock_stub.called) + + def test_encoding(self): + # given + soledad_querier = mock() + when(soledad_querier).get_index_masterkey().thenReturn(INDEX_KEY) + + se = SearchEngine(soledad_querier, self.agent_home) + + headers = { + 'From': 'foo@bar.tld', + 'To': '=?utf-8?b?IsOEw7zDtiDDlsO8w6QiIDxmb2xrZXJAcGl4ZWxhdGVkLXByb2plY3Qub3Jn?=\n =?utf-8?b?PiwgRsO2bGtlciA8Zm9sa2VyQHBpeGVsYXRlZC1wcm9qZWN0Lm9yZz4=?=', + 'Cc': '=?utf-8?b?IsOEw7zDtiDDlsO8w6QiIDxmb2xrZXJAcGl4ZWxhdGVkLXByb2plY3Qub3Jn?=\n =?utf-8?b?PiwgRsO2bGtlciA8Zm9sa2VyQHBpeGVsYXRlZC1wcm9qZWN0Lm9yZz4=?=', + 'Subject': 'Some test mail', + } + + # when + se.index_mail(test_helper.pixelated_mail(extra_headers=headers, chash='mailid')) + + result = se.search('folker') + + self.assertEqual((['mailid'], 1), result) diff --git a/service/test/unit/adapter/test_draft_service.py b/service/test/unit/adapter/test_draft_service.py index baa07ce0..0dd6cd2a 100644 --- a/service/test/unit/adapter/test_draft_service.py +++ b/service/test/unit/adapter/test_draft_service.py @@ -3,7 +3,7 @@ import unittest from pixelated.adapter.model.mail import InputMail from pixelated.adapter.services.draft_service import DraftService import test.support.test_helper as test_helper -from mockito import * +from mockito import mock, verify, inorder, when class DraftServiceTest(unittest.TestCase): diff --git a/service/test/unit/adapter/test_email_recepient_normalizer.py b/service/test/unit/adapter/test_email_recepient_normalizer.py deleted file mode 100644 index 79d50273..00000000 --- a/service/test/unit/adapter/test_email_recepient_normalizer.py +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright (c) 2014 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 <http://www.gnu.org/licenses/>. -import unittest - -from pixelated.adapter.model.mail import PixelatedMail -from pixelated.adapter.services.mailbox import Mailbox -from pixelated.adapter.services.mail_sender import MailSender -from mockito import * -from test.support import test_helper - - -class PixelatedDuplicateEmailTest(unittest.TestCase): - def setUp(self): - self.mail_sender = MailSender(self, "random@gmail.com") - - def test_remove_duplicate_mail_recepients(self): - mail_list = ['simba@gmail.com', 'simba@gmail.com', 'fabio@gmail.com'] - normalized_recepients = self.mail_sender.recepients_normalizer(mail_list) - self.assertEquals(normalized_recepients, set(['simba@gmail.com', 'fabio@gmail.com'])) - - def test_get_email_addresses(self): - mail_list = ['simbarashe<simba@gmail.com>', 'vic@gmail.com', 'Fabio<fabio@gmail.com>', 'slick@gmail.com'] - selected_recepients = self.mail_sender.get_email_addresses(mail_list) - self.assertEquals(selected_recepients, set(['simba@gmail.com', 'vic@gmail.com', 'fabio@gmail.com', 'slick@gmail.com'])) - - def test_remove_duplicate_emails_with_routing_format(self): - mail_list = ['simbarashe<simba@gmail.com>', 'simba<simba@gmail.com>', 'Fabio<fabio@gmail.com>', 'Fabinho<fabio@gmail.com>'] - selected_recepients = self.mail_sender.get_email_addresses(mail_list) - self.assertEquals(selected_recepients, set(['simba@gmail.com', 'fabio@gmail.com'])) diff --git a/service/test/unit/adapter/test_mail.py b/service/test/unit/adapter/test_mail.py index 54c421c7..c7910b7f 100644 --- a/service/test/unit/adapter/test_mail.py +++ b/service/test/unit/adapter/test_mail.py @@ -17,7 +17,7 @@ import unittest import pixelated.support.date from pixelated.adapter.model.mail import PixelatedMail, InputMail -from mockito import * +from mockito import mock, unstub, when from test.support import test_helper import dateutil.parser as dateparser import base64 @@ -31,6 +31,9 @@ class TestPixelatedMail(unittest.TestCase): def setUp(self): self.querier = mock() + def tearDown(self): + unstub() + def test_parse_date_from_soledad_uses_date_header_if_available(self): leap_mail_date = 'Wed, 3 Sep 2014 12:36:17 -0300' leap_mail_date_in_iso_format = "2014-09-03T12:36:17-03:00" @@ -52,6 +55,17 @@ class TestPixelatedMail(unittest.TestCase): self.assertEqual(str(mail.headers['Date']), leap_mail_date_in_iso_format) + def test_parse_date_from_soledad_fallback_to_now_if_neither_date_nor_received_header(self): + leap_mail_date_in_iso_format = "2014-09-03T13:11:15-03:00" + + when(pixelated.support.date).iso_now().thenReturn(leap_mail_date_in_iso_format) + fdoc, hdoc, bdoc = test_helper.leap_mail() + del hdoc.content['date'] + + mail = PixelatedMail.from_soledad(fdoc, hdoc, bdoc, soledad_querier=self.querier) + + self.assertEqual(str(mail.headers['Date']), leap_mail_date_in_iso_format) + def test_update_tags_return_a_set_with_the_current_tags(self): soledad_docs = test_helper.leap_mail(extra_headers={'X-tags': '["custom_1", "custom_2"]'}) pixelated_mail = PixelatedMail.from_soledad(*soledad_docs, soledad_querier=self.querier) @@ -174,9 +188,20 @@ class TestPixelatedMail(unittest.TestCase): self.assertRegexpMatches(mail.html_body, '([\s\S]*100%)') def test_content_type_header_of_mail_part_is_used(self): - plain_headers = {'Content-Type': 'text/plain; charset=utf-8', 'Content-Transfer-Encoding': 'quoted-printable'} + plain_headers = {'Content-Type': 'text/plain; charset=iso-8859-1', 'Content-Transfer-Encoding': 'quoted-printable'} html_headers = {'Content-Type': 'text/html; charset=utf-8', 'Content-Transfer-Encoding': 'quoted-printable'} - parts = {'alternatives': [{'content': 'H=C3=A4llo', 'headers': plain_headers}, {'content': '<p>H=C3=A4llo</p>', 'headers': html_headers}]} + parts = {'alternatives': [{'content': 'H=E4llo', 'headers': plain_headers}, {'content': '<p>H=C3=A4llo</p>', 'headers': html_headers}]} + + mail = PixelatedMail.from_soledad(None, None, self._create_bdoc(raw='some raw body'), parts=parts, soledad_querier=None) + + self.assertEqual(2, len(mail.alternatives)) + self.assertEquals(u'H\xe4llo', mail.text_plain_body) + self.assertEquals(u'<p>H\xe4llo</p>', mail.html_body) + + def test_multi_line_content_type_header_is_supported(self): + plain_headers = {'Content-Type': 'text/plain;\ncharset=iso-8859-1', 'Content-Transfer-Encoding': 'quoted-printable'} + html_headers = {'Content-Type': 'text/html;\ncharset=utf-8', 'Content-Transfer-Encoding': 'quoted-printable'} + parts = {'alternatives': [{'content': 'H=E4llo', 'headers': plain_headers}, {'content': '<p>H=C3=A4llo</p>', 'headers': html_headers}]} mail = PixelatedMail.from_soledad(None, None, self._create_bdoc(raw='some raw body'), parts=parts, soledad_querier=None) @@ -211,6 +236,28 @@ class TestPixelatedMail(unittest.TestCase): self.assertEquals(body, mail.text_plain_body) + def test_that_body_understands_7bit(self): + body = u'testtext' + encoded_body = body + + fdoc, hdoc, bdoc = test_helper.leap_mail() + parts = {'alternatives': []} + parts['alternatives'].append({'content': encoded_body, 'headers': {'Content-Transfer-Encoding': '7bit'}}) + mail = PixelatedMail.from_soledad(fdoc, hdoc, bdoc, soledad_querier=self.querier, parts=parts) + + self.assertEquals(body, mail.text_plain_body) + + def test_that_body_understands_8bit(self): + body = u'testtext' + encoded_body = body + + fdoc, hdoc, bdoc = test_helper.leap_mail() + parts = {'alternatives': []} + parts['alternatives'].append({'content': encoded_body, 'headers': {'Content-Transfer-Encoding': '8bit'}}) + mail = PixelatedMail.from_soledad(fdoc, hdoc, bdoc, soledad_querier=self.querier, parts=parts) + + self.assertEquals(body, mail.text_plain_body) + def test_bounced_mails_are_recognized(self): bounced_mail_hdoc = os.path.join(os.path.dirname(__file__), '..', 'fixtures', 'bounced_mail_hdoc.json') with open(bounced_mail_hdoc) as f: @@ -256,9 +303,40 @@ class TestPixelatedMail(unittest.TestCase): self.content = {'raw': raw} return FakeBDoc(raw) + def test_encoding_special_character_on_header(self): + subject = "=?UTF-8?Q?test_encoding_St=C3=A4ch?=" + email_from = "=?UTF-8?Q?St=C3=A4ch_<stach@pixelated-project.org>?=" + email_to = "=?utf-8?b?IsOEw7zDtiDDlsO8w6QiIDxmb2xrZXJAcGl4ZWxhdGVkLXByb2plY3Qub3Jn?=\n =?utf-8?b?PiwgRsO2bGtlciA8Zm9sa2VyQHBpeGVsYXRlZC1wcm9qZWN0Lm9yZz4=?=" -class InputMailTest(unittest.TestCase): - mail_dict = lambda x: { + pixel_mail = PixelatedMail() + + self.assertEqual(pixel_mail._decode_header(subject), 'test encoding St\xc3\xa4ch') + self.assertEqual(pixel_mail._decode_header(email_from), 'St\xc3\xa4ch <stach@pixelated-project.org>') + self.assertEqual(pixel_mail._decode_header(email_to), '"\xc3\x84\xc3\xbc\xc3\xb6 \xc3\x96\xc3\xbc\xc3\xa4" <folker@pixelated-project.org>, F\xc3\xb6lker <folker@pixelated-project.org>') + self.assertEqual(pixel_mail._decode_header(None), None) + + def test_headers_are_encoded_right(self): + subject = "=?UTF-8?Q?test_encoding_St=C3=A4ch?=" + email_from = "=?UTF-8?Q?St=C3=A4ch_<stach@pixelated-project.org>?=" + email_to = "=?utf-8?b?IsOEw7zDtiDDlsO8w6QiIDxmb2xrZXJAcGl4ZWxhdGVkLXByb2plY3Qub3Jn?=\n =?utf-8?b?PiwgRsO2bGtlciA8Zm9sa2VyQHBpeGVsYXRlZC1wcm9qZWN0Lm9yZz4=?=" + email_cc = "=?UTF-8?Q?St=C3=A4ch_<stach@pixelated-project.org>?=" + email_bcc = "=?UTF-8?Q?St=C3=A4ch_<stach@pixelated-project.org>?=" + + leap_mail = test_helper.leap_mail(extra_headers={'Subject': subject, 'From': email_from, 'To': email_to, 'Cc': email_cc, 'Bcc': email_bcc}) + + mail = PixelatedMail.from_soledad(*leap_mail, soledad_querier=self.querier) + + self.assertEqual(str(mail.headers['Subject']), 'test encoding St\xc3\xa4ch') + self.assertEqual(str(mail.headers['From']), 'St\xc3\xa4ch <stach@pixelated-project.org>') + self.assertEqual(mail.headers['To'], ['"\xc3\x84\xc3\xbc\xc3\xb6 \xc3\x96\xc3\xbc\xc3\xa4" <folker@pixelated-project.org>', 'F\xc3\xb6lker <folker@pixelated-project.org>']) + self.assertEqual(mail.headers['Cc'], ['St\xc3\xa4ch <stach@pixelated-project.org>']) + self.assertEqual(mail.headers['Bcc'], ['St\xc3\xa4ch <stach@pixelated-project.org>']) + + mail.as_dict() + + +def simple_mail_dict(): + return { 'body': 'Este \xe9 o corpo', 'header': { 'cc': ['cc@pixelated.org', 'anothercc@pixelated.org'], @@ -270,7 +348,9 @@ class InputMailTest(unittest.TestCase): 'tags': ['sent'] } - multipart_mail_dict = lambda x: { + +def multipart_mail_dict(): + return { 'body': [{'content-type': 'plain', 'raw': 'Hello world!'}, {'content-type': 'html', 'raw': '<p>Hello html world!</p>'}], 'header': { @@ -283,10 +363,13 @@ class InputMailTest(unittest.TestCase): 'tags': ['sent'] } + +class InputMailTest(unittest.TestCase): + def test_to_mime_multipart_should_add_blank_fields(self): pixelated.support.date.iso_now = lambda: 'date now' - mail_dict = self.mail_dict() + mail_dict = simple_mail_dict() mail_dict['header']['to'] = '' mail_dict['header']['bcc'] = '' mail_dict['header']['cc'] = '' @@ -302,24 +385,24 @@ class InputMailTest(unittest.TestCase): def test_to_mime_multipart(self): pixelated.support.date.iso_now = lambda: 'date now' - mime_multipart = InputMail.from_dict(self.mail_dict()).to_mime_multipart() + mime_multipart = InputMail.from_dict(simple_mail_dict()).to_mime_multipart() self.assertRegexpMatches(mime_multipart.as_string(), "\nTo: to@pixelated.org, anotherto@pixelated.org\n") self.assertRegexpMatches(mime_multipart.as_string(), "\nCc: cc@pixelated.org, anothercc@pixelated.org\n") self.assertRegexpMatches(mime_multipart.as_string(), "\nBcc: bcc@pixelated.org, anotherbcc@pixelated.org\n") self.assertRegexpMatches(mime_multipart.as_string(), "\nDate: date now\n") self.assertRegexpMatches(mime_multipart.as_string(), "\nSubject: Oi\n") - self.assertRegexpMatches(mime_multipart.as_string(), base64.b64encode(self.mail_dict()['body'])) + self.assertRegexpMatches(mime_multipart.as_string(), base64.b64encode(simple_mail_dict()['body'])) def test_smtp_format(self): InputMail.FROM_EMAIL_ADDRESS = 'pixelated@org' - smtp_format = InputMail.from_dict(self.mail_dict()).to_smtp_format() + smtp_format = InputMail.from_dict(simple_mail_dict()).to_smtp_format() self.assertRegexpMatches(smtp_format, "\nFrom: pixelated@org") def test_to_mime_multipart_handles_alternative_bodies(self): - mime_multipart = InputMail.from_dict(self.multipart_mail_dict()).to_mime_multipart() + mime_multipart = InputMail.from_dict(multipart_mail_dict()).to_mime_multipart() part_one = 'Content-Type: text/plain; charset="us-ascii"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\n\nHello world!' part_two = 'Content-Type: text/html; charset="us-ascii"\nMIME-Version: 1.0\nContent-Transfer-Encoding: 7bit\n\n<p>Hello html world!</p>' diff --git a/service/test/unit/adapter/test_mail_service.py b/service/test/unit/adapter/test_mail_service.py index 98ead126..34fec708 100644 --- a/service/test/unit/adapter/test_mail_service.py +++ b/service/test/unit/adapter/test_mail_service.py @@ -18,25 +18,22 @@ from pixelated.adapter.model.mail import InputMail, PixelatedMail from pixelated.adapter.services.mail_service import MailService from test.support.test_helper import mail_dict, leap_mail -from mockito import * +from mockito import mock, unstub, when, verify, verifyNoMoreInteractions, any from twisted.internet.defer import Deferred -from twisted.internet import defer - class TestMailService(unittest.TestCase): def setUp(self): self.drafts = mock() self.querier = mock() self.mailboxes = mock() - self.tag_service = mock() self.mailboxes.drafts = lambda: self.drafts self.mailboxes.trash = lambda: mock() self.mailboxes.sent = lambda: mock() self.mail_sender = mock() self.search_engine = mock() - self.mail_service = MailService(self.mailboxes, self.mail_sender, self.tag_service, self.querier, self.search_engine) + self.mail_service = MailService(self.mailboxes, self.mail_sender, self.querier, self.search_engine) def tearDown(self): unstub() diff --git a/service/test/unit/adapter/test_mailbox.py b/service/test/unit/adapter/test_mailbox.py index b44f507b..ed634648 100644 --- a/service/test/unit/adapter/test_mailbox.py +++ b/service/test/unit/adapter/test_mailbox.py @@ -17,13 +17,12 @@ import unittest from pixelated.adapter.model.mail import PixelatedMail from pixelated.adapter.services.mailbox import Mailbox -from mockito import * +from mockito import mock, when, verify from test.support import test_helper class PixelatedMailboxTest(unittest.TestCase): def setUp(self): - self.tag_service = mock() self.querier = mock() self.search_engine = mock() self.mailbox = Mailbox('INBOX', self.querier, self.search_engine) @@ -35,3 +34,9 @@ class PixelatedMailboxTest(unittest.TestCase): self.mailbox.remove(1) verify(self.querier).remove_mail(mail) + + def test_fresh_mailbox_checking_lastuid(self): + when(self.querier).get_lastuid('INBOX').thenReturn(0) + self.assertTrue(self.mailbox.fresh) + when(self.querier).get_lastuid('INBOX').thenReturn(1) + self.assertFalse(self.mailbox.fresh) diff --git a/service/test/unit/adapter/test_mailbox_indexer_listener.py b/service/test/unit/adapter/test_mailbox_indexer_listener.py index 65ba8966..71c9cd15 100644 --- a/service/test/unit/adapter/test_mailbox_indexer_listener.py +++ b/service/test/unit/adapter/test_mailbox_indexer_listener.py @@ -15,7 +15,7 @@ # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. import unittest -from mockito import * +from mockito import mock, when, verify from pixelated.adapter.listeners.mailbox_indexer_listener import MailboxIndexerListener diff --git a/service/test/unit/adapter/test_soledad_querier.py b/service/test/unit/adapter/test_soledad_querier.py index 2cc23750..e5ea457d 100644 --- a/service/test/unit/adapter/test_soledad_querier.py +++ b/service/test/unit/adapter/test_soledad_querier.py @@ -104,3 +104,47 @@ class SoledadQuerierTest(unittest.TestCase): attachment = querier.attachment(u'0400BEBACAFE', 'quoted-printable') self.assertEquals('esse papo seu ta qualquer coisa', attachment['content']) + + def test_empty_or_null_queries_are_ignored(self): + soledad = mock() + when(soledad).get_from_index(any(), any(), any()).thenReturn(['nonempty', 'list']) + querier = SoledadQuerier(soledad) + + test_parameters = ['', None] + + def call_with_bad_parameters(funct): + for param in test_parameters: + self.assertFalse(funct(param)) + + call_with_bad_parameters(querier.get_all_flags_by_mbox) + call_with_bad_parameters(querier.get_content_by_phash) + call_with_bad_parameters(querier.get_flags_by_chash) + call_with_bad_parameters(querier.get_header_by_chash) + call_with_bad_parameters(querier.get_recent_by_mbox) + call_with_bad_parameters(querier.idents_by_mailbox) + call_with_bad_parameters(querier.get_mbox) + + def test_get_lastuid(self): + soledad = mock() + mbox = mock() + mbox.content = {'lastuid': 0} + when(soledad).get_from_index('by-type-and-mbox', 'mbox', 'INBOX').thenReturn([mbox]) + querier = SoledadQuerier(soledad) + + self.assertEquals(querier.get_lastuid(querier.get_mbox('INBOX')[0]), 0) + mbox.content = {'lastuid': 1} + self.assertEquals(querier.get_lastuid(querier.get_mbox('INBOX')[0]), 1) + + def test_create_mail_increments_uid(self): + soledad = mock() + mbox = mock() + mail = mock() + when(mail).get_for_save(next_uid=any(), mailbox='INBOX').thenReturn([]) + mbox.content = {'lastuid': 0} + when(soledad).get_from_index('by-type-and-mbox', 'mbox', 'INBOX').thenReturn([mbox]) + querier = SoledadQuerier(soledad) + when(querier).mail(any()).thenReturn([]) + + self.assertEquals(querier.get_lastuid(querier.get_mbox('INBOX')[0]), 0) + querier.create_mail(mail, 'INBOX') + self.assertEquals(querier.get_lastuid(querier.get_mbox('INBOX')[0]), 1) diff --git a/service/test/unit/resources/test_sync_info_controller.py b/service/test/unit/resources/test_sync_info_controller.py index a91dd386..1285237b 100644 --- a/service/test/unit/resources/test_sync_info_controller.py +++ b/service/test/unit/resources/test_sync_info_controller.py @@ -18,7 +18,7 @@ import json from test.support.test_helper import request_mock from pixelated.resources.sync_info_resource import SyncInfoResource -from mockito import * +from mockito import mock class SyncInfoResourceTest(unittest.TestCase): diff --git a/service/test/unit/support/test_ext_keymanager_fetch_key.py b/service/test/unit/support/test_ext_keymanager_fetch_key.py new file mode 100644 index 00000000..8998198d --- /dev/null +++ b/service/test/unit/support/test_ext_keymanager_fetch_key.py @@ -0,0 +1,76 @@ +# +# Copyright (c) 2014 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 <http://www.gnu.org/licenses/>. +import unittest +from mock import MagicMock, patch + +from leap.keymanager import KeyManager +from leap.keymanager.keys import KEY_ADDRESS_KEY, KEY_TYPE_KEY, KEY_ID_KEY, KEY_FINGERPRINT_KEY, KEY_DATA_KEY, KEY_PRIVATE_KEY, KEY_LENGTH_KEY, KEY_EXPIRY_DATE_KEY, KEY_FIRST_SEEN_AT_KEY, KEY_LAST_AUDITED_AT_KEY, KEY_VALIDATION_KEY, KEY_TAGS_KEY +from leap.keymanager.openpgp import OpenPGPKey +from leap.keymanager.errors import KeyNotFound +import pixelated.support.ext_keymanager_fetch_key +from requests.exceptions import HTTPError + + +class TestDoc(object): + def __init__(self, encryption_key): + self.content = encryption_key + +sample_key = { + KEY_ADDRESS_KEY: 'foo@bar.de', + KEY_TYPE_KEY: 'type', + KEY_ID_KEY: 'key_id', + KEY_FINGERPRINT_KEY: 'fingerprint', + KEY_DATA_KEY: 'key_data', + KEY_PRIVATE_KEY: None, + KEY_LENGTH_KEY: 'length', + KEY_EXPIRY_DATE_KEY: 'expiry_date', + KEY_FIRST_SEEN_AT_KEY: 'first_seen_at', + KEY_LAST_AUDITED_AT_KEY: 'last_audited_at', + KEY_VALIDATION_KEY: 'validation', + KEY_TAGS_KEY: 'tags', +} + + +class TestExtKeyManagerFetchKey(unittest.TestCase): + + @patch('leap.keymanager.requests') + def test_retrieves_key(self, requests_mock): + nickserver_url = 'http://some/nickserver/uri' + soledad = MagicMock() + soledad.get_from_index.side_effect = [[], [TestDoc(sample_key)]] + + km = KeyManager('me@bar.de', nickserver_url, soledad, ca_cert_path='some path') + + result = km.get_key('foo@bar.de', OpenPGPKey) + + self.assertEqual(str(OpenPGPKey('foo@bar.de', key_id='key_id')), str(result)) + + @patch('leap.keymanager.requests') + def test_http_error_500(self, requests_mock): + def do_request(one, data=None, verify=None): + response = MagicMock() + response.raise_for_status = MagicMock() + response.raise_for_status.side_effect = HTTPError + return response + + nickserver_url = 'http://some/nickserver/uri' + soledad = MagicMock() + soledad.get_from_index.side_effect = [[], []] + requests_mock.get.side_effect = do_request + + km = KeyManager('me@bar.de', nickserver_url, soledad, ca_cert_path='some path') + + self.assertRaises(KeyNotFound, km.get_key, 'foo@bar.de', OpenPGPKey) |