summaryrefslogtreecommitdiff
path: root/service/test
diff options
context:
space:
mode:
authorFolker Bernitt <fbernitt@thoughtworks.com>2016-01-27 13:56:41 +0100
committerFolker Bernitt <fbernitt@thoughtworks.com>2016-01-27 13:57:26 +0100
commite66b356f900bc9899a5506378163ffaadd4a32b5 (patch)
tree1f7d06ac3c23a274f91c6e706afcc1615ded2397 /service/test
parent32dce59ce1aa32846948148fafaffb190206477e (diff)
Add a login multi user functional test
- Issue #576 - Extended AppTestClient with multi user support
Diffstat (limited to 'service/test')
-rw-r--r--service/test/functional/features/environment.py17
-rw-r--r--service/test/functional/features/login.feature28
-rw-r--r--service/test/functional/features/steps/common.py6
-rw-r--r--service/test/functional/features/steps/compose.py1
-rw-r--r--service/test/functional/features/steps/data_setup.py25
-rw-r--r--service/test/functional/features/steps/login.py34
-rw-r--r--service/test/functional/features/steps/mail_list.py1
-rw-r--r--service/test/integration/test_multi_user_login.py1
-rw-r--r--service/test/support/integration/app_test_client.py92
-rw-r--r--service/test/support/integration/multi_user_client.py13
-rw-r--r--service/test/unit/resources/test_login_resource.py4
-rw-r--r--service/test/unit/test_application.py4
12 files changed, 196 insertions, 30 deletions
diff --git a/service/test/functional/features/environment.py b/service/test/functional/features/environment.py
index a41329dd..847322ff 100644
--- a/service/test/functional/features/environment.py
+++ b/service/test/functional/features/environment.py
@@ -19,6 +19,8 @@ import uuid
from crochet import setup, wait_for
from leap.common.events.server import ensure_server
from twisted.internet import defer
+
+from pixelated.application import UserAgentMode
from test.support.dispatcher.proxy import Proxy
from test.support.integration import AppTestClient
from selenium import webdriver
@@ -31,21 +33,26 @@ setup()
@wait_for(timeout=5.0)
-def start_app_test_client(client):
- ensure_server()
- return client.start_client()
+def start_app_test_client(client, mode):
+ return client.start_client(mode=mode)
def before_all(context):
+ ensure_server()
logging.disable('INFO')
client = AppTestClient()
- start_app_test_client(client)
+ start_app_test_client(client, UserAgentMode(is_single_user=True))
client.listenTCP()
proxy = Proxy(proxy_port='8889', app_port='4567')
FeaturesResource.DISABLED_FEATURES.append('autoRefresh')
context.client = client
context.call_to_terminate_proxy = proxy.run_on_a_thread()
+ multi_user_client = AppTestClient()
+ start_app_test_client(multi_user_client, UserAgentMode(is_single_user=False))
+ multi_user_client.listenTCP(port=MULTI_USER_PORT)
+ context.multi_user_client = multi_user_client
+
def after_all(context):
context.call_to_terminate_proxy()
@@ -57,7 +64,7 @@ def before_feature(context, feature):
context.browser.set_window_size(1280, 1024)
context.browser.implicitly_wait(DEFAULT_IMPLICIT_WAIT_TIMEOUT_IN_S)
context.browser.set_page_load_timeout(60) # wait for data
- context.browser.get('http://localhost:8889/')
+ context.browser.get(HOMEPAGE_URL)
def after_step(context, step):
diff --git a/service/test/functional/features/login.feature b/service/test/functional/features/login.feature
new file mode 100644
index 00000000..9a353d74
--- /dev/null
+++ b/service/test/functional/features/login.feature
@@ -0,0 +1,28 @@
+#
+# 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/>.
+
+Feature: login and logout
+ As a user of Pixelated
+ I want to log in to my account
+ So I can see my emails
+
+ Scenario: Existing user logs into his account
+ Given Account for user username exists
+ And I have a mail for username in my inbox
+ When I open the login page
+ And I enter username and password as credentials
+ And I click on the login button
+ Then I have mails
diff --git a/service/test/functional/features/steps/common.py b/service/test/functional/features/steps/common.py
index 9a547375..fb5698f0 100644
--- a/service/test/functional/features/steps/common.py
+++ b/service/test/functional/features/steps/common.py
@@ -26,6 +26,12 @@ TIMEOUT_IN_S = 20
DEFAULT_IMPLICIT_WAIT_TIMEOUT_IN_S = 10.0
+HOMEPAGE_URL = 'http://localhost:8889/'
+
+MULTI_USER_PORT = 4568
+
+MULTI_USER_URL = 'http://localhost:%d/' % MULTI_USER_PORT
+
class ImplicitWait(object):
def __init__(self, context, timeout=5.0):
diff --git a/service/test/functional/features/steps/compose.py b/service/test/functional/features/steps/compose.py
index f0bed86e..67b1bd51 100644
--- a/service/test/functional/features/steps/compose.py
+++ b/service/test/functional/features/steps/compose.py
@@ -21,7 +21,6 @@ from common import *
@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')
toggle.click()
diff --git a/service/test/functional/features/steps/data_setup.py b/service/test/functional/features/steps/data_setup.py
index fb825aba..167acf9a 100644
--- a/service/test/functional/features/steps/data_setup.py
+++ b/service/test/functional/features/steps/data_setup.py
@@ -17,6 +17,7 @@ from uuid import uuid4
from test.support.integration import MailBuilder
from behave import given
from common import wait_for_condition
+from crochet import wait_for
@given('I have a mail in my inbox')
@@ -24,8 +25,30 @@ def add_mail_impl(context):
subject = 'Hi! This the subject %s' % uuid4()
input_mail = MailBuilder().with_subject(subject).build_input_mail()
- context.client.add_mail_to_inbox(input_mail)
+ context.client.add_mail_to_inbox(input_mail)
wait_for_condition(context, lambda _: context.client.search_engine.search(subject)[1] > 0, poll_frequency=0.1)
context.last_subject = subject
+
+
+@given('I have a mail for {username} in my inbox')
+def add_mail_to_user_inbox(context, username):
+ subject = 'Hi! This the subject %s' % uuid4()
+
+ input_mail = MailBuilder().with_subject(subject).build_input_mail()
+
+ context.multi_user_client.add_mail_to_user_inbox(input_mail, username)
+ wait_for_condition(context, lambda _: context.multi_user_client.account_for(username).search_engine.search(subject)[1] > 0, poll_frequency=0.1)
+
+ context.last_subject = subject
+
+
+@wait_for(timeout=10.0)
+def add_multi_user_account(context, username):
+ return context.multi_user_client.create_user('username')
+
+
+@given(u'Account for user {username} exists')
+def add_account(context, username):
+ add_multi_user_account(context, username)
diff --git a/service/test/functional/features/steps/login.py b/service/test/functional/features/steps/login.py
new file mode 100644
index 00000000..3c80e819
--- /dev/null
+++ b/service/test/functional/features/steps/login.py
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2016 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/>.
+from behave import when, then
+from common import *
+
+
+@when(u'I open the login page')
+def login_page(context):
+ context.browser.get(MULTI_USER_URL + '/login')
+
+
+@when(u'I enter {username} and {password} as credentials')
+def enter_credentials(context, username, password):
+ fill_by_css_selector(context, 'input#email', username)
+ fill_by_css_selector(context, 'input#password', password)
+
+
+@when(u'I click on the login button')
+def click_login(context):
+ login_button = wait_until_element_is_visible_by_locator(context, (By.CSS_SELECTOR, 'input[name="login"]'))
+ login_button.click()
diff --git a/service/test/functional/features/steps/mail_list.py b/service/test/functional/features/steps/mail_list.py
index 1b850578..d19de6cd 100644
--- a/service/test/functional/features/steps/mail_list.py
+++ b/service/test/functional/features/steps/mail_list.py
@@ -76,6 +76,7 @@ def impl(context):
@given('I have mails')
+@then(u'I have mails')
def impl(context):
emails = wait_until_elements_are_visible_by_locator(context, (By.CSS_SELECTOR, '#mail-list li span a'))
assert len(emails) > 0
diff --git a/service/test/integration/test_multi_user_login.py b/service/test/integration/test_multi_user_login.py
index c571cf29..029d6f26 100644
--- a/service/test/integration/test_multi_user_login.py
+++ b/service/test/integration/test_multi_user_login.py
@@ -33,6 +33,7 @@ class MultiUserLoginTest(MultiUserClient, SoledadTestBase):
@defer.inlineCallbacks
def test_logged_in_users_sees_resources(self):
response, login_request = yield self.login()
+ yield response
mail = load_mail_from_file('mbox00000000')
mail_id = yield self._create_mail_in_soledad(mail)
expected_mail_dict = {'body': u'Dignissimos ducimus veritatis. Est tenetur consequatur quia occaecati. Vel sit sit voluptas.\n\nEarum distinctio eos. Accusantium qui sint ut quia assumenda. Facere dignissimos inventore autem sit amet. Pariatur voluptatem sint est.\n\nUt recusandae praesentium aspernatur. Exercitationem amet placeat deserunt quae consequatur eum. Unde doloremque suscipit quia.\n\n', 'header': {u'date': u'Tue, 21 Apr 2015 08:43:27 +0000 (UTC)', u'to': [u'carmel@murazikortiz.name'], u'x-tw-pixelated-tags': u'nite, macro, trash', u'from': u'darby.senger@zemlak.biz', u'subject': u'Itaque consequatur repellendus provident sunt quia.'}, 'ident': mail_id, 'status': [], 'tags': [], 'textPlainBody': u'Dignissimos ducimus veritatis. Est tenetur consequatur quia occaecati. Vel sit sit voluptas.\n\nEarum distinctio eos. Accusantium qui sint ut quia assumenda. Facere dignissimos inventore autem sit amet. Pariatur voluptatem sint est.\n\nUt recusandae praesentium aspernatur. Exercitationem amet placeat deserunt quae consequatur eum. Unde doloremque suscipit quia.\n\n', 'mailbox': u'inbox', 'attachments': [], 'security_casing': {'imprints': [{'state': 'no_signature_information'}], 'locks': []}}
diff --git a/service/test/support/integration/app_test_client.py b/service/test/support/integration/app_test_client.py
index baf05e2a..e841cfe5 100644
--- a/service/test/support/integration/app_test_client.py
+++ b/service/test/support/integration/app_test_client.py
@@ -16,6 +16,7 @@
import json
import multiprocessing
from leap.mail.adaptors.soledad import SoledadMailAdaptor
+from leap.srp_session import SRPSession
from mockito import mock
import os
import shutil
@@ -31,9 +32,13 @@ from twisted.internet import reactor, defer
from twisted.internet.defer import succeed
from twisted.web.resource import getChildForRequest
# from twisted.web.server import Site as PixelatedSite
+from zope.interface import implementer
+from twisted.cred import checkers, credentials
from pixelated.adapter.mailstore.leap_attachment_store import LeapAttachmentStore
from pixelated.adapter.services.feedback_service import FeedbackService
-from pixelated.application import ServicesFactory, UserAgentMode, SingleUserServicesFactory
+from pixelated.application import ServicesFactory, UserAgentMode, SingleUserServicesFactory, set_up_protected_resources
+from pixelated.bitmask_libraries.config import LeapConfig
+from pixelated.bitmask_libraries.session import LeapSession
from pixelated.config.site import PixelatedSite
from pixelated.adapter.mailstore import LeapMailStore
@@ -53,17 +58,17 @@ class AppTestAccount(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'
- def __init__(self, user_id):
+ def __init__(self, user_id, leap_home):
self._user_id = user_id
+ self._leap_home = leap_home
self._uuid = str(uuid.uuid4())
self._mail_address = '%s@pixelated.org' % user_id
- self._tmp_dir = TempDir()
self._soledad = None
self._services = None
@defer.inlineCallbacks
def start(self):
- soledad_test_folder = os.path.join(self._tmp_dir.name, self._uuid)
+ soledad_test_folder = os.path.join(self._leap_home, self._uuid)
self.soledad = yield initialize_soledad(tempdir=soledad_test_folder, uuid=self._uuid)
self.search_engine = SearchEngine(self.INDEX_KEY, user_home=soledad_test_folder)
self.keymanager = mock()
@@ -99,7 +104,8 @@ class AppTestAccount(object):
return self._services
def cleanup(self):
- self._tmp_dir.dissolve()
+ soledad_test_folder = os.path.join(self._leap_home, self._uuid)
+ shutil.rmtree(soledad_test_folder)
def _initialize_imap_account(self):
account_ready_cb = defer.Deferred()
@@ -115,19 +121,56 @@ class AppTestAccount(object):
return mail_sender
+@implementer(checkers.ICredentialsChecker)
+class StubSRPChecker(object):
+ credentialInterfaces = (
+ credentials.IUsernamePassword,
+ )
+
+ def __init__(self, leap_provider, credentials={}):
+ self._leap_provider = leap_provider
+ self._credentials = credentials.copy()
+
+ def add_user(self, username, password):
+ self._credentials[username] = password
+
+ def requestAvatarId(self, credentials):
+ leap_auth = SRPSession(credentials.username, uuid.uuid4(), uuid.uuid4(), uuid.uuid4())
+ return defer.succeed(LeapSession(self._leap_provider, leap_auth, None, None, None, None))
+
+
+class StubServicesFactory(ServicesFactory):
+
+ def __init__(self, accounts, mode):
+ super(StubServicesFactory, self).__init__(mode=mode)
+ self._accounts = accounts
+
+ @defer.inlineCallbacks
+ def create_services_from(self, leap_session):
+ account = self._accounts[leap_session.user_auth.username]
+ self._services_by_user[leap_session.user_auth.uuid] = account.services
+ yield defer.succeed(None)
+
+
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 _initialize(self):
+ self._tmp_dir = TempDir()
+ self.accounts = {}
+
@defer.inlineCallbacks
- def start_client(self):
- self._test_account = AppTestAccount(self.ACCOUNT)
+ def start_client(self, mode=UserAgentMode(is_single_user=True)):
+ self._initialize()
+ self._mode = mode
+ self._test_account = AppTestAccount(self.ACCOUNT, self._tmp_dir.name)
yield self._test_account.start()
- self.cleanup = lambda: self._test_account.cleanup()
+ self.cleanup = lambda: self._tmp_dir.dissolve()
# copy fields for single user tests
self.soledad = self._test_account.soledad
@@ -142,12 +185,29 @@ class AppTestClient(object):
self.mail_service = self._test_account.mail_service
self.account = self._test_account.account
- self.service_factory = SingleUserServicesFactory(UserAgentMode(is_single_user=True))
- services = self._test_account.services
- self.service_factory.add_session('someuserid', services)
+ if mode.is_single_user:
+ self.service_factory = SingleUserServicesFactory(mode)
+ services = self._test_account.services
+ self.service_factory.add_session('someuserid', services)
+
+ self.resource = RootResource(self.service_factory)
+ self.resource.initialize()
+ else:
+ self.service_factory = StubServicesFactory(self.accounts, mode)
+ provider = mock()
+ provider.config = LeapConfig(self._tmp_dir.name)
+
+ self.resource = set_up_protected_resources(RootResource(self.service_factory), provider, self.service_factory, checker=StubSRPChecker(provider))
- self.resource = RootResource(self.service_factory)
- self.resource.initialize()
+ @defer.inlineCallbacks
+ def create_user(self, account_name):
+ if self._mode.is_single_user:
+ raise Exception('Not supported in single user mode')
+
+ account = AppTestAccount(account_name, self._tmp_dir.name)
+ yield account.start()
+
+ self.accounts[account_name] = account
def _render(self, request, as_json=True):
def get_str(_str):
@@ -204,6 +264,12 @@ class AppTestClient(object):
mail = yield self.mail_store.add_mail('INBOX', input_mail.raw)
defer.returnValue(mail)
+ def account_for(self, username):
+ return self.accounts[username]
+
+ def add_mail_to_user_inbox(self, input_mail, username):
+ return self.account_for(username).mail_store.add_mail('INBOX', input_mail.raw)
+
@defer.inlineCallbacks
def add_multiple_to_mailbox(self, num, mailbox='', flags=[], tags=[], to='recipient@to.com', cc='recipient@cc.com', bcc='recipient@bcc.com'):
mails = []
diff --git a/service/test/support/integration/multi_user_client.py b/service/test/support/integration/multi_user_client.py
index af314fdf..2aa7d3ff 100644
--- a/service/test/support/integration/multi_user_client.py
+++ b/service/test/support/integration/multi_user_client.py
@@ -35,22 +35,23 @@ from pixelated.adapter.mailstore.searchable_mailstore import SearchableMailStore
from pixelated.adapter.search import SearchEngine
from pixelated.adapter.services.draft_service import DraftService
from pixelated.bitmask_libraries.session import LeapSession, LeapSessionFactory
-from pixelated.config import services as config_services
+import pixelated.config.services
# from pixelated.config.services import Services
from pixelated.resources.auth import LeapPasswordChecker, SessionChecker, PixelatedRealm, PixelatedAuthSessionWrapper
from pixelated.resources.login_resource import LoginResource
from pixelated.resources.root_resource import RootResource
from test.support.integration import AppTestClient
from test.support.integration.app_test_client import initialize_soledad, AppTestAccount
-
+import test.support.mockito
from test.support.test_helper import request_mock
class MultiUserClient(AppTestClient):
@defer.inlineCallbacks
- def start_client(self):
- self._test_account = AppTestAccount('test')
+ def start_client(self, mode=UserAgentMode(is_single_user=True)):
+ self._initialize()
+ self._test_account = AppTestAccount('test', self._tmp_dir.name)
yield self._test_account.start()
@@ -74,9 +75,9 @@ class MultiUserClient(AppTestClient):
leap_session.fresh_account = False
self._set_leap_srp_auth(username, password)
+ when(leap_session).initial_sync().thenAnswer(lambda: defer.succeed(None))
when(LeapSessionFactory).create(username, password).thenReturn(leap_session)
- when(config_services).Services(leap_session).thenReturn(self._test_account.services)
- # when(Services).setup().thenReturn(defer.succeed('mocked so irrelevant'))
+ when(pixelated.config.services).Services(ANY()).thenReturn(self._test_account.services)
request = request_mock(path='/login', method="POST", body={'username': username, 'password': password})
return self._render(request, as_json=False)
diff --git a/service/test/unit/resources/test_login_resource.py b/service/test/unit/resources/test_login_resource.py
index 1f9a7c6a..1ccc530d 100644
--- a/service/test/unit/resources/test_login_resource.py
+++ b/service/test/unit/resources/test_login_resource.py
@@ -88,6 +88,7 @@ class TestLoginPOST(unittest.TestCase):
config.leap_home = 'some_folder'
leap_session.config = config
leap_session.fresh_account = False
+ self.leap_session = leap_session
self.leap_user = LeapUser(leap_session)
@patch('twisted.web.util.redirectTo')
@@ -99,8 +100,7 @@ class TestLoginPOST(unittest.TestCase):
def assert_login_setup_service_for_user(_):
verify(self.portal).login(ANY(), None, IResource)
- self.assertTrue(mock_service_setup.called)
- verify(self.services_factory).add_session('some_user_uuid', ANY())
+ verify(self.services_factory).create_services_from(self.leap_session)
mock_redirect.assert_called_once_with('/', self.request)
self.assertTrue(self.resource.is_logged_in(self.request))
diff --git a/service/test/unit/test_application.py b/service/test/unit/test_application.py
index a0eb9986..5f7f9c74 100644
--- a/service/test/unit/test_application.py
+++ b/service/test/unit/test_application.py
@@ -38,7 +38,7 @@ class ApplicationTest(unittest.TestCase):
@patch('leap.common.events.client')
@patch('pixelated.application.reactor')
- @patch('pixelated.application.Services')
+ @patch('pixelated.application.services.Services')
def test_that_start_user_agent_binds_to_tcp_port_if_no_ssl_options(self, services_mock, reactor_mock, _):
# FIXME patch something closer, instead of leap.common
app_mock = MagicMock()
@@ -56,7 +56,7 @@ class ApplicationTest(unittest.TestCase):
@patch('leap.common.events.client')
@patch('pixelated.application.reactor')
- @patch('pixelated.application.Services')
+ @patch('pixelated.application.services.Services')
def test_that_start_user_agent_binds_to_ssl_if_ssl_options(self, services_mock, reactor_mock, _):
# FIXME patch something closer, instead of leap.common
app_mock = MagicMock()