summaryrefslogtreecommitdiff
path: root/common/src/leap/soledad
diff options
context:
space:
mode:
Diffstat (limited to 'common/src/leap/soledad')
-rw-r--r--common/src/leap/soledad/common/couch.py36
-rw-r--r--common/src/leap/soledad/common/tests/test_couch.py69
-rw-r--r--common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py11
-rw-r--r--common/src/leap/soledad/common/tests/test_server.py58
4 files changed, 99 insertions, 75 deletions
diff --git a/common/src/leap/soledad/common/couch.py b/common/src/leap/soledad/common/couch.py
index a7828e98..8ed704ba 100644
--- a/common/src/leap/soledad/common/couch.py
+++ b/common/src/leap/soledad/common/couch.py
@@ -31,9 +31,10 @@ import threading
from StringIO import StringIO
from collections import defaultdict
+from urlparse import urljoin
-from couchdb.client import Server
+from couchdb.client import Server, Database
from couchdb.http import (
ResourceConflict,
ResourceNotFound,
@@ -380,7 +381,7 @@ class CouchDatabase(CommonBackend):
self._release_fun()
@classmethod
- def open_database(cls, url, create, ensure_ddocs=False):
+ def open_database(cls, url, create, replica_uid=None, ensure_ddocs=False):
"""
Open a U1DB database using CouchDB as backend.
@@ -388,6 +389,8 @@ class CouchDatabase(CommonBackend):
:type url: str
:param create: should the replica be created if it does not exist?
:type create: bool
+ :param replica_uid: an optional unique replica identifier
+ :type replica_uid: str
:param ensure_ddocs: Ensure that the design docs exist on server.
:type ensure_ddocs: bool
@@ -406,10 +409,11 @@ class CouchDatabase(CommonBackend):
except ResourceNotFound:
if not create:
raise DatabaseDoesNotExist()
- return cls(url, dbname, ensure_ddocs=ensure_ddocs)
+ server.create(dbname)
+ return cls(url, dbname, replica_uid=replica_uid, ensure_ddocs=ensure_ddocs)
- def __init__(self, url, dbname, replica_uid=None, full_commit=True,
- session=None, ensure_ddocs=True):
+ def __init__(self, url, dbname, replica_uid=None, ensure_ddocs=True,
+ session=None):
"""
Create a new Couch data container.
@@ -419,31 +423,23 @@ class CouchDatabase(CommonBackend):
:type dbname: str
:param replica_uid: an optional unique replica identifier
:type replica_uid: str
- :param full_commit: turn on the X-Couch-Full-Commit header
- :type full_commit: bool
- :param session: an http.Session instance or None for a default session
- :type session: http.Session
:param ensure_ddocs: Ensure that the design docs exist on server.
:type ensure_ddocs: bool
+ :param session: an http.Session instance or None for a default session
+ :type session: http.Session
"""
# save params
self._url = url
- self._full_commit = full_commit
if session is None:
session = Session(timeout=COUCH_TIMEOUT)
self._session = session
self._factory = CouchDocument
self._real_replica_uid = None
# configure couch
- self._server = Server(url=self._url,
- full_commit=self._full_commit,
- session=self._session)
self._dbname = dbname
- try:
- self._database = self._server[self._dbname]
- except ResourceNotFound:
- self._server.create(self._dbname)
- self._database = self._server[self._dbname]
+ self._database = Database(
+ urljoin(self._url, self._dbname),
+ self._session)
if replica_uid is not None:
self._set_replica_uid(replica_uid)
if ensure_ddocs:
@@ -482,7 +478,8 @@ class CouchDatabase(CommonBackend):
"""
Delete a U1DB CouchDB database.
"""
- del(self._server[self._dbname])
+ server = Server(url=self._url)
+ del(server[self._dbname])
def close(self):
"""
@@ -494,7 +491,6 @@ class CouchDatabase(CommonBackend):
self._url = None
self._full_commit = None
self._session = None
- self._server = None
self._database = None
return True
diff --git a/common/src/leap/soledad/common/tests/test_couch.py b/common/src/leap/soledad/common/tests/test_couch.py
index 86bb4b93..77c46e61 100644
--- a/common/src/leap/soledad/common/tests/test_couch.py
+++ b/common/src/leap/soledad/common/tests/test_couch.py
@@ -25,6 +25,7 @@ import copy
import shutil
from base64 import b64decode
from mock import Mock
+from urlparse import urljoin
from couchdb.client import Server
from u1db import errors as u1db_errors
@@ -151,8 +152,11 @@ class CouchDBTestCase(unittest.TestCase):
class TestCouchBackendImpl(CouchDBTestCase):
def test__allocate_doc_id(self):
- db = couch.CouchDatabase('http://localhost:' + str(self.wrapper.port),
- 'u1db_tests', ensure_ddocs=True)
+ db = couch.CouchDatabase.open_database(
+ urljoin(
+ 'http://localhost:' + str(self.wrapper.port), 'u1db_tests'),
+ create=True,
+ ensure_ddocs=True)
doc_id1 = db._allocate_doc_id()
self.assertTrue(doc_id1.startswith('D-'))
self.assertEqual(34, len(doc_id1))
@@ -166,28 +170,35 @@ class TestCouchBackendImpl(CouchDBTestCase):
def make_couch_database_for_test(test, replica_uid):
port = str(test.wrapper.port)
- return couch.CouchDatabase('http://localhost:' + port, replica_uid,
- replica_uid=replica_uid or 'test',
- ensure_ddocs=True)
+ return couch.CouchDatabase.open_database(
+ urljoin('http://localhost:' + port, replica_uid),
+ create=True,
+ replica_uid=replica_uid or 'test',
+ ensure_ddocs=True)
def copy_couch_database_for_test(test, db):
port = str(test.wrapper.port)
couch_url = 'http://localhost:' + port
new_dbname = db._replica_uid + '_copy'
- new_db = couch.CouchDatabase(couch_url,
- new_dbname,
- replica_uid=db._replica_uid or 'test')
+ new_db = couch.CouchDatabase.open_database(
+ urljoin(couch_url, new_dbname),
+ create=True,
+ replica_uid=db._replica_uid or 'test')
# copy all docs
old_couch_db = Server(couch_url)[db._replica_uid]
new_couch_db = Server(couch_url)[new_dbname]
for doc_id in old_couch_db:
doc = old_couch_db.get(doc_id)
+ # bypass u1db_config document
+ if doc_id == 'u1db_config':
+ pass
# copy design docs
- if ('u1db_rev' not in doc):
+ elif doc_id.startswith('_design'):
+ del doc['_rev']
new_couch_db.save(doc)
# copy u1db docs
- else:
+ elif 'u1db_rev' in doc:
new_doc = {
'_id': doc['_id'],
'u1db_transactions': doc['u1db_transactions'],
@@ -228,7 +239,7 @@ class CouchTests(test_backends.AllDatabaseTests, CouchDBTestCase):
def setUp(self):
test_backends.AllDatabaseTests.setUp(self)
# save db info because of test_close
- self._server = self.db._server
+ self._url = self.db._url
self._dbname = self.db._dbname
def tearDown(self):
@@ -238,7 +249,8 @@ class CouchTests(test_backends.AllDatabaseTests, CouchDBTestCase):
if self.id() == \
'leap.soledad.common.tests.test_couch.CouchTests.' \
'test_close(couch)':
- del(self._server[self._dbname])
+ server = Server(url=self._url)
+ del(server[self._dbname])
else:
self.db.delete_database()
test_backends.AllDatabaseTests.tearDown(self)
@@ -355,10 +367,10 @@ from u1db.backends.inmemory import InMemoryIndex
class IndexedCouchDatabase(couch.CouchDatabase):
- def __init__(self, url, dbname, replica_uid=None, full_commit=True,
- session=None, ensure_ddocs=True):
- old_class.__init__(self, url, dbname, replica_uid, full_commit,
- session, ensure_ddocs=ensure_ddocs)
+ def __init__(self, url, dbname, replica_uid=None, ensure_ddocs=True,
+ session=None):
+ old_class.__init__(self, url, dbname, replica_uid=replica_uid,
+ ensure_ddocs=ensure_ddocs, session=session)
self._indexes = {}
def _put_doc(self, old_doc, doc):
@@ -467,8 +479,9 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
def setUp(self):
CouchDBTestCase.setUp(self)
- self.db = couch.CouchDatabase(
- 'http://127.0.0.1:%d' % self.wrapper.port, 'test',
+ self.db = couch.CouchDatabase.open_database(
+ urljoin('http://127.0.0.1:%d' % self.wrapper.port, 'test'),
+ create=True,
ensure_ddocs=False) # note that we don't enforce ddocs here
def tearDown(self):
@@ -509,8 +522,9 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
Test that all methods that access design documents list functions
will raise if the functions are not present.
"""
- self.db = couch.CouchDatabase(
- 'http://127.0.0.1:%d' % self.wrapper.port, 'test',
+ self.db = couch.CouchDatabase.open_database(
+ urljoin('http://127.0.0.1:%d' % self.wrapper.port, 'test'),
+ create=True,
ensure_ddocs=True)
# erase views from _design/transactions
transactions = self.db._database['_design/transactions']
@@ -538,8 +552,9 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
Test that all methods that access design documents list functions
will raise if the functions are not present.
"""
- self.db = couch.CouchDatabase(
- 'http://127.0.0.1:%d' % self.wrapper.port, 'test',
+ self.db = couch.CouchDatabase.open_database(
+ urljoin('http://127.0.0.1:%d' % self.wrapper.port, 'test'),
+ create=True,
ensure_ddocs=True)
# erase views from _design/transactions
transactions = self.db._database['_design/transactions']
@@ -567,8 +582,9 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
Test that all methods that access design documents' named views will
raise if the views are not present.
"""
- self.db = couch.CouchDatabase(
- 'http://127.0.0.1:%d' % self.wrapper.port, 'test',
+ self.db = couch.CouchDatabase.open_database(
+ urljoin('http://127.0.0.1:%d' % self.wrapper.port, 'test'),
+ create=True,
ensure_ddocs=True)
# erase views from _design/docs
docs = self.db._database['_design/docs']
@@ -608,8 +624,9 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
Test that all methods that access design documents will raise if the
design docs are not present.
"""
- self.db = couch.CouchDatabase(
- 'http://127.0.0.1:%d' % self.wrapper.port, 'test',
+ self.db = couch.CouchDatabase.open_database(
+ urljoin('http://127.0.0.1:%d' % self.wrapper.port, 'test'),
+ create=True,
ensure_ddocs=True)
# delete _design/docs
del self.db._database['_design/docs']
diff --git a/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py b/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py
index 3c457cc5..3c219b91 100644
--- a/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py
+++ b/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py
@@ -24,6 +24,10 @@ import mock
import tempfile
import threading
+
+from urlparse import urljoin
+
+
from leap.soledad.client import Soledad
from leap.soledad.common.couch import CouchDatabase, CouchServerState
from leap.soledad.common.tests.test_couch import CouchDBTestCase
@@ -101,8 +105,11 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
TestCaseWithServer.setUp(self)
CouchDBTestCase.setUp(self)
self._couch_url = 'http://localhost:' + str(self.wrapper.port)
- self.db = CouchDatabase(
- self._couch_url, 'user-user-uuid', replica_uid='replica')
+ self.db = CouchDatabase.open_database(
+ urljoin(self._couch_url, 'user-user-uuid'),
+ create=True,
+ replica_uid='replica',
+ ensure_ddocs=True)
self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
def tearDown(self):
diff --git a/common/src/leap/soledad/common/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py
index f8d2a64f..6fe9211c 100644
--- a/common/src/leap/soledad/common/tests/test_server.py
+++ b/common/src/leap/soledad/common/tests/test_server.py
@@ -27,6 +27,7 @@ import mock
import time
import binascii
+from urlparse import urljoin
from leap.common.testing.basetest import BaseLeapTest
from leap.soledad.common.couch import (
@@ -56,7 +57,8 @@ from leap.soledad.server.auth import URLToAuthorization
def _couch_ensure_database(self, dbname):
db = CouchDatabase.open_database(
self._couch_url + '/' + dbname,
- create=True)
+ create=True,
+ ensure_ddocs=True)
return db, db._replica_uid
CouchServerState.ensure_database = _couch_ensure_database
@@ -352,11 +354,10 @@ class EncryptedSyncTestCase(
self.assertEqual([], doclist)
doc1 = sol1.create_doc(json.loads(simple_doc))
# ensure remote db exists before syncing
- db = CouchDatabase(
- self._couch_url,
- # the name of the user database is "user-<uuid>".
- 'user-user-uuid',
- )
+ db = CouchDatabase.open_database(
+ urljoin(self._couch_url, 'user-user-uuid'),
+ create=True,
+ ensure_ddocs=True)
# sync with server
sol1._server_url = self.getURL()
sol1.sync()
@@ -408,11 +409,10 @@ class EncryptedSyncTestCase(
self.assertEqual([], doclist)
doc1 = sol1.create_doc(json.loads(simple_doc))
# ensure remote db exists before syncing
- db = CouchDatabase(
- self._couch_url,
- # the name of the user database is "user-<uuid>".
- 'user-user-uuid',
- )
+ db = CouchDatabase.open_database(
+ urljoin(self._couch_url, 'user-user-uuid'),
+ create=True,
+ ensure_ddocs=True)
# sync with server
sol1._server_url = self.getURL()
sol1.sync()
@@ -468,11 +468,10 @@ class EncryptedSyncTestCase(
content = binascii.hexlify(os.urandom(length/2)) # len() == length
doc1 = sol1.create_doc({'data': content})
# ensure remote db exists before syncing
- db = CouchDatabase(
- self._couch_url,
- # the name of the user database is "user-<uuid>".
- 'user-user-uuid',
- )
+ db = CouchDatabase.open_database(
+ urljoin(self._couch_url, 'user-user-uuid'),
+ create=True,
+ ensure_ddocs=True)
# sync with server
sol1._server_url = self.getURL()
sol1.sync()
@@ -512,11 +511,10 @@ class EncryptedSyncTestCase(
for i in range(0, number_of_docs):
sol1.create_doc(json.loads(simple_doc))
# ensure remote db exists before syncing
- db = CouchDatabase(
- self._couch_url,
- # the name of the user database is "user-<uuid>".
- 'user-user-uuid',
- )
+ db = CouchDatabase.open_database(
+ urljoin(self._couch_url, 'user-user-uuid'),
+ create=True,
+ ensure_ddocs=True)
# sync with server
sol1._server_url = self.getURL()
sol1.sync()
@@ -558,8 +556,14 @@ class LockResourceTestCase(
self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
self._couch_url = 'http://localhost:' + str(self.wrapper.port)
# create the databases
- CouchDatabase(self._couch_url, 'shared')
- CouchDatabase(self._couch_url, 'tokens')
+ CouchDatabase.open_database(
+ urljoin(self._couch_url, 'shared'),
+ create=True,
+ ensure_ddocs=True)
+ CouchDatabase.open_database(
+ urljoin(self._couch_url, 'tokens'),
+ create=True,
+ ensure_ddocs=True)
self._state = CouchServerState(
self._couch_url, 'shared', 'tokens')
@@ -567,10 +571,10 @@ class LockResourceTestCase(
CouchDBTestCase.tearDown(self)
TestCaseWithServer.tearDown(self)
# delete remote database
- db = CouchDatabase(
- self._couch_url,
- 'shared',
- )
+ db = CouchDatabase.open_database(
+ urljoin(self._couch_url, 'shared'),
+ create=True,
+ ensure_ddocs=True)
db.delete_database()
def test__try_obtain_filesystem_lock(self):