summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/setup.py2
-rw-r--r--common/src/leap/soledad/common/tests/__init__.py44
-rw-r--r--common/src/leap/soledad/common/tests/server_state.py80
-rw-r--r--common/src/leap/soledad/common/tests/test_couch.py13
-rw-r--r--common/src/leap/soledad/common/tests/test_sqlcipher.py69
-rw-r--r--common/src/leap/soledad/common/tests/test_target.py160
-rw-r--r--common/src/leap/soledad/common/tests/u1db_tests/test_sync.py4
-rw-r--r--common/src/leap/soledad/common/tests/util.py177
-rw-r--r--server/src/leap/soledad/server/__init__.py1
9 files changed, 424 insertions, 126 deletions
diff --git a/common/setup.py b/common/setup.py
index e142d958..6ee166ef 100644
--- a/common/setup.py
+++ b/common/setup.py
@@ -285,7 +285,7 @@ setup(
namespace_packages=["leap", "leap.soledad"],
packages=find_packages('src', exclude=['leap.soledad.common.tests']),
package_dir={'': 'src'},
- test_suite='leap.soledad.common.tests',
+ test_suite='leap.soledad.common.tests.load_tests',
install_requires=utils.parse_requirements(),
tests_require=utils.parse_requirements(
reqfiles=['pkg/requirements-testing.pip']),
diff --git a/common/src/leap/soledad/common/tests/__init__.py b/common/src/leap/soledad/common/tests/__init__.py
index 88f98272..a38bdaed 100644
--- a/common/src/leap/soledad/common/tests/__init__.py
+++ b/common/src/leap/soledad/common/tests/__init__.py
@@ -1,3 +1,21 @@
+# -*- coding: utf-8 -*-
+# __init__.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
"""
Tests to make sure Soledad provides U1DB functionality and more.
"""
@@ -273,3 +291,29 @@ RZXoH+FTg9UAW87eqU610npOkT6cRaBxaMK/mDtGNdc=
=JTFu
-----END PGP PRIVATE KEY BLOCK-----
"""
+
+
+def load_tests():
+ """
+ Build a test suite that includes all tests in leap.soledad.common.tests
+ but does not include tests in the u1db_tests/ subfolder. The reason for
+ not including those tests are:
+
+ 1. they by themselves only test u1db functionality in the u1db module
+ (despite we use them as basis for testing soledad functionalities).
+
+ 2. they would fail because we monkey patch u1db's remote http server
+ to add soledad functionality we need.
+ """
+ import unittest
+ import glob
+ import imp
+ tests_prefix = os.path.join(
+ '.', 'src', 'leap', 'soledad', 'common', 'tests')
+ suite = unittest.TestSuite()
+ for testcase in glob.glob(os.path.join(tests_prefix, 'test_*.py')):
+ modname = os.path.basename(os.path.splitext(testcase)[0])
+ f, pathname, description = imp.find_module(modname, [tests_prefix])
+ module = imp.load_module(modname, f, pathname, description)
+ suite.addTest(unittest.TestLoader().loadTestsFromModule(module))
+ return suite
diff --git a/common/src/leap/soledad/common/tests/server_state.py b/common/src/leap/soledad/common/tests/server_state.py
new file mode 100644
index 00000000..2bc15377
--- /dev/null
+++ b/common/src/leap/soledad/common/tests/server_state.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+# server_state.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""
+State for servers to be used in tests.
+"""
+
+
+import os
+import errno
+import tempfile
+
+
+from u1db.remote.server_state import ServerState
+from leap.soledad.common.tests.util import (
+ copy_sqlcipher_database_for_test,
+)
+
+
+class ServerStateForTests(ServerState):
+ """Passed to a Request when it is instantiated.
+
+ This is used to track server-side state, such as working-directory, open
+ databases, etc.
+ """
+
+ def __init__(self):
+ self._workingdir = tempfile.mkdtemp()
+
+ def _relpath(self, relpath):
+ return os.path.join(self._workingdir, relpath)
+
+ def open_database(self, path):
+ """Open a database at the given location."""
+ from leap.soledad.client.sqlcipher import SQLCipherDatabase
+ return SQLCipherDatabase.open_database(path, '123', False)
+
+ def create_database(self, path):
+ """Create a database at the given location."""
+ from leap.soledad.client.sqlcipher import SQLCipherDatabase
+ return SQLCipherDatabase.open_database(path, '123', True)
+
+ def check_database(self, path):
+ """Check if the database at the given location exists.
+
+ Simply returns if it does or raises DatabaseDoesNotExist.
+ """
+ db = self.open_database(path)
+ db.close()
+
+ def ensure_database(self, path):
+ """Ensure database at the given location."""
+ from leap.soledad.client.sqlcipher import SQLCipherDatabase
+ full_path = self._relpath(path)
+ db = SQLCipherDatabase.open_database(full_path, '123', False)
+ return db, db._replica_uid
+
+ def delete_database(self, path):
+ """Delete database at the given location."""
+ from leap.u1db.backends import sqlite_backend
+ full_path = self._relpath(path)
+ sqlite_backend.SQLiteDatabase.delete_database(full_path)
+
+ def _copy_database(self, db):
+ return copy_sqlcipher_database_for_test(None, db)
diff --git a/common/src/leap/soledad/common/tests/test_couch.py b/common/src/leap/soledad/common/tests/test_couch.py
index 17d4a519..a1fa9568 100644
--- a/common/src/leap/soledad/common/tests/test_couch.py
+++ b/common/src/leap/soledad/common/tests/test_couch.py
@@ -249,9 +249,7 @@ class CouchTests(test_backends.AllDatabaseTests, CouchDBTestCase):
# if current test is `test_close` we have to use saved objects to
# delete the database because the close() method will have removed the
# references needed to do it using the CouchDatabase.
- if self.id() == \
- 'leap.soledad.common.tests.test_couch.CouchTests.' \
- 'test_close(couch)':
+ if self.id().endswith('test_couch.CouchTests.test_close(couch)'):
session = couch.Session()
server = Server(url=self._url, session=session)
del(server[self._dbname])
@@ -365,8 +363,6 @@ class CouchDatabaseSyncTargetTests(test_sync.DatabaseSyncTargetTests,
# The following tests need that the database have an index, so we fake one.
-old_class = couch.CouchDatabase
-
from u1db.backends.inmemory import InMemoryIndex
@@ -444,7 +440,12 @@ class IndexedCouchDatabase(couch.CouchDatabase):
return list(set([tuple(key.split('\x01')) for key in keys]))
-couch.CouchDatabase = IndexedCouchDatabase
+# monkey patch CouchDatabase (once) to include virtual indexes
+if getattr(couch.CouchDatabase, '_old_class', None) is None:
+ old_class = couch.CouchDatabase
+ IndexedCouchDatabase._old_class = old_class
+ couch.CouchDatabase = IndexedCouchDatabase
+
sync_scenarios = []
for name, scenario in COUCH_SCENARIOS:
diff --git a/common/src/leap/soledad/common/tests/test_sqlcipher.py b/common/src/leap/soledad/common/tests/test_sqlcipher.py
index c79a6045..891aca0f 100644
--- a/common/src/leap/soledad/common/tests/test_sqlcipher.py
+++ b/common/src/leap/soledad/common/tests/test_sqlcipher.py
@@ -30,6 +30,7 @@ import threading
from pysqlcipher import dbapi2
from StringIO import StringIO
+from urlparse import urljoin
# u1db stuff.
@@ -54,19 +55,26 @@ from leap.soledad.common.crypto import (
ENC_JSON_KEY,
ENC_SCHEME_KEY,
)
-from leap.soledad.client.target import decrypt_doc
+from leap.soledad.client.target import (
+ decrypt_doc,
+ SoledadSyncTarget,
+)
# u1db tests stuff.
+from leap.common.testing.basetest import BaseLeapTest
from leap.soledad.common.tests import u1db_tests as tests, BaseSoledadTest
from leap.soledad.common.tests.u1db_tests import test_sqlite_backend
from leap.soledad.common.tests.u1db_tests import test_backends
from leap.soledad.common.tests.u1db_tests import test_open
from leap.soledad.common.tests.u1db_tests import test_sync
-from leap.soledad.client.target import SoledadSyncTarget
-from leap.common.testing.basetest import BaseLeapTest
-
-PASSWORD = '123456'
+from leap.soledad.common.tests.util import (
+ make_sqlcipher_database_for_test,
+ copy_sqlcipher_database_for_test,
+ make_soledad_app,
+ SoledadWithCouchServerMixin,
+ PASSWORD,
+)
#-----------------------------------------------------------------------------
@@ -88,32 +96,6 @@ class TestSQLCipherBackendImpl(tests.TestCase):
# The following tests come from `u1db.tests.test_backends`.
#-----------------------------------------------------------------------------
-def make_sqlcipher_database_for_test(test, replica_uid):
- db = SQLCipherDatabase(':memory:', PASSWORD)
- db._set_replica_uid(replica_uid)
- return db
-
-
-def copy_sqlcipher_database_for_test(test, db):
- # DO NOT COPY OR REUSE THIS CODE OUTSIDE TESTS: COPYING U1DB DATABASES IS
- # THE WRONG THING TO DO, THE ONLY REASON WE DO SO HERE IS TO TEST THAT WE
- # CORRECTLY DETECT IT HAPPENING SO THAT WE CAN RAISE ERRORS RATHER THAN
- # CORRUPT USER DATA. USE SYNC INSTEAD, OR WE WILL SEND NINJA TO YOUR
- # HOUSE.
- new_db = SQLCipherDatabase(':memory:', PASSWORD)
- tmpfile = StringIO()
- for line in db._db_handle.iterdump():
- if not 'sqlite_sequence' in line: # work around bug in iterdump
- tmpfile.write('%s\n' % line)
- tmpfile.seek(0)
- new_db._db_handle = dbapi2.connect(':memory:')
- new_db._db_handle.cursor().executescript(tmpfile.read())
- new_db._db_handle.commit()
- new_db._set_replica_uid(db._replica_uid)
- new_db._factory = db._factory
- return new_db
-
-
def make_document_for_test(test, doc_id, rev, content, has_conflicts=False):
return SoledadDocument(doc_id, rev, content, has_conflicts=has_conflicts)
@@ -451,7 +433,7 @@ sync_scenarios.append(('pyleap', {
'copy_database_for_test': test_sync.copy_database_for_http_test,
'make_document_for_test': make_document_for_test,
'make_app_with_state': tests.test_remote_sync_target.make_http_app,
- 'do_sync': sync_via_synchronizer_and_leap,
+ 'do_sync': test_sync.sync_via_synchronizer,
}))
@@ -616,7 +598,7 @@ class SQLCipherDatabaseSyncTests(
# update on 1
doc1.set_json('{"a": 3}')
self.db1.put_doc(doc1)
- # conflicts
+ # conflicts
self.sync(self.db2, self.db1)
self.sync(db3, self.db1)
self.assertTrue(self.db2.get_doc('the-doc').has_conflicts)
@@ -658,32 +640,35 @@ class SQLCipherDatabaseSyncTests(
'return': {'docs': [], 'last_gen': 1}})
-def _make_local_db_and_leap_target(test, path='test'):
+def _make_local_db_and_token_http_target(test, path='test'):
test.startServer()
db = test.request_state._create_database(os.path.basename(path))
- st = SoledadSyncTarget.connect(test.getURL(path), test._soledad._crypto)
+ st = SoledadSyncTarget.connect(
+ test.getURL(path), crypto=test._soledad._crypto)
st.set_token_credentials('user-uuid', 'auth-token')
return db, st
target_scenarios = [
('leap', {
- 'create_db_and_target': _make_local_db_and_leap_target,
- 'make_app_with_state': tests.test_remote_sync_target.make_http_app}),
+ 'create_db_and_target': _make_local_db_and_token_http_target,
+# 'make_app_with_state': tests.test_remote_sync_target.make_http_app,
+ 'make_app_with_state': make_soledad_app,
+ 'do_sync': test_sync.sync_via_synchronizer}),
]
class SQLCipherSyncTargetTests(
- test_sync.DatabaseSyncTargetTests, BaseSoledadTest):
+ SoledadWithCouchServerMixin, test_sync.DatabaseSyncTargetTests):
scenarios = (tests.multiply_scenarios(SQLCIPHER_SCENARIOS,
target_scenarios))
- def setUp(self):
- test_sync.DatabaseSyncTargetTests.setUp(self)
+ whitebox = False
- def tearDown(self):
- test_sync.DatabaseSyncTargetTests.tearDown(self)
+ def setUp(self):
+ self.main_test_class = test_sync.DatabaseSyncTargetTests
+ SoledadWithCouchServerMixin.setUp(self)
def test_sync_exchange(self):
"""
diff --git a/common/src/leap/soledad/common/tests/test_target.py b/common/src/leap/soledad/common/tests/test_target.py
index c1e00d52..3457a3e1 100644
--- a/common/src/leap/soledad/common/tests/test_target.py
+++ b/common/src/leap/soledad/common/tests/test_target.py
@@ -27,6 +27,7 @@ import simplejson as json
import cStringIO
+from u1db import SyncTarget
from u1db.sync import Synchronizer
from u1db.remote import (
http_client,
@@ -39,14 +40,20 @@ from leap.soledad.client import (
target,
auth,
VerifiedHTTPSConnection,
+ sync,
)
from leap.soledad.common.document import SoledadDocument
-from leap.soledad.server import SoledadApp
from leap.soledad.server.auth import SoledadTokenAuthMiddleware
from leap.soledad.common.tests import u1db_tests as tests
from leap.soledad.common.tests import BaseSoledadTest
+from leap.soledad.common.tests.util import (
+ make_sqlcipher_database_for_test,
+ make_soledad_app,
+ make_token_soledad_app,
+ SoledadWithCouchServerMixin,
+)
from leap.soledad.common.tests.u1db_tests import test_backends
from leap.soledad.common.tests.u1db_tests import test_http_database
from leap.soledad.common.tests.u1db_tests import test_http_client
@@ -54,6 +61,10 @@ from leap.soledad.common.tests.u1db_tests import test_document
from leap.soledad.common.tests.u1db_tests import test_remote_sync_target
from leap.soledad.common.tests.u1db_tests import test_https
from leap.soledad.common.tests.u1db_tests import test_sync
+from leap.soledad.common.tests.test_couch import (
+ CouchDBTestCase,
+ CouchDBWrapper,
+)
#-----------------------------------------------------------------------------
@@ -66,28 +77,6 @@ def make_leap_document_for_test(test, doc_id, rev, content,
doc_id, rev, content, has_conflicts=has_conflicts)
-def make_soledad_app(state):
- return SoledadApp(state)
-
-
-def make_token_soledad_app(state):
- app = SoledadApp(state)
-
- def _verify_authentication_data(uuid, auth_data):
- if uuid == 'user-uuid' and auth_data == 'auth-token':
- return True
- return False
-
- # we test for action authorization in leap.soledad.common.tests.test_server
- def _verify_authorization(uuid, environ):
- return True
-
- application = SoledadTokenAuthMiddleware(app)
- application._verify_authentication_data = _verify_authentication_data
- application._verify_authorization = _verify_authorization
- return application
-
-
LEAP_SCENARIOS = [
('http', {
'make_database_for_test': test_backends.make_http_database_for_test,
@@ -362,16 +351,47 @@ def token_leap_sync_target(test, path):
return st
+def make_local_db_and_soledad_target(test, path='test'):
+ test.startServer()
+ db = test.request_state._create_database(os.path.basename(path))
+ st = target.SoledadSyncTarget.connect(
+ test.getURL(path), crypto=test._soledad._crypto)
+ return db, st
+
+
+def make_local_db_and_token_soledad_target(test):
+ db, st = make_local_db_and_soledad_target(test, 'test')
+ st.set_token_credentials('user-uuid', 'auth-token')
+ return db, st
+
+
class TestSoledadSyncTarget(
- test_remote_sync_target.TestRemoteSyncTargets, BaseSoledadTest):
+ SoledadWithCouchServerMixin,
+ test_remote_sync_target.TestRemoteSyncTargets):
scenarios = [
('token_soledad',
{'make_app_with_state': make_token_soledad_app,
'make_document_for_test': make_leap_document_for_test,
+ 'create_db_and_target': make_local_db_and_token_soledad_target,
+ 'make_database_for_test': make_sqlcipher_database_for_test,
'sync_target': token_leap_sync_target}),
]
+ def setUp(self):
+ tests.TestCaseWithServer.setUp(self)
+ self.main_test_class = test_remote_sync_target.TestRemoteSyncTargets
+ SoledadWithCouchServerMixin.setUp(self)
+ self.startServer()
+ self.db1 = make_sqlcipher_database_for_test(self, 'test1')
+ self.db2 = self.request_state._create_database('test2')
+
+ def tearDown(self):
+ SoledadWithCouchServerMixin.tearDown(self)
+ tests.TestCaseWithServer.tearDown(self)
+ db, _ = self.request_state.ensure_database('test2')
+ db.delete_database()
+
def test_sync_exchange_send(self):
"""
Test for sync exchanging send of document.
@@ -383,7 +403,7 @@ class TestSoledadSyncTarget(
remote_target = self.getSyncTarget('test')
other_docs = []
- def receive_doc(doc):
+ def receive_doc(doc, gen, trans_id):
other_docs.append((doc.doc_id, doc.rev, doc.get_json()))
doc = self.make_document('doc-here', 'replica:1', '{"value": "here"}')
@@ -398,7 +418,10 @@ class TestSoledadSyncTarget(
"""
Test for sync exchange failure and retry.
- This test was adapted to decrypt remote content before assert.
+ This test was adapted to:
+ - decrypt remote content before assert.
+ - not expect a bounced document because soledad has stateful
+ recoverable sync.
"""
self.startServer()
@@ -412,7 +435,7 @@ class TestSoledadSyncTarget(
_put_doc_if_newer = db._put_doc_if_newer
trigger_ids = ['doc-here2']
- def bomb_put_doc_if_newer(doc, save_conflict,
+ def bomb_put_doc_if_newer(self, doc, save_conflict,
replica_uid=None, replica_gen=None,
replica_trans_id=None):
if doc.doc_id in trigger_ids:
@@ -421,7 +444,9 @@ class TestSoledadSyncTarget(
replica_uid=replica_uid,
replica_gen=replica_gen,
replica_trans_id=replica_trans_id)
- self.patch(db, '_put_doc_if_newer', bomb_put_doc_if_newer)
+ from leap.soledad.common.tests.test_couch import IndexedCouchDatabase
+ self.patch(
+ IndexedCouchDatabase, '_put_doc_if_newer', bomb_put_doc_if_newer)
remote_target = self.getSyncTarget('test')
other_changes = []
@@ -455,10 +480,11 @@ class TestSoledadSyncTarget(
self.assertEqual(
(11, 'T-sud'), db._get_replica_gen_and_trans_id('replica'))
self.assertEqual(2, new_gen)
- # bounced back to us
- self.assertEqual(
- ('doc-here', 'replica:1', '{"value": "here"}', 1),
- other_changes[0][:-1])
+ # we do not expect the document to be bounced back because soledad has
+ # stateful sync
+ #self.assertEqual(
+ # ('doc-here', 'replica:1', '{"value": "here"}', 1),
+ # other_changes[0][:-1])
def test_sync_exchange_send_ensure_callback(self):
"""
@@ -471,7 +497,7 @@ class TestSoledadSyncTarget(
other_docs = []
replica_uid_box = []
- def receive_doc(doc):
+ def receive_doc(doc, gen, trans_id):
other_docs.append((doc.doc_id, doc.rev, doc.get_json()))
def ensure_cb(replica_uid):
@@ -489,6 +515,11 @@ class TestSoledadSyncTarget(
self.assertGetEncryptedDoc(
db, 'doc-here', 'replica:1', '{"value": "here"}', False)
+ def test_sync_exchange_in_stream_error(self):
+ # we bypass this test because our sync_exchange process does not
+ # return u1db error 503 "unavailable" for now.
+ pass
+
#-----------------------------------------------------------------------------
# The following tests come from `u1db.tests.test_https`.
@@ -595,42 +626,34 @@ class TestHTTPDatabaseWithCreds(
# The following tests come from `u1db.tests.test_sync`.
#-----------------------------------------------------------------------------
-def _make_local_db_and_leap_target(test, path='test'):
- test.startServer()
- db = test.request_state._create_database(os.path.basename(path))
- st = target.SoledadSyncTarget.connect(
- test.getURL(path), crypto=test._soledad._crypto)
- return db, st
-
-
-def _make_local_db_and_token_leap_target(test):
- db, st = _make_local_db_and_leap_target(test, 'test')
- st.set_token_credentials('user-uuid', 'auth-token')
- return db, st
-
-
target_scenarios = [
('token_leap', {'create_db_and_target':
- _make_local_db_and_token_leap_target,
- 'make_app_with_state': make_token_soledad_app}),
+ make_local_db_and_token_soledad_target,
+ 'make_app_with_state': make_soledad_app}),
]
class SoledadDatabaseSyncTargetTests(
- test_sync.DatabaseSyncTargetTests, BaseSoledadTest):
+ SoledadWithCouchServerMixin, test_sync.DatabaseSyncTargetTests):
scenarios = (
tests.multiply_scenarios(
tests.DatabaseBaseTests.scenarios,
target_scenarios))
+ whitebox = False
+
+ def setUp(self):
+ self.main_test_class = test_sync.DatabaseSyncTargetTests
+ SoledadWithCouchServerMixin.setUp(self)
+
def test_sync_exchange(self):
"""
Test sync exchange.
This test was adapted to decrypt remote content before assert.
"""
- sol = _make_local_db_and_leap_target(self)
+ sol, _ = make_local_db_and_soledad_target(self)
docs_by_gen = [
(self.make_document('doc-id', 'replica:1', tests.simple_doc), 10,
'T-sid')]
@@ -703,17 +726,15 @@ class SoledadDatabaseSyncTargetTests(
[(doc.doc_id, doc.rev), (doc2.doc_id, doc2.rev)]})
-class TestSoledadDbSync(test_sync.TestDbSync, BaseSoledadTest):
+class TestSoledadDbSync(
+ SoledadWithCouchServerMixin, test_sync.TestDbSync):
"""Test db.sync remote sync shortcut"""
scenarios = [
- ('py-http', {
- 'make_app_with_state': make_soledad_app,
- 'make_database_for_test': tests.make_memory_database_for_test,
- }),
('py-token-http', {
+ 'create_db_and_target': make_local_db_and_token_soledad_target,
'make_app_with_state': make_token_soledad_app,
- 'make_database_for_test': tests.make_memory_database_for_test,
+ 'make_database_for_test': make_sqlcipher_database_for_test,
'token': True
}),
]
@@ -721,6 +742,10 @@ class TestSoledadDbSync(test_sync.TestDbSync, BaseSoledadTest):
oauth = False
token = False
+ def setUp(self):
+ self.main_test_class = test_sync.TestDbSync
+ SoledadWithCouchServerMixin.setUp(self)
+
def do_sync(self, target_name):
"""
Perform sync using SoledadSyncTarget and Token auth.
@@ -748,7 +773,7 @@ class TestSoledadDbSync(test_sync.TestDbSync, BaseSoledadTest):
"""
doc1 = self.db.create_doc_from_json(tests.simple_doc)
doc2 = self.db2.create_doc_from_json(tests.nested_doc)
- local_gen_before_sync = self.do_sync('test2.db')
+ local_gen_before_sync = self.do_sync('test2')
gen, _, changes = self.db.whats_changed(local_gen_before_sync)
self.assertEqual(1, len(changes))
self.assertEqual(doc2.doc_id, changes[0][0])
@@ -760,24 +785,9 @@ class TestSoledadDbSync(test_sync.TestDbSync, BaseSoledadTest):
def test_db_sync_autocreate(self):
"""
- Test sync.
-
- Adapted to check for encrypted content.
+ We bypass this test because we never need to autocreate databases.
"""
- doc1 = self.db.create_doc_from_json(tests.simple_doc)
- local_gen_before_sync = self.do_sync('test3.db')
- gen, _, changes = self.db.whats_changed(local_gen_before_sync)
- self.assertEqual(0, gen - local_gen_before_sync)
- db3 = self.request_state.open_database('test3.db')
- gen, _, changes = db3.whats_changed()
- self.assertEqual(1, len(changes))
- self.assertEqual(doc1.doc_id, changes[0][0])
- self.assertGetEncryptedDoc(
- db3, doc1.doc_id, doc1.rev, tests.simple_doc, False)
- t_gen, _ = self.db._get_replica_gen_and_trans_id('test3.db')
- s_gen, _ = db3._get_replica_gen_and_trans_id('test1')
- self.assertEqual(1, t_gen)
- self.assertEqual(1, s_gen)
+ pass
load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/u1db_tests/test_sync.py b/common/src/leap/soledad/common/tests/u1db_tests/test_sync.py
index a37c36db..633fd8dd 100644
--- a/common/src/leap/soledad/common/tests/u1db_tests/test_sync.py
+++ b/common/src/leap/soledad/common/tests/u1db_tests/test_sync.py
@@ -1155,12 +1155,12 @@ class TestDbSync(tests.TestCaseWithServer):
super(TestDbSync, self).setUp()
self.startServer()
self.db = self.make_database_for_test(self, 'test1')
- self.db2 = self.request_state._create_database('test2.db')
+ self.db2 = self.request_state._create_database('test2')
def test_db_sync(self):
doc1 = self.db.create_doc_from_json(tests.simple_doc)
doc2 = self.db2.create_doc_from_json(tests.nested_doc)
- local_gen_before_sync = self.do_sync('test2.db')
+ local_gen_before_sync = self.do_sync('test2')
gen, _, changes = self.db.whats_changed(local_gen_before_sync)
self.assertEqual(1, len(changes))
self.assertEqual(doc2.doc_id, changes[0][0])
diff --git a/common/src/leap/soledad/common/tests/util.py b/common/src/leap/soledad/common/tests/util.py
new file mode 100644
index 00000000..249cbdaa
--- /dev/null
+++ b/common/src/leap/soledad/common/tests/util.py
@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+# util.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""
+Utilities used by multiple test suites.
+"""
+
+
+import tempfile
+import shutil
+from urlparse import urljoin
+
+from StringIO import StringIO
+from pysqlcipher import dbapi2
+from u1db.errors import DatabaseDoesNotExist
+
+
+from leap.soledad.common import soledad_assert
+from leap.soledad.common.couch import CouchDatabase, CouchServerState
+from leap.soledad.server import SoledadApp
+from leap.soledad.server.auth import SoledadTokenAuthMiddleware
+
+
+from leap.soledad.common.tests import u1db_tests as tests, BaseSoledadTest
+from leap.soledad.common.tests.test_couch import CouchDBWrapper, CouchDBTestCase
+
+
+from leap.soledad.client.sqlcipher import SQLCipherDatabase
+
+
+PASSWORD = '123456'
+
+
+def make_sqlcipher_database_for_test(test, replica_uid):
+ db = SQLCipherDatabase(':memory:', PASSWORD)
+ db._set_replica_uid(replica_uid)
+ return db
+
+
+def copy_sqlcipher_database_for_test(test, db):
+ # DO NOT COPY OR REUSE THIS CODE OUTSIDE TESTS: COPYING U1DB DATABASES IS
+ # THE WRONG THING TO DO, THE ONLY REASON WE DO SO HERE IS TO TEST THAT WE
+ # CORRECTLY DETECT IT HAPPENING SO THAT WE CAN RAISE ERRORS RATHER THAN
+ # CORRUPT USER DATA. USE SYNC INSTEAD, OR WE WILL SEND NINJA TO YOUR
+ # HOUSE.
+ new_db = SQLCipherDatabase(':memory:', PASSWORD)
+ tmpfile = StringIO()
+ for line in db._db_handle.iterdump():
+ if not 'sqlite_sequence' in line: # work around bug in iterdump
+ tmpfile.write('%s\n' % line)
+ tmpfile.seek(0)
+ new_db._db_handle = dbapi2.connect(':memory:')
+ new_db._db_handle.cursor().executescript(tmpfile.read())
+ new_db._db_handle.commit()
+ new_db._set_replica_uid(db._replica_uid)
+ new_db._factory = db._factory
+ return new_db
+
+
+def make_soledad_app(state):
+ return SoledadApp(state)
+
+
+def make_token_soledad_app(state):
+ app = SoledadApp(state)
+
+ def _verify_authentication_data(uuid, auth_data):
+ if uuid == 'user-uuid' and auth_data == 'auth-token':
+ return True
+ return False
+
+ # we test for action authorization in leap.soledad.common.tests.test_server
+ def _verify_authorization(uuid, environ):
+ return True
+
+ application = SoledadTokenAuthMiddleware(app)
+ application._verify_authentication_data = _verify_authentication_data
+ application._verify_authorization = _verify_authorization
+ return application
+
+
+class CouchServerStateForTests(CouchServerState):
+ """
+ This is a slightly modified CouchDB server state that allows for creating
+ a database.
+
+ Ordinarily, the CouchDB server state does not allow some operations,
+ because for security purposes the Soledad Server should not even have
+ enough permissions to perform them. For tests, we allow database creation,
+ otherwise we'd have to create those databases in setUp/tearDown methods,
+ which is less pleasant than allowing the db to be automatically created.
+ """
+
+ def _create_database(self, dbname):
+ return CouchDatabase.open_database(
+ urljoin(self._couch_url, dbname),
+ True,
+ replica_uid=dbname,
+ ensure_ddocs=True)
+
+ def ensure_database(self, dbname):
+ db = self._create_database(dbname)
+ return db, db.replica_uid
+
+
+class SoledadWithCouchServerMixin(
+ BaseSoledadTest,
+ CouchDBTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ Make sure we have a CouchDB instance for a test.
+ """
+ # from BaseLeapTest
+ cls.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
+ # from CouchDBTestCase
+ cls.wrapper = CouchDBWrapper()
+ cls.wrapper.start()
+ #self.db = self.wrapper.db
+
+ @classmethod
+ def tearDownClass(cls):
+ """
+ Stop CouchDB instance for test.
+ """
+ # from BaseLeapTest
+ soledad_assert(
+ cls.tempdir.startswith('/tmp/leap_tests-'),
+ "beware! tried to remove a dir which does not "
+ "live in temporal folder!")
+ shutil.rmtree(cls.tempdir)
+ # from CouchDBTestCase
+ cls.wrapper.stop()
+
+ def setUp(self):
+ BaseSoledadTest.setUp(self)
+ CouchDBTestCase.setUp(self)
+ main_test_class = getattr(self, 'main_test_class', None)
+ if main_test_class is not None:
+ main_test_class.setUp(self)
+ self._couch_url = 'http://localhost:%d' % self.wrapper.port
+
+ def tearDown(self):
+ BaseSoledadTest.tearDown(self)
+ CouchDBTestCase.tearDown(self)
+ main_test_class = getattr(self, 'main_test_class', None)
+ if main_test_class is not None:
+ main_test_class.tearDown(self)
+ # delete the test database
+ try:
+ db = CouchDatabase(self._couch_url, 'test')
+ db.delete_database()
+ except DatabaseDoesNotExist:
+ pass
+
+ def make_app(self):
+ couch_url = urljoin(
+ 'http://localhost:' + str(self.wrapper.port), 'tests')
+ self.request_state = CouchServerStateForTests(
+ couch_url, 'shared', 'tokens')
+ return self.make_app_with_state(self.request_state)
diff --git a/server/src/leap/soledad/server/__init__.py b/server/src/leap/soledad/server/__init__.py
index 573afdd6..cd006f51 100644
--- a/server/src/leap/soledad/server/__init__.py
+++ b/server/src/leap/soledad/server/__init__.py
@@ -258,6 +258,7 @@ class HTTPInvocationByMethodWithBody(
raise http_app.BadRequest()
+# monkey patch server with new http invocation
http_app.HTTPInvocationByMethodWithBody = HTTPInvocationByMethodWithBody