summaryrefslogtreecommitdiff
path: root/common/src
diff options
context:
space:
mode:
Diffstat (limited to 'common/src')
-rw-r--r--common/src/leap/soledad/common/tests/__init__.py284
-rw-r--r--common/src/leap/soledad/common/tests/test_couch.py164
-rw-r--r--common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py206
-rw-r--r--common/src/leap/soledad/common/tests/test_crypto.py17
-rw-r--r--common/src/leap/soledad/common/tests/test_http.py5
-rw-r--r--common/src/leap/soledad/common/tests/test_http_client.py11
-rw-r--r--common/src/leap/soledad/common/tests/test_https.py42
-rw-r--r--common/src/leap/soledad/common/tests/test_server.py352
-rw-r--r--common/src/leap/soledad/common/tests/test_soledad.py166
-rw-r--r--common/src/leap/soledad/common/tests/test_soledad_app.py59
-rw-r--r--common/src/leap/soledad/common/tests/test_soledad_doc.py24
-rw-r--r--common/src/leap/soledad/common/tests/test_sqlcipher.py62
-rw-r--r--common/src/leap/soledad/common/tests/test_sqlcipher_sync.py49
-rw-r--r--common/src/leap/soledad/common/tests/test_sync.py153
-rw-r--r--common/src/leap/soledad/common/tests/test_sync_deferred.py150
-rw-r--r--common/src/leap/soledad/common/tests/test_sync_target.py285
-rw-r--r--common/src/leap/soledad/common/tests/test_target.py797
-rw-r--r--common/src/leap/soledad/common/tests/test_target_soledad.py102
-rw-r--r--common/src/leap/soledad/common/tests/u1db_tests/__init__.py4
-rw-r--r--common/src/leap/soledad/common/tests/u1db_tests/test_https.py2
-rw-r--r--common/src/leap/soledad/common/tests/util.py352
21 files changed, 1116 insertions, 2170 deletions
diff --git a/common/src/leap/soledad/common/tests/__init__.py b/common/src/leap/soledad/common/tests/__init__.py
index f8253409..acebb77b 100644
--- a/common/src/leap/soledad/common/tests/__init__.py
+++ b/common/src/leap/soledad/common/tests/__init__.py
@@ -19,291 +19,9 @@
"""
Tests to make sure Soledad provides U1DB functionality and more.
"""
-import os
-import random
-import string
-import u1db
-from mock import Mock
-
-
-from leap.soledad.common.document import SoledadDocument
-from leap.soledad.common.crypto import ENC_SCHEME_KEY
-from leap.soledad.client import Soledad
-from leap.soledad.client.crypto import decrypt_doc_dict
-from leap.common.testing.basetest import BaseLeapTest
-
-
-#-----------------------------------------------------------------------------
-# Some tests inherit from BaseSoledadTest in order to have a working Soledad
-# instance in each test.
-#-----------------------------------------------------------------------------
-
-ADDRESS = 'leap@leap.se'
-
-
-class BaseSoledadTest(BaseLeapTest):
- """
- Instantiates Soledad for usage in tests.
- """
- defer_sync_encryption = False
-
- def setUp(self):
- # config info
- self.db1_file = os.path.join(self.tempdir, "db1.u1db")
- self.db2_file = os.path.join(self.tempdir, "db2.u1db")
- self.email = ADDRESS
- # open test dbs
- self._db1 = u1db.open(self.db1_file, create=True,
- document_factory=SoledadDocument)
- self._db2 = u1db.open(self.db2_file, create=True,
- document_factory=SoledadDocument)
- # get a random prefix for each test, so we do not mess with
- # concurrency during initialization and shutting down of
- # each local db.
- self.rand_prefix = ''.join(
- map(lambda x: random.choice(string.ascii_letters), range(6)))
- # initialize soledad by hand so we can control keys
- self._soledad = self._soledad_instance(
- prefix=self.rand_prefix, user=self.email)
-
- def tearDown(self):
- self._db1.close()
- self._db2.close()
- self._soledad.close()
-
- # XXX should not access "private" attrs
- for f in [self._soledad._local_db_path, self._soledad._secrets_path]:
- if os.path.isfile(f):
- os.unlink(f)
- def get_default_shared_mock(self, put_doc_side_effect):
- """
- Get a default class for mocking the shared DB
- """
- class defaultMockSharedDB(object):
- get_doc = Mock(return_value=None)
- put_doc = Mock(side_effect=put_doc_side_effect)
- lock = Mock(return_value=('atoken', 300))
- unlock = Mock(return_value=True)
- def __call__(self):
- return self
- return defaultMockSharedDB
-
- def _soledad_instance(self, user=ADDRESS, passphrase=u'123',
- prefix='',
- secrets_path='secrets.json',
- local_db_path='soledad.u1db', server_url='',
- cert_file=None, secret_id=None,
- shared_db_class=None):
-
- def _put_doc_side_effect(doc):
- self._doc_put = doc
-
- if shared_db_class is not None:
- MockSharedDB = shared_db_class
- else:
- MockSharedDB = self.get_default_shared_mock(
- _put_doc_side_effect)
-
- Soledad._shared_db = MockSharedDB()
- return Soledad(
- user,
- passphrase,
- secrets_path=os.path.join(
- self.tempdir, prefix, secrets_path),
- local_db_path=os.path.join(
- self.tempdir, prefix, local_db_path),
- server_url=server_url, # Soledad will fail if not given an url.
- cert_file=cert_file,
- secret_id=secret_id,
- defer_encryption=self.defer_sync_encryption)
-
- def assertGetEncryptedDoc(
- self, db, doc_id, doc_rev, content, has_conflicts):
- """
- Assert that the document in the database looks correct.
- """
- exp_doc = self.make_document(doc_id, doc_rev, content,
- has_conflicts=has_conflicts)
- doc = db.get_doc(doc_id)
-
- if ENC_SCHEME_KEY in doc.content:
- # XXX check for SYM_KEY too
- key = self._soledad._crypto.doc_passphrase(doc.doc_id)
- secret = self._soledad._crypto.secret
- decrypted = decrypt_doc_dict(
- doc.content, doc.doc_id, doc.rev,
- key, secret)
- doc.set_json(decrypted)
- self.assertEqual(exp_doc.doc_id, doc.doc_id)
- self.assertEqual(exp_doc.rev, doc.rev)
- self.assertEqual(exp_doc.has_conflicts, doc.has_conflicts)
- self.assertEqual(exp_doc.content, doc.content)
-
-
-# Key material for testing
-KEY_FINGERPRINT = "E36E738D69173C13D709E44F2F455E2824D18DDF"
-PUBLIC_KEY = """
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v1.4.10 (GNU/Linux)
-
-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
-=MuOY
------END PGP PUBLIC KEY BLOCK-----
-"""
-PRIVATE_KEY = """
------BEGIN PGP PRIVATE KEY BLOCK-----
-Version: GnuPG v1.4.10 (GNU/Linux)
-
-lQcYBFC9+dkBEADNRfwV23TWEoGc/x0wWH1P7PlXt8MnC2Z1kKaKKmfnglVrpOiz
-iLWoiU58sfZ0L5vHkzXHXCBf6Eiy/EtUIvdiWAn+yASJ1mk5jZTBKO/WMAHD8wTO
-zpMsFmWyg3xc4DkmFa9KQ5EVU0o/nqPeyQxNMQN7px5pPwrJtJFmPxnxm+aDkPYx
-irDmz/4DeDNqXliazGJKw7efqBdlwTHkl9Akw2gwy178pmsKwHHEMOBOFFvX61AT
-huKqHYmlCGSliwbrJppTG7jc1/ls3itrK+CWTg4txREkSpEVmfcASvw/ZqLbjgfs
-d/INMwXnR9U81O8+7LT6yw/ca4ppcFoJD7/XJbkRiML6+bJ4Dakiy6i727BzV17g
-wI1zqNvm5rAhtALKfACha6YO43aJzairO4II1wxVHvRDHZn2IuKDDephQ3Ii7/vb
-hUOf6XCSmchkAcpKXUOvbxm1yfB1LRa64mMc2RcZxf4mW7KQkulBsdV5QG2276lv
-U2UUy2IutXcGP5nXC+f6sJJGJeEToKJ57yiO/VWJFjKN8SvP+7AYsQSqINUuEf6H
-T5gCPCraGMkTUTPXrREvu7NOohU78q6zZNaL3GW8ai7eSeANSuQ8Vzffx7Wd8Y7i
-Pw9sYj0SMFs1UgjbuL6pO5ueHh+qyumbtAq2K0Bci0kqOcU4E9fNtdiovQARAQAB
-AA/+JHtlL39G1wsH9R6UEfUQJGXR9MiIiwZoKcnRB2o8+DS+OLjg0JOh8XehtuCs
-E/8oGQKtQqa5bEIstX7IZoYmYFiUQi9LOzIblmp2vxOm+HKkxa4JszWci2/ZmC3t
-KtaA4adl9XVnshoQ7pijuCMUKB3naBEOAxd8s9d/JeReGIYkJErdrnVfNk5N71Ds
-FmH5Ll3XtEDvgBUQP3nkA6QFjpsaB94FHjL3gDwum/cxzj6pCglcvHOzEhfY0Ddb
-J967FozQTaf2JW3O+w3LOqtcKWpq87B7+O61tVidQPSSuzPjCtFF0D2LC9R/Hpky
-KTMQ6CaKja4MPhjwywd4QPcHGYSqjMpflvJqi+kYIt8psUK/YswWjnr3r4fbuqVY
-VhtiHvnBHQjz135lUqWvEz4hM3Xpnxydx7aRlv5NlevK8+YIO5oFbWbGNTWsPZI5
-jpoFBpSsnR1Q5tnvtNHauvoWV+XN2qAOBTG+/nEbDYH6Ak3aaE9jrpTdYh0CotYF
-q7csANsDy3JvkAzeU6WnYpsHHaAjqOGyiZGsLej1UcXPFMosE/aUo4WQhiS8Zx2c
-zOVKOi/X5vQ2GdNT9Qolz8AriwzsvFR+bxPzyd8V6ALwDsoXvwEYinYBKK8j0OPv
-OOihSR6HVsuP9NUZNU9ewiGzte/+/r6pNXHvR7wTQ8EWLcEIAN6Zyrb0bHZTIlxt
-VWur/Ht2mIZrBaO50qmM5RD3T5oXzWXi/pjLrIpBMfeZR9DWfwQwjYzwqi7pxtYx
-nJvbMuY505rfnMoYxb4J+cpRXV8MS7Dr1vjjLVUC9KiwSbM3gg6emfd2yuA93ihv
-Pe3mffzLIiQa4mRE3wtGcioC43nWuV2K2e1KjxeFg07JhrezA/1Cak505ab/tmvP
-4YmjR5c44+yL/YcQ3HdFgs4mV+nVbptRXvRcPpolJsgxPccGNdvHhsoR4gwXMS3F
-RRPD2z6x8xeN73Q4KH3bm01swQdwFBZbWVfmUGLxvN7leCdfs9+iFJyqHiCIB6Iv
-mQfp8F0IAOwSo8JhWN+V1dwML4EkIrM8wUb4yecNLkyR6TpPH/qXx4PxVMC+vy6x
-sCtjeHIwKE+9vqnlhd5zOYh7qYXEJtYwdeDDmDbL8oks1LFfd+FyAuZXY33DLwn0
-cRYsr2OEZmaajqUB3NVmj3H4uJBN9+paFHyFSXrH68K1Fk2o3n+RSf2EiX+eICwI
-L6rqoF5sSVUghBWdNegV7qfy4anwTQwrIMGjgU5S6PKW0Dr/3iO5z3qQpGPAj5OW
-ATqPWkDICLbObPxD5cJlyyNE2wCA9VVc6/1d6w4EVwSq9h3/WTpATEreXXxTGptd
-LNiTA1nmakBYNO2Iyo3djhaqBdWjk+EIAKtVEnJH9FAVwWOvaj1RoZMA5DnDMo7e
-SnhrCXl8AL7Z1WInEaybasTJXn1uQ8xY52Ua4b8cbuEKRKzw/70NesFRoMLYoHTO
-dyeszvhoDHberpGRTciVmpMu7Hyi33rM31K9epA4ib6QbbCHnxkWOZB+Bhgj1hJ8
-xb4RBYWiWpAYcg0+DAC3w9gfxQhtUlZPIbmbrBmrVkO2GVGUj8kH6k4UV6kUHEGY
-HQWQR0HcbKcXW81ZXCCD0l7ROuEWQtTe5Jw7dJ4/QFuqZnPutXVRNOZqpl6eRShw
-7X2/a29VXBpmHA95a88rSQsL+qm7Fb3prqRmuMCtrUZgFz7HLSTuUMR867QcTGVh
-cCBUZXN0IEtleSA8bGVhcEBsZWFwLnNlPokCNwQTAQgAIQUCUL352QIbAwULCQgH
-AwUVCgkICwUWAgMBAAIeAQIXgAAKCRAvRV4oJNGN30+xEACh9yLkZ4jqW0/wwyIM
-MI896MQf1tAwzMj16MJYUjrjNK4Bn57QaQW926HsxF8C/OjT0MTRhq7heYZJnnEo
-rj0rzpkJapUveTRkKeoTRtGGigqJYfkOTU7KRVwgJBXIfaKlI3tC3cX0j0H1fVKX
-hLxsj5pNSPRCVf2A5mePg44HtXe6oVWSJ8+EcdTa0shf03NhAtFaY0BbFGPSm9mA
-QUe4rxugwXPLctIyV4uweFo5BXFBCb4kKTBdnQi3aJwnoWLNT6rDdTe4/nhY0Hfo
-alTCYGLkhio77gBHwpTOjEMO/hZhcDMi4CvxMPw7bRxAwq4u+0j0pDhkiLcQs4U4
-Ou/fH+pia+1nF5h19cNVXIm+RX2fL0wxVYc/14AIAK3YT6PVev9XYEkogSj0P7Kb
-HKOruYpnToXJBERNJZwGL1U+ihPNUyroRf29t7u8flnXsOpCtBEIWAO8Muy5pWjV
-3O6zAUCfWetAieCQ7WrQVmdJDa7dlX3Qx1XagUzqZdAq2jVI1hOWDA2rKytnReSF
-/A97rmLaWZ8aoNCs8i4NLcy9Lbzi9QtornYGVCEmTTym0tM9L/mn7gAJ8dqUwt7n
-s24dibfElky4ZZeItD+D7OZGeh0FDuejvv2dXFqL1/pkHpGBZhEckg0fZ95NbgMC
-4pSZkZnqRpr2GwfB5aFfB6sIIJ0HGARQvfnZARAAtCP8Z9bm6KzIA7wbXx9LBIcJ
-1wQvOPf99s4nOrnQev9xH5PZ820qS9xUjrlyE2bGYAhz5Cmq56ENs7THErIdZHtQ
-uYEBprO+VFZjP50vtmCOL2PDl/xgv6J9r1Mp3KnR/m0esR+YceDW1qX07IkB5s+Q
-us80v5LmmxnWcikWmR7dt1kOyV/+M6Y6mwvfQ4x3D/QUpO7SfMCOG5DGA7hVUHU/
-Tuh8MihmMFFOLAEEQI+1wkxr1W09HaYCcB+EZqxLSaBwMeFoYPJie9dBNBgps39o
-6pDbjsO+or4JNuyoHvh8NNI5iY0IR2NMlW4mqCcHEazys2koxFTYK6YD95Vz0RkA
-K4BErCDk3lVR0PH4StmLU3gmPayIjvi9Dl9saPRyu4Xx2WVi+q6spl3ckn4c4f3+
-iD8hxVp74+wa5ew0fIXjIpMoHCar/nndsse4i8glCddINdiOPPmhI9Wi3nT+5Z2t
-9omPP2dEh0CzR+j1zvUpT3KtmhVICqhO+QP9BTJOwrp81NTlq9mbUyzTtVk/9dy3
-zoYbhKvY08k6LJ9FsQYySqtfJZ4cwl5WsOhALWwOwlMLA9wkz0eemgFxStyOylzl
-QKoIK7zHuU6XYOXa32KSPIWaLy+WgIG/u2ObWtdE3CXVIUuSt5BQFnv7XVNHJllD
-Az9VDEkOSYOiSEFVoUsAEQEAAQAP/1AagnZQZyzHDEgw4QELAspYHCWLXE5aZInX
-wTUJhK31IgIXNn9bJ0hFiSpQR2xeMs9oYtRuPOu0P8oOFMn4/z374fkjZy8QVY3e
-PlL+3EUeqYtkMwlGNmVw5a/NbNuNfm5Darb7pEfbYd1gPcni4MAYw7R2SG/57GbC
-9gucvspHIfOSfBNLBthDzmK8xEKe1yD2eimfc2T7IRYb6hmkYfeds5GsqvGI6mwI
-85h4uUHWRc5JOlhVM6yX8hSWx0L60Z3DZLChmc8maWnFXd7C8eQ6P1azJJbW71Ih
-7CoK0XW4LE82vlQurSRFgTwfl7wFYszW2bOzCuhHDDtYnwH86Nsu0DC78ZVRnvxn
-E8Ke/AJgrdhIOo4UAyR+aZD2+2mKd7/waOUTUrUtTzc7i8N3YXGi/EIaNReBXaq+
-ZNOp24BlFzRp+FCF/pptDW9HjPdiV09x0DgICmeZS4Gq/4vFFIahWctg52NGebT0
-Idxngjj+xDtLaZlLQoOz0n5ByjO/Wi0ANmMv1sMKCHhGvdaSws2/PbMR2r4caj8m
-KXpIgdinM/wUzHJ5pZyF2U/qejsRj8Kw8KH/tfX4JCLhiaP/mgeTuWGDHeZQERAT
-xPmRFHaLP9/ZhvGNh6okIYtrKjWTLGoXvKLHcrKNisBLSq+P2WeFrlme1vjvJMo/
-jPwLT5o9CADQmcbKZ+QQ1ZM9v99iDZol7SAMZX43JC019sx6GK0u6xouJBcLfeB4
-OXacTgmSYdTa9RM9fbfVpti01tJ84LV2SyL/VJq/enJF4XQPSynT/tFTn1PAor6o
-tEAAd8fjKdJ6LnD5wb92SPHfQfXqI84rFEO8rUNIE/1ErT6DYifDzVCbfD2KZdoF
-cOSp7TpD77sY1bs74ocBX5ejKtd+aH99D78bJSMM4pSDZsIEwnomkBHTziubPwJb
-OwnATy0LmSMAWOw5rKbsh5nfwCiUTM20xp0t5JeXd+wPVWbpWqI2EnkCEN+RJr9i
-7dp/ymDQ+Yt5wrsN3NwoyiexPOG91WQVCADdErHsnglVZZq9Z8Wx7KwecGCUurJ2
-H6lKudv5YOxPnAzqZS5HbpZd/nRTMZh2rdXCr5m2YOuewyYjvM757AkmUpM09zJX
-MQ1S67/UX2y8/74TcRF97Ncx9HeELs92innBRXoFitnNguvcO6Esx4BTe1OdU6qR
-ER3zAmVf22Le9ciXbu24DN4mleOH+OmBx7X2PqJSYW9GAMTsRB081R6EWKH7romQ
-waxFrZ4DJzZ9ltyosEJn5F32StyLrFxpcrdLUoEaclZCv2qka7sZvi0EvovDVEBU
-e10jOx9AOwf8Gj2ufhquQ6qgVYCzbP+YrodtkFrXRS3IsljIchj1M2ffB/0bfoUs
-rtER9pLvYzCjBPg8IfGLw0o754Qbhh/ReplCRTusP/fQMybvCvfxreS3oyEriu/G
-GufRomjewZ8EMHDIgUsLcYo2UHZsfF7tcazgxMGmMvazp4r8vpgrvW/8fIN/6Adu
-tF+WjWDTvJLFJCe6O+BFJOWrssNrrra1zGtLC1s8s+Wfpe+bGPL5zpHeebGTwH1U
-22eqgJArlEKxrfarz7W5+uHZJHSjF/K9ZvunLGD0n9GOPMpji3UO3zeM8IYoWn7E
-/EWK1XbjnssNemeeTZ+sDh+qrD7BOi+vCX1IyBxbfqnQfJZvmcPWpruy1UsO+aIC
-0GY8Jr3OL69dDQ21jueJAh8EGAEIAAkFAlC9+dkCGwwACgkQL0VeKCTRjd9HCw/+
-LQSVgLLF4ulYlPCjWIIuQwrPbJfWUVVr2dPUFVM85DCv8gBzk5c121snXh9Swovm
-laBbw6ate3BmbXLh64jVE9Za5sbTWi7PCcbO/bpRy4d6oLmitmNw6cq0vjTLxUYy
-bwuiJxWREkfxuU85EKdouN062YDevH+/YResmlJrcCE7LRlJFeRlKsrrwBU3BqYd
-GgFJjKjQC1peeQ9fj62Y7xfwE9+PXbkiWO5u/Bk8hb1VZH1SoIRU98NHVcp6BVvp
-VK0jLAXuSauSczULmpRjbyt1lhaAqivDTWEEZXiNNbRyp17c3nVdPWOcgBr42hdQ
-z25CgZgyLCsvu82wuXLKJblrIPJX3Yf+si6KqEWBsmwdOWybsjygaF5HvzgFqAAD
-U0goPWoQ71PorP2XOUNp5ZLkBQp5etvtkksjVNMIhnHn8PGMuoxO39EUGlWj2B5l
-Cu8tSosAzB1pS8NcLZzoNoI9dOHrmgJmP+GrOUkcf5GhNZbMoj4GNfGBRYX0SZlQ
-GuDrwNKYj73C4MWyNnnUFyq8nDHJ/G1NpaF2hiof9RBL4PUU/f92JkceXPBXA8gL
-Mz2ig1OButwPPLFGQhWqxXAGrsS3Ny+BhTJfnfIbbkaLLphBpDZm1D9XKbAUvdd1
-RZXoH+FTg9UAW87eqU610npOkT6cRaBxaMK/mDtGNdc=
-=JTFu
------END PGP PRIVATE KEY BLOCK-----
-"""
+import os
def load_tests():
diff --git a/common/src/leap/soledad/common/tests/test_couch.py b/common/src/leap/soledad/common/tests/test_couch.py
index 10d6c136..d2aef9bb 100644
--- a/common/src/leap/soledad/common/tests/test_couch.py
+++ b/common/src/leap/soledad/common/tests/test_couch.py
@@ -20,134 +20,21 @@
Test ObjectStore and Couch backend bits.
"""
-import re
-import copy
-import shutil
-from base64 import b64decode
-from mock import Mock
-from urlparse import urljoin
+import simplejson as json
+
+from urlparse import urljoin
from u1db import errors as u1db_errors
from couchdb.client import Server
-from leap.common.files import mkdir_p
+from testscenarios import TestWithScenarios
+
+from leap.soledad.common import couch, errors
from leap.soledad.common.tests import u1db_tests as tests
from leap.soledad.common.tests.u1db_tests import test_backends
from leap.soledad.common.tests.u1db_tests import test_sync
-from leap.soledad.common import couch, errors
-import simplejson as json
-
-
-#-----------------------------------------------------------------------------
-# A wrapper for running couchdb locally.
-#-----------------------------------------------------------------------------
-
-import re
-import os
-import tempfile
-import subprocess
-import time
-import unittest
-
-
-# from: https://github.com/smcq/paisley/blob/master/paisley/test/util.py
-# TODO: include license of above project.
-class CouchDBWrapper(object):
- """
- Wrapper for external CouchDB instance which is started and stopped for
- testing.
- """
-
- def start(self):
- """
- Start a CouchDB instance for a test.
- """
- self.tempdir = tempfile.mkdtemp(suffix='.couch.test')
-
- path = os.path.join(os.path.dirname(__file__),
- 'couchdb.ini.template')
- handle = open(path)
- conf = handle.read() % {
- 'tempdir': self.tempdir,
- }
- handle.close()
-
- confPath = os.path.join(self.tempdir, 'test.ini')
- handle = open(confPath, 'w')
- handle.write(conf)
- handle.close()
-
- # create the dirs from the template
- mkdir_p(os.path.join(self.tempdir, 'lib'))
- mkdir_p(os.path.join(self.tempdir, 'log'))
- args = ['couchdb', '-n', '-a', confPath]
- null = open('/dev/null', 'w')
-
- self.process = subprocess.Popen(
- args, env=None, stdout=null.fileno(), stderr=null.fileno(),
- close_fds=True)
- # find port
- logPath = os.path.join(self.tempdir, 'log', 'couch.log')
- while not os.path.exists(logPath):
- if self.process.poll() is not None:
- got_stdout, got_stderr = "", ""
- if self.process.stdout is not None:
- got_stdout = self.process.stdout.read()
-
- if self.process.stderr is not None:
- got_stderr = self.process.stderr.read()
- raise Exception("""
-couchdb exited with code %d.
-stdout:
-%s
-stderr:
-%s""" % (
- self.process.returncode, got_stdout, got_stderr))
- time.sleep(0.01)
- while os.stat(logPath).st_size == 0:
- time.sleep(0.01)
- PORT_RE = re.compile(
- 'Apache CouchDB has started on http://127.0.0.1:(?P<port>\d+)')
-
- handle = open(logPath)
- line = handle.read()
- handle.close()
- m = PORT_RE.search(line)
- if not m:
- self.stop()
- raise Exception("Cannot find port in line %s" % line)
- self.port = int(m.group('port'))
-
- def stop(self):
- """
- Terminate the CouchDB instance.
- """
- self.process.terminate()
- self.process.communicate()
- shutil.rmtree(self.tempdir)
-
-
-class CouchDBTestCase(unittest.TestCase):
- """
- TestCase base class for tests against a real CouchDB server.
- """
-
- @classmethod
- def setUpClass(cls):
- """
- Make sure we have a CouchDB instance for a test.
- """
- cls.wrapper = CouchDBWrapper()
- cls.wrapper.start()
- #self.db = self.wrapper.db
-
- @classmethod
- def tearDownClass(cls):
- """
- Stop CouchDB instance for test.
- """
- cls.wrapper.stop()
+from leap.soledad.common.tests.util import CouchDBTestCase
#-----------------------------------------------------------------------------
@@ -239,7 +126,8 @@ COUCH_SCENARIOS = [
]
-class CouchTests(test_backends.AllDatabaseTests, CouchDBTestCase):
+class CouchTests(
+ TestWithScenarios, test_backends.AllDatabaseTests, CouchDBTestCase):
scenarios = COUCH_SCENARIOS
@@ -262,7 +150,8 @@ class CouchTests(test_backends.AllDatabaseTests, CouchDBTestCase):
test_backends.AllDatabaseTests.tearDown(self)
-class CouchDatabaseTests(test_backends.LocalDatabaseTests, CouchDBTestCase):
+class CouchDatabaseTests(
+ TestWithScenarios, test_backends.LocalDatabaseTests, CouchDBTestCase):
scenarios = COUCH_SCENARIOS
@@ -271,7 +160,7 @@ class CouchDatabaseTests(test_backends.LocalDatabaseTests, CouchDBTestCase):
test_backends.LocalDatabaseTests.tearDown(self)
-class CouchValidateGenNTransIdTests(
+class CouchValidateGenNTransIdTests(TestWithScenarios,
test_backends.LocalDatabaseValidateGenNTransIdTests, CouchDBTestCase):
scenarios = COUCH_SCENARIOS
@@ -281,7 +170,7 @@ class CouchValidateGenNTransIdTests(
test_backends.LocalDatabaseValidateGenNTransIdTests.tearDown(self)
-class CouchValidateSourceGenTests(
+class CouchValidateSourceGenTests(TestWithScenarios,
test_backends.LocalDatabaseValidateSourceGenTests, CouchDBTestCase):
scenarios = COUCH_SCENARIOS
@@ -291,7 +180,7 @@ class CouchValidateSourceGenTests(
test_backends.LocalDatabaseValidateSourceGenTests.tearDown(self)
-class CouchWithConflictsTests(
+class CouchWithConflictsTests(TestWithScenarios,
test_backends.LocalDatabaseWithConflictsTests, CouchDBTestCase):
scenarios = COUCH_SCENARIOS
@@ -325,23 +214,11 @@ simple_doc = tests.simple_doc
nested_doc = tests.nested_doc
-class CouchDatabaseSyncTargetTests(test_sync.DatabaseSyncTargetTests,
- CouchDBTestCase):
+class CouchDatabaseSyncTargetTests(
+ TestWithScenarios, test_sync.DatabaseSyncTargetTests, CouchDBTestCase):
scenarios = (tests.multiply_scenarios(COUCH_SCENARIOS, target_scenarios))
- def setUp(self):
- # we implement parents' setUp methods here to prevent from launching
- # more couch instances then needed.
- tests.TestCase.setUp(self)
- self.server = self.server_thread = None
- self.db, self.st = self.create_db_and_target(self)
- self.other_changes = []
-
- def tearDown(self):
- self.db.delete_database()
- test_sync.DatabaseSyncTargetTests.tearDown(self)
-
def test_sync_exchange_returns_many_new_docs(self):
# This test was replicated to allow dictionaries to be compared after
# JSON expansion (because one dictionary may have many different
@@ -372,7 +249,7 @@ from u1db.backends.inmemory import InMemoryIndex
class IndexedCouchDatabase(couch.CouchDatabase):
def __init__(self, url, dbname, replica_uid=None, ensure_ddocs=True):
- old_class.__init__(self, url, dbname, replica_uid=replica_uid,
+ old_class.__init__(self, url, dbname, replica_uid=replica_uid,
ensure_ddocs=ensure_ddocs)
self._indexes = {}
@@ -458,7 +335,8 @@ for name, scenario in COUCH_SCENARIOS:
scenario = dict(scenario)
-class CouchDatabaseSyncTests(test_sync.DatabaseSyncTests, CouchDBTestCase):
+class CouchDatabaseSyncTests(
+ TestWithScenarios, test_sync.DatabaseSyncTests, CouchDBTestCase):
scenarios = sync_scenarios
@@ -498,6 +376,7 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
def tearDown(self):
self.db.delete_database()
self.db.close()
+ CouchDBTestCase.tearDown(self)
def test_missing_design_doc_raises(self):
"""
@@ -670,6 +549,3 @@ class CouchDatabaseExceptionsTests(CouchDBTestCase):
self.assertRaises(
errors.MissingDesignDocDeletedError,
self.db._do_set_replica_gen_and_trans_id, 1, 2, 3)
-
-
-load_tests = tests.load_with_scenarios
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 6465eb80..83cee469 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
@@ -15,26 +15,25 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-Test atomocity for couch operations.
+Test atomicity of couch operations.
"""
import os
-import mock
import tempfile
import threading
-
from urlparse import urljoin
-
+from twisted.internet import defer
from leap.soledad.client import Soledad
from leap.soledad.common.couch import CouchDatabase, CouchServerState
-from leap.soledad.common.tests.test_couch import CouchDBTestCase
-from leap.soledad.common.tests.u1db_tests import TestCaseWithServer
-from leap.soledad.common.tests.test_sync_target import (
+
+from leap.soledad.common.tests.util import (
make_token_soledad_app,
- make_leap_document_for_test,
- token_leap_sync_target,
+ make_soledad_document_for_test,
+ token_soledad_sync_target,
)
+from leap.soledad.common.tests.test_couch import CouchDBTestCase
+from leap.soledad.common.tests.u1db_tests import TestCaseWithServer
from leap.soledad.common.tests.test_server import _couch_ensure_database
@@ -52,15 +51,15 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
def make_app_after_state(state):
return make_token_soledad_app(state)
- make_document_for_test = make_leap_document_for_test
+ make_document_for_test = make_soledad_document_for_test
- sync_target = token_leap_sync_target
+ sync_target = token_soledad_sync_target
def _soledad_instance(self, user='user-uuid', passphrase=u'123',
prefix='',
- secrets_path=Soledad.STORAGE_SECRETS_FILE_NAME,
+ secrets_path='secrets.json',
local_db_path='soledad.u1db', server_url='',
- cert_file=None, auth_token=None, secret_id=None):
+ cert_file=None, auth_token=None):
"""
Instantiate Soledad.
"""
@@ -70,19 +69,6 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
def _put_doc_side_effect(doc):
self._doc_put = doc
- # we need a mocked shared db or else Soledad will try to access the
- # network to find if there are uploaded secrets.
- class MockSharedDB(object):
-
- get_doc = mock.Mock(return_value=None)
- put_doc = mock.Mock(side_effect=_put_doc_side_effect)
- lock = mock.Mock(return_value=('atoken', 300))
- unlock = mock.Mock()
-
- def __call__(self):
- return self
-
- Soledad._shared_db = MockSharedDB()
return Soledad(
user,
passphrase,
@@ -92,7 +78,7 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
server_url=server_url,
cert_file=cert_file,
auth_token=auth_token,
- secret_id=secret_id)
+ shared_db=self.get_default_shared_mock(_put_doc_side_effect))
def make_app(self):
self.request_state = CouchServerState(self._couch_url, 'shared',
@@ -126,7 +112,6 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
puts.
"""
doc = self.db.create_doc({'ops': 0})
- ops = 1
docs = [doc.doc_id]
for i in range(0, REPEAT_TIMES):
self.assertEqual(
@@ -183,24 +168,27 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
auth_token='auth-token',
server_url=self.getURL())
- def _create_docs_and_sync(sol, syncs):
- # create a lot of documents
- for i in range(0, REPEAT_TIMES):
- sol.create_doc({})
+ def _create_docs(results):
+ deferreds = []
+ for i in xrange(0, REPEAT_TIMES):
+ deferreds.append(sol.create_doc({}))
+ return defer.DeferredList(deferreds)
+
+ def _assert_transaction_and_sync_logs(results, sync_idx):
# assert sizes of transaction and sync logs
self.assertEqual(
- syncs*REPEAT_TIMES,
+ sync_idx*REPEAT_TIMES,
len(self.db._get_transaction_log()))
self.assertEqual(
- 1 if syncs > 0 else 0,
+ 1 if sync_idx > 0 else 0,
len(self.db._database.view('syncs/log').rows))
- # sync to the remote db
- sol.sync()
- gen, docs = self.db.get_all_docs()
- self.assertEqual((syncs+1)*REPEAT_TIMES, gen)
- self.assertEqual((syncs+1)*REPEAT_TIMES, len(docs))
+
+ def _assert_sync(results, sync_idx):
+ gen, docs = results
+ self.assertEqual((sync_idx+1)*REPEAT_TIMES, gen)
+ self.assertEqual((sync_idx+1)*REPEAT_TIMES, len(docs))
# assert sizes of transaction and sync logs
- self.assertEqual((syncs+1)*REPEAT_TIMES,
+ self.assertEqual((sync_idx+1)*REPEAT_TIMES,
len(self.db._get_transaction_log()))
sync_log_rows = self.db._database.view('syncs/log').rows
sync_log = sync_log_rows[0].value
@@ -210,14 +198,32 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
# assert sync_log has exactly 1 row
self.assertEqual(1, len(sync_log_rows))
# assert it has the correct replica_uid, gen and trans_id
- self.assertEqual(sol._db._replica_uid, replica_uid)
- sol_gen, sol_trans_id = sol._db._get_generation_info()
+ self.assertEqual(sol._dbpool.replica_uid, replica_uid)
+ conn_key = sol._dbpool._u1dbconnections.keys().pop()
+ conn = sol._dbpool._u1dbconnections[conn_key]
+ sol_gen, sol_trans_id = conn._get_generation_info()
self.assertEqual(sol_gen, known_gen)
self.assertEqual(sol_trans_id, known_trans_id)
+
+ # create some documents
+ d = _create_docs(None)
- _create_docs_and_sync(sol, 0)
- _create_docs_and_sync(sol, 1)
- sol.close()
+ # sync first time and assert success
+ d.addCallback(_assert_transaction_and_sync_logs, 0)
+ d.addCallback(lambda _: sol.sync())
+ d.addCallback(lambda _: sol.get_all_docs())
+ d.addCallback(_assert_sync, 0)
+
+ # create more docs, sync second time and assert success
+ d.addCallback(_create_docs)
+ d.addCallback(_assert_transaction_and_sync_logs, 1)
+ d.addCallback(lambda _: sol.sync())
+ d.addCallback(lambda _: sol.get_all_docs())
+ d.addCallback(_assert_sync, 1)
+
+ d.addCallback(lambda _: sol.close())
+
+ return d
#
# Concurrency tests
@@ -313,86 +319,76 @@ class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):
"""
Assert that the sync_log is correct after concurrent syncs.
"""
- threads = []
docs = []
- pool = threading.BoundedSemaphore(value=1)
+
self.startServer()
+
sol = self._soledad_instance(
auth_token='auth-token',
server_url=self.getURL())
- def _run_method(self):
- # create a lot of documents
- doc = self._params['sol'].create_doc({})
- pool.acquire()
- docs.append(doc.doc_id)
- pool.release()
+ def _save_doc_ids(results):
+ for doc in results:
+ docs.append(doc.doc_id)
- # launch threads to create documents in parallel
+ # create documents in parallel
+ deferreds = []
for i in range(0, REPEAT_TIMES):
- thread = self._WorkerThread(
- {'sol': sol, 'syncs': i},
- _run_method)
- thread.start()
- threads.append(thread)
+ d = sol.create_doc({})
+ deferreds.append(d)
- # wait for threads to finish
- for thread in threads:
- thread.join()
+ # wait for documents creation and sync
+ d = defer.gatherResults(deferreds)
+ d.addCallback(_save_doc_ids)
+ d.addCallback(lambda _: sol.sync())
- # do the sync!
- sol.sync()
+ def _assert_logs(results):
+ transaction_log = self.db._get_transaction_log()
+ self.assertEqual(REPEAT_TIMES, len(transaction_log))
+ # assert all documents are in the remote log
+ self.assertEqual(REPEAT_TIMES, len(docs))
+ for doc_id in docs:
+ self.assertEqual(
+ 1,
+ len(filter(lambda t: t[0] == doc_id, transaction_log)))
- transaction_log = self.db._get_transaction_log()
- self.assertEqual(REPEAT_TIMES, len(transaction_log))
- # assert all documents are in the remote log
- self.assertEqual(REPEAT_TIMES, len(docs))
- for doc_id in docs:
- self.assertEqual(
- 1,
- len(filter(lambda t: t[0] == doc_id, transaction_log)))
- sol.close()
+ d.addCallback(_assert_logs)
+ d.addCallback(lambda _: sol.close())
+
+ return d
def test_concurrent_syncs_do_not_fail(self):
"""
Assert that concurrent attempts to sync end up being executed
sequentially and do not fail.
"""
- threads = []
docs = []
- pool = threading.BoundedSemaphore(value=1)
+
self.startServer()
+
sol = self._soledad_instance(
auth_token='auth-token',
server_url=self.getURL())
- def _run_method(self):
- # create a lot of documents
- doc = self._params['sol'].create_doc({})
- # do the sync!
- sol.sync()
- pool.acquire()
- docs.append(doc.doc_id)
- pool.release()
-
- # launch threads to create documents in parallel
- for i in range(0, REPEAT_TIMES):
- thread = self._WorkerThread(
- {'sol': sol, 'syncs': i},
- _run_method)
- thread.start()
- threads.append(thread)
-
- # wait for threads to finish
- for thread in threads:
- thread.join()
-
- transaction_log = self.db._get_transaction_log()
- self.assertEqual(REPEAT_TIMES, len(transaction_log))
- # assert all documents are in the remote log
- self.assertEqual(REPEAT_TIMES, len(docs))
- for doc_id in docs:
- self.assertEqual(
- 1,
- len(filter(lambda t: t[0] == doc_id, transaction_log)))
- sol.close()
+ deferreds = []
+ for i in xrange(0, REPEAT_TIMES):
+ d = sol.create_doc({})
+ d.addCallback(lambda doc: docs.append(doc.doc_id))
+ d.addCallback(lambda _: sol.sync())
+ deferreds.append(d)
+
+ def _assert_logs(results):
+ transaction_log = self.db._get_transaction_log()
+ self.assertEqual(REPEAT_TIMES, len(transaction_log))
+ # assert all documents are in the remote log
+ self.assertEqual(REPEAT_TIMES, len(docs))
+ for doc_id in docs:
+ self.assertEqual(
+ 1,
+ len(filter(lambda t: t[0] == doc_id, transaction_log)))
+
+ d = defer.gatherResults(deferreds)
+ d.addCallback(_assert_logs)
+ d.addCallback(lambda _: sol.close())
+
+ return d
diff --git a/common/src/leap/soledad/common/tests/test_crypto.py b/common/src/leap/soledad/common/tests/test_crypto.py
index f5fb4b7a..fdad8aac 100644
--- a/common/src/leap/soledad/common/tests/test_crypto.py
+++ b/common/src/leap/soledad/common/tests/test_crypto.py
@@ -23,7 +23,7 @@ import binascii
from leap.soledad.client import crypto
from leap.soledad.common.document import SoledadDocument
-from leap.soledad.common.tests import BaseSoledadTest
+from leap.soledad.common.tests.util import BaseSoledadTest
from leap.soledad.common.crypto import WrongMacError
from leap.soledad.common.crypto import UnknownMacMethodError
from leap.soledad.common.crypto import EncryptionMethods
@@ -82,7 +82,7 @@ class RecoveryDocumentTestCase(BaseSoledadTest):
rd = self._soledad.secrets._export_recovery_document()
s = self._soledad_instance()
s.secrets._import_recovery_document(rd)
- s.set_secret_id(self._soledad.secrets._secret_id)
+ s.secrets.set_secret_id(self._soledad.secrets._secret_id)
self.assertEqual(self._soledad.storage_secret,
s.storage_secret,
'Failed settinng secret for symmetric encryption.')
@@ -95,7 +95,7 @@ class SoledadSecretsTestCase(BaseSoledadTest):
# instantiate and save secret_id
sol = self._soledad_instance(user='user@leap.se')
self.assertTrue(len(sol.secrets._secrets) == 1)
- secret_id_1 = sol.secret_id
+ secret_id_1 = sol.secrets.secret_id
# assert id is hash of secret
self.assertTrue(
secret_id_1 == hashlib.sha256(sol.storage_secret).hexdigest())
@@ -104,9 +104,8 @@ class SoledadSecretsTestCase(BaseSoledadTest):
self.assertTrue(secret_id_1 != secret_id_2)
sol.close()
# re-instantiate
- sol = self._soledad_instance(
- user='user@leap.se',
- secret_id=secret_id_1)
+ sol = self._soledad_instance(user='user@leap.se')
+ sol.secrets.set_secret_id(secret_id_1)
# assert ids are valid
self.assertTrue(len(sol.secrets._secrets) == 2)
self.assertTrue(secret_id_1 in sol.secrets._secrets)
@@ -117,7 +116,7 @@ class SoledadSecretsTestCase(BaseSoledadTest):
secret_length = sol.secrets.GEN_SECRET_LENGTH
self.assertTrue(len(sol.storage_secret) == secret_length)
# assert format of secret 2
- sol.set_secret_id(secret_id_2)
+ sol.secrets.set_secret_id(secret_id_2)
self.assertTrue(sol.storage_secret is not None)
self.assertIsInstance(sol.storage_secret, str)
self.assertTrue(len(sol.storage_secret) == secret_length)
@@ -134,12 +133,12 @@ class SoledadSecretsTestCase(BaseSoledadTest):
"Should have a secret at this point")
# setting secret id to None should not interfere in the fact we have a
# secret.
- sol.set_secret_id(None)
+ sol.secrets.set_secret_id(None)
self.assertTrue(
sol.secrets._has_secret(),
"Should have a secret at this point")
# but not being able to decrypt correctly should
- sol.secrets._secrets[sol.secret_id] = None
+ sol.secrets._secrets[sol.secrets.secret_id] = None
self.assertFalse(sol.secrets._has_secret())
sol.close()
diff --git a/common/src/leap/soledad/common/tests/test_http.py b/common/src/leap/soledad/common/tests/test_http.py
index d21470e0..1f661b77 100644
--- a/common/src/leap/soledad/common/tests/test_http.py
+++ b/common/src/leap/soledad/common/tests/test_http.py
@@ -20,8 +20,6 @@ Test Leap backend bits: test http database
from u1db.remote import http_database
from leap.soledad.client import auth
-
-from leap.soledad.common.tests import u1db_tests as tests
from leap.soledad.common.tests.u1db_tests import test_http_database
@@ -59,6 +57,3 @@ class TestHTTPDatabaseWithCreds(
'token': 'auth-token',
}})
self.assertIn('token', db1._creds)
-
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_http_client.py b/common/src/leap/soledad/common/tests/test_http_client.py
index 3169398b..db731c32 100644
--- a/common/src/leap/soledad/common/tests/test_http_client.py
+++ b/common/src/leap/soledad/common/tests/test_http_client.py
@@ -21,8 +21,9 @@ import json
from u1db.remote import http_client
+from testscenarios import TestWithScenarios
+
from leap.soledad.client import auth
-from leap.soledad.common.tests import u1db_tests as tests
from leap.soledad.common.tests.u1db_tests import test_http_client
from leap.soledad.server.auth import SoledadTokenAuthMiddleware
@@ -31,7 +32,9 @@ from leap.soledad.server.auth import SoledadTokenAuthMiddleware
# The following tests come from `u1db.tests.test_http_client`.
#-----------------------------------------------------------------------------
-class TestSoledadClientBase(test_http_client.TestHTTPClientBase):
+class TestSoledadClientBase(
+ TestWithScenarios,
+ test_http_client.TestHTTPClientBase):
"""
This class should be used to test Token auth.
"""
@@ -90,7 +93,7 @@ class TestSoledadClientBase(test_http_client.TestHTTPClientBase):
"message": e.message})]
uuid, token = encoded.decode('base64').split(':', 1)
if uuid != 'user-uuid' and token != 'auth-token':
- return unauth_err("Incorrect address or token.")
+ return Exception("Incorrect address or token.")
start_response("200 OK", [('Content-Type', 'application/json')])
return [json.dumps([environ['PATH_INFO'], uuid, token])]
@@ -112,5 +115,3 @@ class TestSoledadClientBase(test_http_client.TestHTTPClientBase):
res, headers = cli._request('GET', ['doc', 'token'])
self.assertEqual(
['/dbase/doc/token', 'user-uuid', 'auth-token'], json.loads(res))
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_https.py b/common/src/leap/soledad/common/tests/test_https.py
index b6288188..4dd55754 100644
--- a/common/src/leap/soledad/common/tests/test_https.py
+++ b/common/src/leap/soledad/common/tests/test_https.py
@@ -14,30 +14,35 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
"""
Test Leap backend bits: https
"""
-from leap.soledad.common.tests import BaseSoledadTest
-from leap.soledad.common.tests import test_sync_target as test_st
-from leap.soledad.common.tests import u1db_tests as tests
-from leap.soledad.common.tests.u1db_tests import test_backends
-from leap.soledad.common.tests.u1db_tests import test_https
-from leap.soledad import client
-from leap.soledad.server import SoledadApp
from u1db.remote import http_client
+from leap.soledad import client
+
+from testscenarios import TestWithScenarios
+
+from leap.soledad.common.tests.u1db_tests import test_backends
+from leap.soledad.common.tests.u1db_tests import test_https
+from leap.soledad.common.tests.util import (
+ BaseSoledadTest,
+ make_soledad_document_for_test,
+ make_soledad_app,
+ make_token_soledad_app,
+)
-def make_soledad_app(state):
- return SoledadApp(state)
LEAP_SCENARIOS = [
('http', {
'make_database_for_test': test_backends.make_http_database_for_test,
'copy_database_for_test': test_backends.copy_http_database_for_test,
- 'make_document_for_test': test_st.make_leap_document_for_test,
- 'make_app_with_state': test_st.make_soledad_app}),
+ 'make_document_for_test': make_soledad_document_for_test,
+ 'make_app_with_state': make_soledad_app}),
]
@@ -55,14 +60,15 @@ def token_leap_https_sync_target(test, host, path):
class TestSoledadSyncTargetHttpsSupport(
+ TestWithScenarios,
test_https.TestHttpSyncTargetHttpsSupport,
BaseSoledadTest):
scenarios = [
('token_soledad_https',
{'server_def': test_https.https_server_def,
- 'make_app_with_state': test_st.make_token_soledad_app,
- 'make_document_for_test': test_st.make_leap_document_for_test,
+ 'make_app_with_state': make_token_soledad_app,
+ 'make_document_for_test': make_soledad_document_for_test,
'sync_target': token_leap_https_sync_target}),
]
@@ -71,8 +77,8 @@ class TestSoledadSyncTargetHttpsSupport(
# run smoothly with standard u1db.
test_https.TestHttpSyncTargetHttpsSupport.setUp(self)
# so here monkey patch again to test our functionality.
- http_client._VerifiedHTTPSConnection = client.VerifiedHTTPSConnection
- client.SOLEDAD_CERT = http_client.CA_CERTS
+ http_client._VerifiedHTTPSConnection = client.api.VerifiedHTTPSConnection
+ client.api.SOLEDAD_CERT = http_client.CA_CERTS
def test_working(self):
"""
@@ -83,7 +89,7 @@ class TestSoledadSyncTargetHttpsSupport(
"""
self.startServer()
db = self.request_state._create_database('test')
- self.patch(client, 'SOLEDAD_CERT', self.cacert_pem)
+ self.patch(client.api, 'SOLEDAD_CERT', self.cacert_pem)
remote_target = self.getSyncTarget('localhost', 'test')
remote_target.record_sync_info('other-id', 2, 'T-id')
self.assertEqual(
@@ -99,10 +105,8 @@ class TestSoledadSyncTargetHttpsSupport(
"""
self.startServer()
self.request_state._create_database('test')
- self.patch(client, 'SOLEDAD_CERT', self.cacert_pem)
+ self.patch(client.api, 'SOLEDAD_CERT', self.cacert_pem)
remote_target = self.getSyncTarget('127.0.0.1', 'test')
self.assertRaises(
http_client.CertificateError, remote_target.record_sync_info,
'other-id', 2, 'T-id')
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py
index acd0a54c..836bd74a 100644
--- a/common/src/leap/soledad/common/tests/test_server.py
+++ b/common/src/leap/soledad/common/tests/test_server.py
@@ -19,29 +19,28 @@ Tests for server-related functionality.
"""
import os
import tempfile
-import simplejson as json
import mock
import time
import binascii
from urlparse import urljoin
+from twisted.internet import defer
-from leap.common.testing.basetest import BaseLeapTest
from leap.soledad.common.couch import (
CouchServerState,
CouchDatabase,
)
-from leap.soledad.common.tests.u1db_tests import (
- TestCaseWithServer,
- simple_doc,
-)
+from leap.soledad.common.tests.u1db_tests import TestCaseWithServer
from leap.soledad.common.tests.test_couch import CouchDBTestCase
-from leap.soledad.common.tests.test_target_soledad import (
+from leap.soledad.common.tests.util import (
make_token_soledad_app,
- make_leap_document_for_test,
+ make_soledad_document_for_test,
+ token_soledad_sync_target,
+ BaseSoledadTest,
)
-from leap.soledad.common.tests.test_sync_target import token_leap_sync_target
-from leap.soledad.client import Soledad, crypto
+
+from leap.soledad.common import crypto
+from leap.soledad.client import Soledad
from leap.soledad.server import LockResource
from leap.soledad.server.auth import URLToAuthorization
@@ -58,7 +57,7 @@ def _couch_ensure_database(self, dbname):
CouchServerState.ensure_database = _couch_ensure_database
-class ServerAuthorizationTestCase(BaseLeapTest):
+class ServerAuthorizationTestCase(BaseSoledadTest):
"""
Tests related to Soledad server authorization.
"""
@@ -272,19 +271,24 @@ class EncryptedSyncTestCase(
Tests for encrypted sync using Soledad server backed by a couch database.
"""
+ # increase twisted.trial's timeout because large files syncing might take
+ # some time to finish.
+ timeout = 500
+
@staticmethod
def make_app_with_state(state):
return make_token_soledad_app(state)
- make_document_for_test = make_leap_document_for_test
+ make_document_for_test = make_soledad_document_for_test
- sync_target = token_leap_sync_target
+ sync_target = token_soledad_sync_target
def _soledad_instance(self, user='user-uuid', passphrase=u'123',
prefix='',
- secrets_path=Soledad.STORAGE_SECRETS_FILE_NAME,
- local_db_path='soledad.u1db', server_url='',
- cert_file=None, auth_token=None, secret_id=None):
+ secrets_path='secrets.json',
+ local_db_path='soledad.u1db',
+ server_url='',
+ cert_file=None, auth_token=None):
"""
Instantiate Soledad.
"""
@@ -294,20 +298,15 @@ class EncryptedSyncTestCase(
def _put_doc_side_effect(doc):
self._doc_put = doc
- # we need a mocked shared db or else Soledad will try to access the
- # network to find if there are uploaded secrets.
- class MockSharedDB(object):
-
- get_doc = mock.Mock(return_value=None)
- put_doc = mock.Mock(side_effect=_put_doc_side_effect)
- lock = mock.Mock(return_value=('atoken', 300))
- unlock = mock.Mock()
- close = mock.Mock()
-
- def __call__(self):
- return self
+ if not server_url:
+ # attempt to find the soledad server url
+ server_address = None
+ server = getattr(self, 'server', None)
+ if server:
+ server_address = getattr(self.server, 'server_address', None)
+ if server_address:
+ server_url = 'http://%s:%d' % (server_address)
- Soledad._shared_db = MockSharedDB()
return Soledad(
user,
passphrase,
@@ -317,7 +316,7 @@ class EncryptedSyncTestCase(
server_url=server_url,
cert_file=cert_file,
auth_token=auth_token,
- secret_id=secret_id)
+ shared_db=self.get_default_shared_mock(_put_doc_side_effect))
def make_app(self):
self.request_state = CouchServerState(self._couch_url, 'shared',
@@ -325,70 +324,122 @@ class EncryptedSyncTestCase(
return self.make_app_with_state(self.request_state)
def setUp(self):
- TestCaseWithServer.setUp(self)
+ # the order of the following initializations is crucial because of
+ # dependencies.
+ # XXX explain better
CouchDBTestCase.setUp(self)
- self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
self._couch_url = 'http://localhost:' + str(self.wrapper.port)
+ self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
+ TestCaseWithServer.setUp(self)
def tearDown(self):
CouchDBTestCase.tearDown(self)
TestCaseWithServer.tearDown(self)
- def test_encrypted_sym_sync(self):
+ def _test_encrypted_sym_sync(self, passphrase=u'123', doc_size=2,
+ number_of_docs=1):
"""
Test the complete syncing chain between two soledad dbs using a
Soledad server backed by a couch database.
"""
self.startServer()
+
# instantiate soledad and create a document
sol1 = self._soledad_instance(
# token is verified in test_target.make_token_soledad_app
- auth_token='auth-token'
- )
- _, doclist = sol1.get_all_docs()
- self.assertEqual([], doclist)
- doc1 = sol1.create_doc(json.loads(simple_doc))
+ auth_token='auth-token',
+ passphrase=passphrase)
+
+ # instantiate another soledad using the same secret as the previous
+ # one (so we can correctly verify the mac of the synced document)
+ sol2 = self._soledad_instance(
+ prefix='x',
+ auth_token='auth-token',
+ secrets_path=sol1._secrets_path,
+ passphrase=passphrase)
+
# ensure remote db exists before syncing
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()
- # assert doc was sent to couch db
- _, doclist = db.get_all_docs()
- self.assertEqual(1, len(doclist))
- couchdoc = doclist[0]
- # assert document structure in couch server
- self.assertEqual(doc1.doc_id, couchdoc.doc_id)
- self.assertEqual(doc1.rev, couchdoc.rev)
- self.assertEqual(6, len(couchdoc.content))
- self.assertTrue(crypto.ENC_JSON_KEY in couchdoc.content)
- self.assertTrue(crypto.ENC_SCHEME_KEY in couchdoc.content)
- self.assertTrue(crypto.ENC_METHOD_KEY in couchdoc.content)
- self.assertTrue(crypto.ENC_IV_KEY in couchdoc.content)
- self.assertTrue(crypto.MAC_KEY in couchdoc.content)
- self.assertTrue(crypto.MAC_METHOD_KEY in couchdoc.content)
- # instantiate soledad with empty db, but with same secrets path
- sol2 = self._soledad_instance(prefix='x', auth_token='auth-token')
- _, doclist = sol2.get_all_docs()
- self.assertEqual([], doclist)
- sol2.secrets_path = sol1.secrets_path
- sol2.secrets._load_secrets()
- sol2.set_secret_id(sol1.secret_id)
- # sync the new instance
- sol2._server_url = self.getURL()
- sol2.sync()
- _, doclist = sol2.get_all_docs()
- self.assertEqual(1, len(doclist))
- doc2 = doclist[0]
- # assert incoming doc is equal to the first sent doc
- self.assertEqual(doc1, doc2)
- db.delete_database()
- db.close()
- sol1.close()
- sol2.close()
+
+ def _db1AssertEmptyDocList(results):
+ _, doclist = results
+ self.assertEqual([], doclist)
+
+ def _db1CreateDocs(results):
+ deferreds = []
+ for i in xrange(number_of_docs):
+ content = binascii.hexlify(os.urandom(doc_size/2))
+ deferreds.append(sol1.create_doc({'data': content}))
+ return defer.DeferredList(deferreds)
+
+ def _db1AssertDocsSyncedToServer(results):
+ _, sol_doclist = results
+ self.assertEqual(number_of_docs, len(sol_doclist))
+ # assert doc was sent to couch db
+ _, couch_doclist = db.get_all_docs()
+ self.assertEqual(number_of_docs, len(couch_doclist))
+ for i in xrange(number_of_docs):
+ soldoc = sol_doclist.pop()
+ couchdoc = couch_doclist.pop()
+ # assert document structure in couch server
+ self.assertEqual(soldoc.doc_id, couchdoc.doc_id)
+ self.assertEqual(soldoc.rev, couchdoc.rev)
+ self.assertEqual(6, len(couchdoc.content))
+ self.assertTrue(crypto.ENC_JSON_KEY in couchdoc.content)
+ self.assertTrue(crypto.ENC_SCHEME_KEY in couchdoc.content)
+ self.assertTrue(crypto.ENC_METHOD_KEY in couchdoc.content)
+ self.assertTrue(crypto.ENC_IV_KEY in couchdoc.content)
+ self.assertTrue(crypto.MAC_KEY in couchdoc.content)
+ self.assertTrue(crypto.MAC_METHOD_KEY in couchdoc.content)
+
+ d = sol1.get_all_docs()
+ d.addCallback(_db1AssertEmptyDocList)
+ d.addCallback(_db1CreateDocs)
+ d.addCallback(lambda _: sol1.sync())
+ d.addCallback(lambda _: sol1.get_all_docs())
+ d.addCallback(_db1AssertDocsSyncedToServer)
+
+ def _db2AssertEmptyDocList(results):
+ _, doclist = results
+ self.assertEqual([], doclist)
+
+ def _getAllDocsFromBothDbs(results):
+ d1 = sol1.get_all_docs()
+ d2 = sol2.get_all_docs()
+ return defer.DeferredList([d1, d2])
+
+ d.addCallback(lambda _: sol2.get_all_docs())
+ d.addCallback(_db2AssertEmptyDocList)
+ d.addCallback(lambda _: sol2.sync())
+ d.addCallback(_getAllDocsFromBothDbs)
+
+ def _assertDocSyncedFromDb1ToDb2(results):
+ r1, r2 = results
+ _, (gen1, doclist1) = r1
+ _, (gen2, doclist2) = r2
+ self.assertEqual(number_of_docs, gen1)
+ self.assertEqual(number_of_docs, gen2)
+ self.assertEqual(number_of_docs, len(doclist1))
+ self.assertEqual(number_of_docs, len(doclist2))
+ self.assertEqual(doclist1[0], doclist2[0])
+
+ d.addCallback(_assertDocSyncedFromDb1ToDb2)
+
+ def _cleanUp(results):
+ db.delete_database()
+ db.close()
+ sol1.close()
+ sol2.close()
+
+ d.addCallback(_cleanUp)
+
+ return d
+
+ def test_encrypted_sym_sync(self):
+ return self._test_encrypted_sym_sync()
def test_encrypted_sym_sync_with_unicode_passphrase(self):
"""
@@ -396,152 +447,20 @@ class EncryptedSyncTestCase(
Soledad server backed by a couch database, using an unicode
passphrase.
"""
- self.startServer()
- # instantiate soledad and create a document
- sol1 = self._soledad_instance(
- # token is verified in test_target.make_token_soledad_app
- auth_token='auth-token',
- passphrase=u'ãáàäéàëíìïóòöõúùüñç',
- )
- _, doclist = sol1.get_all_docs()
- self.assertEqual([], doclist)
- doc1 = sol1.create_doc(json.loads(simple_doc))
- # ensure remote db exists before syncing
- 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()
- # assert doc was sent to couch db
- _, doclist = db.get_all_docs()
- self.assertEqual(1, len(doclist))
- couchdoc = doclist[0]
- # assert document structure in couch server
- self.assertEqual(doc1.doc_id, couchdoc.doc_id)
- self.assertEqual(doc1.rev, couchdoc.rev)
- self.assertEqual(6, len(couchdoc.content))
- self.assertTrue(crypto.ENC_JSON_KEY in couchdoc.content)
- self.assertTrue(crypto.ENC_SCHEME_KEY in couchdoc.content)
- self.assertTrue(crypto.ENC_METHOD_KEY in couchdoc.content)
- self.assertTrue(crypto.ENC_IV_KEY in couchdoc.content)
- self.assertTrue(crypto.MAC_KEY in couchdoc.content)
- self.assertTrue(crypto.MAC_METHOD_KEY in couchdoc.content)
- # instantiate soledad with empty db, but with same secrets path
- sol2 = self._soledad_instance(
- prefix='x',
- auth_token='auth-token',
- passphrase=u'ãáàäéàëíìïóòöõúùüñç',
- )
- _, doclist = sol2.get_all_docs()
- self.assertEqual([], doclist)
- sol2.secrets_path = sol1.secrets_path
- sol2.secrets._load_secrets()
- sol2.set_secret_id(sol1.secret_id)
- # sync the new instance
- sol2._server_url = self.getURL()
- sol2.sync()
- _, doclist = sol2.get_all_docs()
- self.assertEqual(1, len(doclist))
- doc2 = doclist[0]
- # assert incoming doc is equal to the first sent doc
- self.assertEqual(doc1, doc2)
- db.delete_database()
- db.close()
- sol1.close()
- sol2.close()
+ return self._test_encrypted_sym_sync(passphrase=u'ãáàäéàëíìïóòöõúùüñç')
def test_sync_very_large_files(self):
"""
Test if Soledad can sync very large files.
"""
- # define the size of the "very large file"
length = 100*(10**6) # 100 MB
- self.startServer()
- # instantiate soledad and create a document
- sol1 = self._soledad_instance(
- # token is verified in test_target.make_token_soledad_app
- auth_token='auth-token'
- )
- _, doclist = sol1.get_all_docs()
- self.assertEqual([], doclist)
- content = binascii.hexlify(os.urandom(length/2)) # len() == length
- doc1 = sol1.create_doc({'data': content})
- # ensure remote db exists before syncing
- 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()
- # instantiate soledad with empty db, but with same secrets path
- sol2 = self._soledad_instance(prefix='x', auth_token='auth-token')
- _, doclist = sol2.get_all_docs()
- self.assertEqual([], doclist)
- sol2.secrets_path = sol1.secrets_path
- sol2.secrets._load_secrets()
- sol2.set_secret_id(sol1.secret_id)
- # sync the new instance
- sol2._server_url = self.getURL()
- sol2.sync()
- _, doclist = sol2.get_all_docs()
- self.assertEqual(1, len(doclist))
- doc2 = doclist[0]
- # assert incoming doc is equal to the first sent doc
- self.assertEqual(doc1, doc2)
- # delete remote database
- db.delete_database()
- db.close()
- sol1.close()
- sol2.close()
+ return self._test_encrypted_sym_sync(doc_size=length, number_of_docs=1)
def test_sync_many_small_files(self):
"""
Test if Soledad can sync many smallfiles.
"""
- number_of_docs = 100
- self.startServer()
- # instantiate soledad and create a document
- sol1 = self._soledad_instance(
- # token is verified in test_target.make_token_soledad_app
- auth_token='auth-token'
- )
- _, doclist = sol1.get_all_docs()
- self.assertEqual([], doclist)
- # create many small files
- for i in range(0, number_of_docs):
- sol1.create_doc(json.loads(simple_doc))
- # ensure remote db exists before syncing
- 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()
- # instantiate soledad with empty db, but with same secrets path
- sol2 = self._soledad_instance(prefix='x', auth_token='auth-token')
- _, doclist = sol2.get_all_docs()
- self.assertEqual([], doclist)
- sol2.secrets_path = sol1.secrets_path
- sol2.secrets._load_secrets()
- sol2.set_secret_id(sol1.secret_id)
- # sync the new instance
- sol2._server_url = self.getURL()
- sol2.sync()
- _, doclist = sol2.get_all_docs()
- self.assertEqual(number_of_docs, len(doclist))
- # assert incoming docs are equal to sent docs
- for doc in doclist:
- self.assertEqual(sol1.get_doc(doc.doc_id), doc)
- # delete remote database
- db.delete_database()
- db.close()
- sol1.close()
- sol2.close()
-
+ return self._test_encrypted_sym_sync(doc_size=2, number_of_docs=100)
class LockResourceTestCase(
CouchDBTestCase, TestCaseWithServer):
@@ -553,15 +472,18 @@ class LockResourceTestCase(
def make_app_with_state(state):
return make_token_soledad_app(state)
- make_document_for_test = make_leap_document_for_test
+ make_document_for_test = make_soledad_document_for_test
- sync_target = token_leap_sync_target
+ sync_target = token_soledad_sync_target
def setUp(self):
- TestCaseWithServer.setUp(self)
+ # the order of the following initializations is crucial because of
+ # dependencies.
+ # XXX explain better
CouchDBTestCase.setUp(self)
- self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
self._couch_url = 'http://localhost:' + str(self.wrapper.port)
+ self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
+ TestCaseWithServer.setUp(self)
# create the databases
CouchDatabase.open_database(
urljoin(self._couch_url, 'shared'),
@@ -575,14 +497,14 @@ class LockResourceTestCase(
self._couch_url, 'shared', 'tokens')
def tearDown(self):
- CouchDBTestCase.tearDown(self)
- TestCaseWithServer.tearDown(self)
# delete remote database
db = CouchDatabase.open_database(
urljoin(self._couch_url, 'shared'),
create=True,
ensure_ddocs=True)
db.delete_database()
+ CouchDBTestCase.tearDown(self)
+ TestCaseWithServer.tearDown(self)
def test__try_obtain_filesystem_lock(self):
responder = mock.Mock()
diff --git a/common/src/leap/soledad/common/tests/test_soledad.py b/common/src/leap/soledad/common/tests/test_soledad.py
index 31c02fc4..0b49d9f5 100644
--- a/common/src/leap/soledad/common/tests/test_soledad.py
+++ b/common/src/leap/soledad/common/tests/test_soledad.py
@@ -20,9 +20,8 @@ Tests for general Soledad functionality.
import os
from mock import Mock
-
from leap.common.events import events_pb2 as proto
-from leap.soledad.common.tests import (
+from leap.soledad.common.tests.util import (
BaseSoledadTest,
ADDRESS,
)
@@ -30,10 +29,9 @@ from leap import soledad
from leap.soledad.common.document import SoledadDocument
from leap.soledad.common.crypto import WrongMacError
from leap.soledad.client import Soledad
-from leap.soledad.client.sqlcipher import SQLCipherDatabase
+from leap.soledad.client.adbapi import U1DBConnectionPool
from leap.soledad.client.secrets import PassphraseTooShort
from leap.soledad.client.shared_db import SoledadSharedDatabase
-from leap.soledad.client.target import SoledadSyncTarget
class AuxMethodsTestCase(BaseSoledadTest):
@@ -41,18 +39,24 @@ class AuxMethodsTestCase(BaseSoledadTest):
def test__init_dirs(self):
sol = self._soledad_instance(prefix='_init_dirs')
local_db_dir = os.path.dirname(sol.local_db_path)
- secrets_path = os.path.dirname(sol.secrets_path)
+ secrets_path = os.path.dirname(sol.secrets.secrets_path)
self.assertTrue(os.path.isdir(local_db_dir))
self.assertTrue(os.path.isdir(secrets_path))
- sol.close()
- def test__init_db(self):
+ def _close_soledad(results):
+ sol.close()
+
+ d = sol.create_doc({})
+ d.addCallback(_close_soledad)
+ return d
+
+ def test__init_u1db_sqlcipher_backend(self):
sol = self._soledad_instance(prefix='_init_db')
- self.assertIsInstance(sol._db, SQLCipherDatabase)
+ self.assertIsInstance(sol._dbpool, U1DBConnectionPool)
self.assertTrue(os.path.isfile(sol.local_db_path))
sol.close()
- def test__init_config_defaults(self):
+ def test__init_config_with_defaults(self):
"""
Test if configuration defaults point to the correct place.
"""
@@ -62,23 +66,16 @@ class AuxMethodsTestCase(BaseSoledadTest):
def __init__(self):
pass
- # instantiate without initializing so we just test _init_config()
+ # instantiate without initializing so we just test
+ # _init_config_with_defaults()
sol = SoledadMock()
sol._passphrase = u''
- sol._secrets_path = None
- sol._local_db_path = None
sol._server_url = ''
- sol._init_config()
- # assert value of secrets_path
- self.assertEquals(
- os.path.join(
- sol.DEFAULT_PREFIX, Soledad.STORAGE_SECRETS_FILE_NAME),
- sol._secrets_path)
+ sol._init_config_with_defaults()
# assert value of local_db_path
self.assertEquals(
- os.path.join(sol.DEFAULT_PREFIX, 'soledad.u1db'),
+ os.path.join(sol.default_prefix, 'soledad.u1db'),
sol.local_db_path)
- sol.close()
def test__init_config_from_params(self):
"""
@@ -93,43 +90,56 @@ class AuxMethodsTestCase(BaseSoledadTest):
cert_file=None)
self.assertEqual(
os.path.join(self.tempdir, 'value_3'),
- sol.secrets_path)
+ sol.secrets.secrets_path)
self.assertEqual(
os.path.join(self.tempdir, 'value_2'),
sol.local_db_path)
- self.assertEqual('value_1', sol.server_url)
+ self.assertEqual('value_1', sol._server_url)
sol.close()
def test_change_passphrase(self):
"""
Test if passphrase can be changed.
"""
+ prefix = '_change_passphrase'
sol = self._soledad_instance(
'leap@leap.se',
passphrase=u'123',
- prefix=self.rand_prefix,
+ prefix=prefix,
)
- doc = sol.create_doc({'simple': 'doc'})
- doc_id = doc.doc_id
-
- # change the passphrase
- sol.change_passphrase(u'654321')
- sol.close()
- self.assertRaises(
- WrongMacError,
- self._soledad_instance, 'leap@leap.se',
- passphrase=u'123',
- prefix=self.rand_prefix)
-
- # use new passphrase and retrieve doc
- sol2 = self._soledad_instance(
- 'leap@leap.se',
- passphrase=u'654321',
- prefix=self.rand_prefix)
- doc2 = sol2.get_doc(doc_id)
- self.assertEqual(doc, doc2)
- sol2.close()
+ def _change_passphrase(doc1):
+ self._doc1 = doc1
+ sol.change_passphrase(u'654321')
+ sol.close()
+
+ def _assert_wrong_password_raises(results):
+ self.assertRaises(
+ WrongMacError,
+ self._soledad_instance, 'leap@leap.se',
+ passphrase=u'123',
+ prefix=prefix)
+
+ def _instantiate_with_new_passphrase(results):
+ sol2 = self._soledad_instance(
+ 'leap@leap.se',
+ passphrase=u'654321',
+ prefix=prefix)
+ self._sol2 = sol2
+ return sol2.get_doc(self._doc1.doc_id)
+
+ def _assert_docs_are_equal(doc2):
+ self.assertEqual(self._doc1, doc2)
+ self._sol2.close()
+
+ d = sol.create_doc({'simple': 'doc'})
+ d.addCallback(_change_passphrase)
+ d.addCallback(_assert_wrong_password_raises)
+ d.addCallback(_instantiate_with_new_passphrase)
+ d.addCallback(_assert_docs_are_equal)
+ d.addCallback(lambda _: sol.close())
+
+ return d
def test_change_passphrase_with_short_passphrase_raises(self):
"""
@@ -150,7 +160,7 @@ class AuxMethodsTestCase(BaseSoledadTest):
Assert passphrase getter works fine.
"""
sol = self._soledad_instance()
- self.assertEqual('123', sol.passphrase)
+ self.assertEqual('123', sol._passphrase)
sol.close()
@@ -175,7 +185,7 @@ class SoledadSharedDBTestCase(BaseSoledadTest):
doc_id = self._soledad.secrets._shared_db_doc_id()
self._soledad.secrets._get_secrets_from_shared_db()
self.assertTrue(
- self._soledad._shared_db().get_doc.assert_called_with(
+ self._soledad.shared_db.get_doc.assert_called_with(
doc_id) is None,
'Wrong doc_id when fetching recovery document.')
@@ -186,11 +196,11 @@ class SoledadSharedDBTestCase(BaseSoledadTest):
doc_id = self._soledad.secrets._shared_db_doc_id()
self._soledad.secrets._put_secrets_in_shared_db()
self.assertTrue(
- self._soledad._shared_db().get_doc.assert_called_with(
+ self._soledad.shared_db.get_doc.assert_called_with(
doc_id) is None,
'Wrong doc_id when fetching recovery document.')
self.assertTrue(
- self._soledad._shared_db.put_doc.assert_called_with(
+ self._soledad.shared_db.put_doc.assert_called_with(
self._doc_put) is None,
'Wrong document when putting recovery document.')
self.assertTrue(
@@ -285,8 +295,8 @@ class SoledadSignalingTestCase(BaseSoledadTest):
ADDRESS,
)
# assert db was locked and unlocked
- sol._shared_db.lock.assert_called_with()
- sol._shared_db.unlock.assert_called_with('atoken')
+ sol.shared_db.lock.assert_called_with()
+ sol.shared_db.unlock.assert_called_with('atoken')
sol.close()
def test_stage2_bootstrap_signals(self):
@@ -299,25 +309,15 @@ class SoledadSignalingTestCase(BaseSoledadTest):
# create a document with secrets
doc = SoledadDocument(doc_id=sol.secrets._shared_db_doc_id())
doc.content = sol.secrets._export_recovery_document()
-
- class Stage2MockSharedDB(object):
-
- get_doc = Mock(return_value=doc)
- put_doc = Mock()
- lock = Mock(return_value=('atoken', 300))
- unlock = Mock()
-
- def __call__(self):
- return self
-
sol.close()
# reset mock
soledad.client.secrets.events.signal.reset_mock()
# get a fresh instance so it emits all bootstrap signals
+ shared_db = self.get_default_shared_mock(get_doc_return_value=doc)
sol = self._soledad_instance(
secrets_path='alternative_stage2.json',
local_db_path='alternative_stage2.u1db',
- shared_db_class=Stage2MockSharedDB)
+ shared_db_class=shared_db)
# reverse call order so we can verify in the order the signals were
# expected
soledad.client.secrets.events.signal.mock_calls.reverse()
@@ -355,33 +355,17 @@ class SoledadSignalingTestCase(BaseSoledadTest):
sol = self._soledad_instance()
# mock the actual db sync so soledad does not try to connect to the
# server
- sol._db.sync = Mock()
- # do the sync
- sol.sync()
- # assert the signal has been emitted
- soledad.client.signal.assert_called_with(
- proto.SOLEDAD_DONE_DATA_SYNC,
- ADDRESS,
- )
- sol.close()
-
- def test_need_sync_signals(self):
- """
- Test Soledad emits SOLEDAD_CREATING_KEYS signal.
- """
- soledad.client.signal.reset_mock()
- sol = self._soledad_instance()
- # mock the sync target
- old_get_sync_info = SoledadSyncTarget.get_sync_info
- SoledadSyncTarget.get_sync_info = Mock(return_value=[0, 0, 0, 0, 2])
- # mock our generation so soledad thinks there's new data to sync
- sol._db._get_generation = Mock(return_value=1)
- # check for new data to sync
- sol.need_sync('http://provider/userdb')
- # assert the signal has been emitted
- soledad.client.signal.assert_called_with(
- proto.SOLEDAD_NEW_DATA_TO_SYNC,
- ADDRESS,
- )
- SoledadSyncTarget.get_sync_info = old_get_sync_info
- sol.close()
+ sol._dbsyncer.sync = Mock()
+
+ def _assert_done_data_sync_signal_emitted(results):
+ # assert the signal has been emitted
+ soledad.client.signal.assert_called_with(
+ proto.SOLEDAD_DONE_DATA_SYNC,
+ ADDRESS,
+ )
+ sol.close()
+
+ # do the sync and assert signal was emitted
+ d = sol.sync()
+ d.addCallback(_assert_done_data_sync_signal_emitted)
+ return d
diff --git a/common/src/leap/soledad/common/tests/test_soledad_app.py b/common/src/leap/soledad/common/tests/test_soledad_app.py
new file mode 100644
index 00000000..6efae1d6
--- /dev/null
+++ b/common/src/leap/soledad/common/tests/test_soledad_app.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+# test_soledad_app.py
+# Copyright (C) 2014 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/>.
+
+
+"""
+Test ObjectStore and Couch backend bits.
+"""
+
+
+from testscenarios import TestWithScenarios
+
+from leap.soledad.common.tests.util import BaseSoledadTest
+from leap.soledad.common.tests.util import make_soledad_document_for_test
+from leap.soledad.common.tests.util import make_soledad_app
+from leap.soledad.common.tests.util import make_token_soledad_app
+from leap.soledad.common.tests.util import make_token_http_database_for_test
+from leap.soledad.common.tests.util import copy_token_http_database_for_test
+from leap.soledad.common.tests.u1db_tests import test_backends
+
+
+#-----------------------------------------------------------------------------
+# The following tests come from `u1db.tests.test_backends`.
+#-----------------------------------------------------------------------------
+
+LEAP_SCENARIOS = [
+ ('http', {
+ 'make_database_for_test': test_backends.make_http_database_for_test,
+ 'copy_database_for_test': test_backends.copy_http_database_for_test,
+ 'make_document_for_test': make_soledad_document_for_test,
+ 'make_app_with_state': make_soledad_app}),
+]
+
+
+class SoledadTests(
+ TestWithScenarios, test_backends.AllDatabaseTests, BaseSoledadTest):
+
+ scenarios = LEAP_SCENARIOS + [
+ ('token_http', {'make_database_for_test':
+ make_token_http_database_for_test,
+ 'copy_database_for_test':
+ copy_token_http_database_for_test,
+ 'make_document_for_test': make_soledad_document_for_test,
+ 'make_app_with_state': make_token_soledad_app,
+ })
+ ]
diff --git a/common/src/leap/soledad/common/tests/test_soledad_doc.py b/common/src/leap/soledad/common/tests/test_soledad_doc.py
index 0952de6d..4a67f80a 100644
--- a/common/src/leap/soledad/common/tests/test_soledad_doc.py
+++ b/common/src/leap/soledad/common/tests/test_soledad_doc.py
@@ -17,28 +17,30 @@
"""
Test Leap backend bits: soledad docs
"""
-from leap.soledad.common.tests import BaseSoledadTest
+from testscenarios import TestWithScenarios
+
from leap.soledad.common.tests.u1db_tests import test_document
-from leap.soledad.common.tests import u1db_tests as tests
-from leap.soledad.common.tests import test_sync_target as st
+from leap.soledad.common.tests.util import BaseSoledadTest
+from leap.soledad.common.tests.util import make_soledad_document_for_test
+
#-----------------------------------------------------------------------------
# The following tests come from `u1db.tests.test_document`.
#-----------------------------------------------------------------------------
-
-class TestSoledadDocument(test_document.TestDocument, BaseSoledadTest):
+class TestSoledadDocument(
+ TestWithScenarios,
+ test_document.TestDocument, BaseSoledadTest):
scenarios = ([(
'leap', {
- 'make_document_for_test': st.make_leap_document_for_test})])
+ 'make_document_for_test': make_soledad_document_for_test})])
-class TestSoledadPyDocument(test_document.TestPyDocument, BaseSoledadTest):
+class TestSoledadPyDocument(
+ TestWithScenarios,
+ test_document.TestPyDocument, BaseSoledadTest):
scenarios = ([(
'leap', {
- 'make_document_for_test': st.make_leap_document_for_test})])
-
-
-load_tests = tests.load_with_scenarios
+ 'make_document_for_test': make_soledad_document_for_test})])
diff --git a/common/src/leap/soledad/common/tests/test_sqlcipher.py b/common/src/leap/soledad/common/tests/test_sqlcipher.py
index 78e2f01b..ceb095b8 100644
--- a/common/src/leap/soledad/common/tests/test_sqlcipher.py
+++ b/common/src/leap/soledad/common/tests/test_sqlcipher.py
@@ -20,10 +20,11 @@ Test sqlcipher backend internals.
import os
import time
import threading
-
+import tempfile
+import shutil
from pysqlcipher import dbapi2
-
+from testscenarios import TestWithScenarios
# u1db stuff.
from u1db import (
@@ -34,17 +35,16 @@ from u1db.backends.sqlite_backend import SQLitePartialExpandDatabase
# soledad stuff.
+from leap.soledad.common import soledad_assert
from leap.soledad.common.document import SoledadDocument
from leap.soledad.client.sqlcipher import (
SQLCipherDatabase,
SQLCipherOptions,
DatabaseIsNotEncrypted,
- initialize_sqlcipher_db,
)
# u1db tests stuff.
-from leap.common.testing.basetest import BaseLeapTest
from leap.soledad.common.tests import u1db_tests as tests
from leap.soledad.common.tests.u1db_tests import test_sqlite_backend
from leap.soledad.common.tests.u1db_tests import test_backends
@@ -53,12 +53,12 @@ from leap.soledad.common.tests.util import (
make_sqlcipher_database_for_test,
copy_sqlcipher_database_for_test,
PASSWORD,
+ BaseSoledadTest,
)
def sqlcipher_open(path, passphrase, create=True, document_factory=None):
return SQLCipherDatabase(
- None,
SQLCipherOptions(path, passphrase, create=create))
@@ -93,30 +93,34 @@ SQLCIPHER_SCENARIOS = [
]
-class SQLCipherTests(test_backends.AllDatabaseTests):
+class SQLCipherTests(TestWithScenarios, test_backends.AllDatabaseTests):
scenarios = SQLCIPHER_SCENARIOS
-class SQLCipherDatabaseTests(test_backends.LocalDatabaseTests):
+class SQLCipherDatabaseTests(TestWithScenarios, test_backends.LocalDatabaseTests):
scenarios = SQLCIPHER_SCENARIOS
class SQLCipherValidateGenNTransIdTests(
+ TestWithScenarios,
test_backends.LocalDatabaseValidateGenNTransIdTests):
scenarios = SQLCIPHER_SCENARIOS
class SQLCipherValidateSourceGenTests(
+ TestWithScenarios,
test_backends.LocalDatabaseValidateSourceGenTests):
scenarios = SQLCIPHER_SCENARIOS
class SQLCipherWithConflictsTests(
+ TestWithScenarios,
test_backends.LocalDatabaseWithConflictsTests):
scenarios = SQLCIPHER_SCENARIOS
-class SQLCipherIndexTests(test_backends.DatabaseIndexTests):
+class SQLCipherIndexTests(
+ TestWithScenarios, test_backends.DatabaseIndexTests):
scenarios = SQLCIPHER_SCENARIOS
@@ -124,7 +128,7 @@ class SQLCipherIndexTests(test_backends.DatabaseIndexTests):
# The following tests come from `u1db.tests.test_sqlite_backend`.
#-----------------------------------------------------------------------------
-class TestSQLCipherDatabase(test_sqlite_backend.TestSQLiteDatabase):
+class TestSQLCipherDatabase(TestWithScenarios, test_sqlite_backend.TestSQLiteDatabase):
def test_atomic_initialize(self):
# This test was modified to ensure that db2.close() is called within
@@ -137,11 +141,11 @@ class TestSQLCipherDatabase(test_sqlite_backend.TestSQLiteDatabase):
class SQLCipherDatabaseTesting(SQLCipherDatabase):
_index_storage_value = "testing"
- def __init__(self, soledad_crypto, dbname, ntry):
+ def __init__(self, dbname, ntry):
self._try = ntry
self._is_initialized_invocations = 0
SQLCipherDatabase.__init__(
- self, soledad_crypto,
+ self,
SQLCipherOptions(dbname, PASSWORD))
def _is_initialized(self, c):
@@ -161,15 +165,14 @@ class TestSQLCipherDatabase(test_sqlite_backend.TestSQLiteDatabase):
def run(self):
try:
- db2 = SQLCipherDatabaseTesting(None, dbname, 2)
+ db2 = SQLCipherDatabaseTesting(dbname, 2)
except Exception, e:
SecondTry.outcome2.append(e)
else:
SecondTry.outcome2.append(db2)
- self.close()
t2 = SecondTry()
- db1 = SQLCipherDatabaseTesting(None, dbname, 1)
+ db1 = SQLCipherDatabaseTesting(dbname, 1)
t2.join()
self.assertIsInstance(SecondTry.outcome2[0], SQLCipherDatabaseTesting)
@@ -368,7 +371,7 @@ class SQLCipherOpen(test_open.TestU1DBOpen):
# Tests for actual encryption of the database
#-----------------------------------------------------------------------------
-class SQLCipherEncryptionTest(BaseLeapTest):
+class SQLCipherEncryptionTest(BaseSoledadTest):
"""
Tests to guarantee SQLCipher is indeed encrypting data when storing.
"""
@@ -379,11 +382,37 @@ class SQLCipherEncryptionTest(BaseLeapTest):
os.unlink(dbfile)
def setUp(self):
+ # the following come from BaseLeapTest.setUpClass, because
+ # twisted.trial doesn't support such class methods for setting up
+ # test classes.
+ self.old_path = os.environ['PATH']
+ self.old_home = os.environ['HOME']
+ self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
+ self.home = self.tempdir
+ bin_tdir = os.path.join(
+ self.tempdir,
+ 'bin')
+ os.environ["PATH"] = bin_tdir
+ os.environ["HOME"] = self.tempdir
+ # this is our own stuff
self.DB_FILE = os.path.join(self.tempdir, 'test.db')
self._delete_dbfiles()
def tearDown(self):
self._delete_dbfiles()
+ # the following come from BaseLeapTest.tearDownClass, because
+ # twisted.trial doesn't support such class methods for tearing down
+ # test classes.
+ os.environ["PATH"] = self.old_path
+ os.environ["HOME"] = self.old_home
+ # safety check! please do not wipe my home...
+ # XXX needs to adapt to non-linuces
+ soledad_assert(
+ self.tempdir.startswith('/tmp/leap_tests-') or
+ self.tempdir.startswith('/var/folder'),
+ "beware! tried to remove a dir which does not "
+ "live in temporal folder!")
+ shutil.rmtree(self.tempdir)
def test_try_to_open_encrypted_db_with_sqlite_backend(self):
"""
@@ -426,6 +455,3 @@ class SQLCipherEncryptionTest(BaseLeapTest):
"dbs.")
except DatabaseIsNotEncrypted:
pass
-
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_sqlcipher_sync.py b/common/src/leap/soledad/common/tests/test_sqlcipher_sync.py
index ad2a06b3..83c3449e 100644
--- a/common/src/leap/soledad/common/tests/test_sqlcipher_sync.py
+++ b/common/src/leap/soledad/common/tests/test_sqlcipher_sync.py
@@ -19,12 +19,14 @@ Test sqlcipher backend sync.
"""
+import os
import simplejson as json
from u1db import (
sync,
vectorclock,
)
+from testscenarios import TestWithScenarios
from leap.soledad.common.crypto import ENC_SCHEME_KEY
from leap.soledad.client.target import SoledadSyncTarget
@@ -33,15 +35,12 @@ from leap.soledad.client.sqlcipher import (
SQLCipherDatabase,
)
-
-from leap.soledad.common.tests import u1db_tests as tests, BaseSoledadTest
+from leap.soledad.common.tests import u1db_tests as tests
from leap.soledad.common.tests.u1db_tests import test_sync
-from leap.soledad.common.tests.test_sqlcipher import (
- SQLCIPHER_SCENARIOS,
- make_document_for_test,
-)
+from leap.soledad.common.tests.test_sqlcipher import SQLCIPHER_SCENARIOS
from leap.soledad.common.tests.util import (
make_soledad_app,
+ BaseSoledadTest,
SoledadWithCouchServerMixin,
)
@@ -50,15 +49,7 @@ from leap.soledad.common.tests.util import (
# The following tests come from `u1db.tests.test_sync`.
#-----------------------------------------------------------------------------
-sync_scenarios = []
-for name, scenario in SQLCIPHER_SCENARIOS:
- scenario = dict(scenario)
- scenario['do_sync'] = test_sync.sync_via_synchronizer
- sync_scenarios.append((name, scenario))
- scenario = dict(scenario)
-
-
-def sync_via_synchronizer_and_leap(test, db_source, db_target,
+def sync_via_synchronizer_and_soledad(test, db_source, db_target,
trace_hook=None, trace_hook_shallow=None):
if trace_hook:
test.skipTest("full trace hook unsupported over http")
@@ -71,17 +62,16 @@ def sync_via_synchronizer_and_leap(test, db_source, db_target,
return sync.Synchronizer(db_source, target).sync()
-sync_scenarios.append(('pyleap', {
- 'make_database_for_test': test_sync.make_database_for_http_test,
- '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': test_sync.sync_via_synchronizer,
-}))
+sync_scenarios = []
+for name, scenario in SQLCIPHER_SCENARIOS:
+ scenario['do_sync'] = test_sync.sync_via_synchronizer
+ sync_scenarios.append((name, scenario))
class SQLCipherDatabaseSyncTests(
- test_sync.DatabaseSyncTests, BaseSoledadTest):
+ TestWithScenarios,
+ test_sync.DatabaseSyncTests,
+ BaseSoledadTest):
"""
Test for succesfull sync between SQLCipher and LeapBackend.
@@ -92,8 +82,8 @@ class SQLCipherDatabaseSyncTests(
scenarios = sync_scenarios
- def setUp(self):
- test_sync.DatabaseSyncTests.setUp(self)
+ #def setUp(self):
+ # test_sync.DatabaseSyncTests.setUp(self)
def tearDown(self):
test_sync.DatabaseSyncTests.tearDown(self)
@@ -112,8 +102,6 @@ class SQLCipherDatabaseSyncTests(
and isinstance(self.db3, SQLCipherDatabase):
self.db3.close()
-
-
def test_sync_autoresolves(self):
"""
Test for sync autoresolve remote.
@@ -321,12 +309,14 @@ target_scenarios = [
'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}),
+ 'do_sync': sync_via_synchronizer_and_soledad}),
]
class SQLCipherSyncTargetTests(
- SoledadWithCouchServerMixin, test_sync.DatabaseSyncTargetTests):
+ TestWithScenarios,
+ SoledadWithCouchServerMixin,
+ test_sync.DatabaseSyncTargetTests):
scenarios = (tests.multiply_scenarios(SQLCIPHER_SCENARIOS,
target_scenarios))
@@ -406,4 +396,3 @@ class SQLCipherSyncTargetTests(
self.db._last_exchange_log['return'],
{'last_gen': 2, 'docs':
[(doc.doc_id, doc.rev), (doc2.doc_id, doc2.rev)]})
-
diff --git a/common/src/leap/soledad/common/tests/test_sync.py b/common/src/leap/soledad/common/tests/test_sync.py
index 0433fac9..893df56b 100644
--- a/common/src/leap/soledad/common/tests/test_sync.py
+++ b/common/src/leap/soledad/common/tests/test_sync.py
@@ -16,43 +16,35 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import mock
-import os
import json
import tempfile
import threading
import time
+
from urlparse import urljoin
+from twisted.internet import defer
+
+from testscenarios import TestWithScenarios
from leap.soledad.common import couch
+from leap.soledad.client import target
+from leap.soledad.client import sync
+from leap.soledad.server import SoledadApp
-from leap.soledad.common.tests import BaseSoledadTest
-from leap.soledad.common.tests import test_sync_target
from leap.soledad.common.tests import u1db_tests as tests
-from leap.soledad.common.tests.u1db_tests import (
- TestCaseWithServer,
- simple_doc,
- test_backends,
- test_sync
-)
-from leap.soledad.common.tests.test_couch import CouchDBTestCase
-from leap.soledad.common.tests.test_target_soledad import (
- make_token_soledad_app,
- make_leap_document_for_test,
-)
-from leap.soledad.common.tests.test_sync_target import token_leap_sync_target
-from leap.soledad.client import (
- Soledad,
- target,
-)
+from leap.soledad.common.tests.u1db_tests import TestCaseWithServer
+from leap.soledad.common.tests.u1db_tests import simple_doc
+from leap.soledad.common.tests.u1db_tests import test_sync
+from leap.soledad.common.tests.util import make_token_soledad_app
+from leap.soledad.common.tests.util import make_soledad_document_for_test
+from leap.soledad.common.tests.util import token_soledad_sync_target
+from leap.soledad.common.tests.util import BaseSoledadTest
from leap.soledad.common.tests.util import SoledadWithCouchServerMixin
-from leap.soledad.client.sync import SoledadSynchronizer
-from leap.soledad.server import SoledadApp
-
+from leap.soledad.common.tests.test_couch import CouchDBTestCase
class InterruptableSyncTestCase(
- CouchDBTestCase, TestCaseWithServer):
+ BaseSoledadTest, CouchDBTestCase, TestCaseWithServer):
"""
Tests for encrypted sync using Soledad server backed by a couch database.
"""
@@ -61,47 +53,9 @@ class InterruptableSyncTestCase(
def make_app_with_state(state):
return make_token_soledad_app(state)
- make_document_for_test = make_leap_document_for_test
-
- sync_target = token_leap_sync_target
+ make_document_for_test = make_soledad_document_for_test
- def _soledad_instance(self, user='user-uuid', passphrase=u'123',
- prefix='',
- secrets_path=Soledad.STORAGE_SECRETS_FILE_NAME,
- local_db_path='soledad.u1db', server_url='',
- cert_file=None, auth_token=None, secret_id=None):
- """
- Instantiate Soledad.
- """
-
- # this callback ensures we save a document which is sent to the shared
- # db.
- def _put_doc_side_effect(doc):
- self._doc_put = doc
-
- # we need a mocked shared db or else Soledad will try to access the
- # network to find if there are uploaded secrets.
- class MockSharedDB(object):
-
- get_doc = mock.Mock(return_value=None)
- put_doc = mock.Mock(side_effect=_put_doc_side_effect)
- lock = mock.Mock(return_value=('atoken', 300))
- unlock = mock.Mock()
-
- def __call__(self):
- return self
-
- Soledad._shared_db = MockSharedDB()
- return Soledad(
- user,
- passphrase,
- secrets_path=os.path.join(self.tempdir, prefix, secrets_path),
- local_db_path=os.path.join(
- self.tempdir, prefix, local_db_path),
- server_url=server_url,
- cert_file=cert_file,
- auth_token=auth_token,
- secret_id=secret_id)
+ sync_target = token_soledad_sync_target
def make_app(self):
self.request_state = couch.CouchServerState(
@@ -135,7 +89,8 @@ class InterruptableSyncTestCase(
def run(self):
while db._get_generation() < 2:
- time.sleep(1)
+ #print "WAITING %d" % db._get_generation()
+ time.sleep(0.1)
self._soledad.stop_sync()
time.sleep(1)
@@ -143,16 +98,7 @@ class InterruptableSyncTestCase(
self.startServer()
# instantiate soledad and create a document
- sol = self._soledad_instance(
- # token is verified in test_target.make_token_soledad_app
- auth_token='auth-token'
- )
- _, doclist = sol.get_all_docs()
- self.assertEqual([], doclist)
-
- # create many small files
- for i in range(0, number_of_docs):
- sol.create_doc(json.loads(simple_doc))
+ sol = self._soledad_instance(user='user-uuid', server_url=self.getURL())
# ensure remote db exists before syncing
db = couch.CouchDatabase.open_database(
@@ -164,21 +110,35 @@ class InterruptableSyncTestCase(
t = _SyncInterruptor(sol, db)
t.start()
- # sync with server
- sol._server_url = self.getURL()
- sol.sync() # this will be interrupted when couch db gen >= 2
- t.join()
+ d = sol.get_all_docs()
+ d.addCallback(lambda results: self.assertEqual([], results[1]))
- # recover the sync process
- sol.sync()
+ def _create_docs(results):
+ # create many small files
+ deferreds = []
+ for i in range(0, number_of_docs):
+ deferreds.append(sol.create_doc(json.loads(simple_doc)))
+ return defer.DeferredList(deferreds)
- gen, doclist = db.get_all_docs()
- self.assertEqual(number_of_docs, len(doclist))
-
- # delete remote database
- db.delete_database()
- db.close()
- sol.close()
+ # sync with server
+ d.addCallback(_create_docs)
+ d.addCallback(lambda _: sol.get_all_docs())
+ d.addCallback(lambda results: self.assertEqual(number_of_docs, len(results[1])))
+ d.addCallback(lambda _: sol.sync())
+ d.addCallback(lambda _: t.join())
+ d.addCallback(lambda _: db.get_all_docs())
+ d.addCallback(lambda results: self.assertNotEqual(number_of_docs, len(results[1])))
+ d.addCallback(lambda _: sol.sync())
+ d.addCallback(lambda _: db.get_all_docs())
+ d.addCallback(lambda results: self.assertEqual(number_of_docs, len(results[1])))
+
+ def _tear_down(results):
+ db.delete_database()
+ db.close()
+ sol.close()
+
+ d.addCallback(_tear_down)
+ return d
def make_soledad_app(state):
@@ -186,6 +146,7 @@ def make_soledad_app(state):
class TestSoledadDbSync(
+ TestWithScenarios,
SoledadWithCouchServerMixin,
test_sync.TestDbSync):
"""
@@ -198,7 +159,7 @@ class TestSoledadDbSync(
'make_database_for_test': tests.make_memory_database_for_test,
}),
('py-token-http', {
- 'make_app_with_state': test_sync_target.make_token_soledad_app,
+ 'make_app_with_state': make_token_soledad_app,
'make_database_for_test': tests.make_memory_database_for_test,
'token': True
}),
@@ -211,10 +172,11 @@ class TestSoledadDbSync(
"""
Need to explicitely invoke inicialization on all bases.
"""
- tests.TestCaseWithServer.setUp(self)
- self.main_test_class = test_sync.TestDbSync
+ #tests.TestCaseWithServer.setUp(self)
+ #self.main_test_class = test_sync.TestDbSync
SoledadWithCouchServerMixin.setUp(self)
self.startServer()
+ self.db = self.make_database_for_test(self, 'test1')
self.db2 = couch.CouchDatabase.open_database(
urljoin(
'http://localhost:' + str(self.wrapper.port), 'test'),
@@ -227,7 +189,7 @@ class TestSoledadDbSync(
"""
self.db2.delete_database()
SoledadWithCouchServerMixin.tearDown(self)
- tests.TestCaseWithServer.tearDown(self)
+ #tests.TestCaseWithServer.tearDown(self)
def do_sync(self, target_name):
"""
@@ -240,7 +202,7 @@ class TestSoledadDbSync(
'token': 'auth-token',
}})
target_url = self.getURL(target_name)
- return SoledadSynchronizer(
+ return sync.SoledadSynchronizer(
self.db,
target.SoledadSyncTarget(
target_url,
@@ -254,8 +216,10 @@ class TestSoledadDbSync(
Adapted to check for encrypted content.
"""
+
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('test')
gen, _, changes = self.db.whats_changed(local_gen_before_sync)
self.assertEqual(1, len(changes))
@@ -287,6 +251,3 @@ class TestSoledadDbSync(
s_gen, _ = db3._get_replica_gen_and_trans_id('test1')
self.assertEqual(1, t_gen)
self.assertEqual(1, s_gen)
-
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_sync_deferred.py b/common/src/leap/soledad/common/tests/test_sync_deferred.py
index 07a9742b..26889aff 100644
--- a/common/src/leap/soledad/common/tests/test_sync_deferred.py
+++ b/common/src/leap/soledad/common/tests/test_sync_deferred.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# test_sync_deferred.py
# Copyright (C) 2014 LEAP
#
@@ -21,28 +20,31 @@ import time
import os
import random
import string
+import shutil
+
from urlparse import urljoin
-from leap.soledad.common.tests import u1db_tests as tests, ADDRESS
+from leap.soledad.common import couch
+from leap.soledad.client.sqlcipher import (
+ SQLCipherOptions,
+ SQLCipherDatabase,
+ SQLCipherU1DBSync,
+)
+
+from testscenarios import TestWithScenarios
+
+from leap.soledad.common.tests import u1db_tests as tests
from leap.soledad.common.tests.u1db_tests import test_sync
+from leap.soledad.common.tests.util import ADDRESS
+from leap.soledad.common.tests.util import SoledadWithCouchServerMixin
+from leap.soledad.common.tests.util import make_soledad_app
-from leap.soledad.common.document import SoledadDocument
-from leap.soledad.common import couch
-from leap.soledad.client import target
-from leap.soledad.client.sync import SoledadSynchronizer
# Just to make clear how this test is different... :)
DEFER_DECRYPTION = True
WAIT_STEP = 1
MAX_WAIT = 10
-
-
-from leap.soledad.client.sqlcipher import open as open_sqlcipher
-from leap.soledad.common.tests.util import SoledadWithCouchServerMixin
-from leap.soledad.common.tests.util import make_soledad_app
-
-
DBPASS = "pass"
@@ -54,8 +56,10 @@ class BaseSoledadDeferredEncTest(SoledadWithCouchServerMixin):
defer_sync_encryption = True
def setUp(self):
+ SoledadWithCouchServerMixin.setUp(self)
# config info
self.db1_file = os.path.join(self.tempdir, "db1.u1db")
+ os.unlink(self.db1_file)
self.db_pass = DBPASS
self.email = ADDRESS
@@ -64,17 +68,21 @@ class BaseSoledadDeferredEncTest(SoledadWithCouchServerMixin):
# each local db.
self.rand_prefix = ''.join(
map(lambda x: random.choice(string.ascii_letters), range(6)))
- # initialize soledad by hand so we can control keys
- self._soledad = self._soledad_instance(
- prefix=self.rand_prefix, user=self.email)
-
- # open test dbs: db1 will be the local sqlcipher db
- # (which instantiates a syncdb)
- self.db1 = open_sqlcipher(self.db1_file, DBPASS, create=True,
- document_factory=SoledadDocument,
- crypto=self._soledad._crypto,
- defer_encryption=True,
- sync_db_key=DBPASS)
+
+ # open test dbs: db1 will be the local sqlcipher db (which
+ # instantiates a syncdb). We use the self._soledad instance that was
+ # already created on some setUp method.
+ import binascii
+ tohex = binascii.b2a_hex
+ key = tohex(self._soledad.secrets.get_local_storage_key())
+ sync_db_key = tohex(self._soledad.secrets.get_sync_db_key())
+ dbpath = self._soledad._local_db_path
+
+ self.opts = SQLCipherOptions(
+ dbpath, key, is_raw_key=True, create=False,
+ defer_encryption=True, sync_db_key=sync_db_key)
+ self.db1 = SQLCipherDatabase(self.opts)
+
self.db2 = couch.CouchDatabase.open_database(
urljoin(
'http://localhost:' + str(self.wrapper.port), 'test'),
@@ -87,20 +95,8 @@ class BaseSoledadDeferredEncTest(SoledadWithCouchServerMixin):
self._soledad.close()
# XXX should not access "private" attrs
- import shutil
shutil.rmtree(os.path.dirname(self._soledad._local_db_path))
-
-
-#SQLCIPHER_SCENARIOS = [
-# ('http', {
-# #'make_app_with_state': test_sync_target.make_token_soledad_app,
-# 'make_app_with_state': make_soledad_app,
-# 'make_database_for_test': ts.make_sqlcipher_database_for_test,
-# 'copy_database_for_test': ts.copy_sqlcipher_database_for_test,
-# 'make_document_for_test': ts.make_document_for_test,
-# 'token': True
-# }),
-#]
+ SoledadWithCouchServerMixin.tearDown(self)
class SyncTimeoutError(Exception):
@@ -111,8 +107,9 @@ class SyncTimeoutError(Exception):
class TestSoledadDbSyncDeferredEncDecr(
- BaseSoledadDeferredEncTest,
- test_sync.TestDbSync):
+ TestWithScenarios,
+ test_sync.TestDbSync,
+ BaseSoledadDeferredEncTest):
"""
Test db.sync remote sync shortcut.
Case with deferred encryption and decryption: using the intermediate
@@ -129,13 +126,17 @@ class TestSoledadDbSyncDeferredEncDecr(
oauth = False
token = True
+ def make_app(self):
+ self.request_state = couch.CouchServerState(
+ self._couch_url, 'shared', 'tokens')
+ return self.make_app_with_state(self.request_state)
+
def setUp(self):
"""
Need to explicitely invoke inicialization on all bases.
"""
- tests.TestCaseWithServer.setUp(self)
- self.main_test_class = test_sync.TestDbSync
BaseSoledadDeferredEncTest.setUp(self)
+ self.server = self.server_thread = None
self.startServer()
self.syncer = None
@@ -143,8 +144,10 @@ class TestSoledadDbSyncDeferredEncDecr(
"""
Need to explicitely invoke destruction on all bases.
"""
+ dbsyncer = getattr(self, 'dbsyncer', None)
+ if dbsyncer:
+ dbsyncer.close()
BaseSoledadDeferredEncTest.tearDown(self)
- tests.TestCaseWithServer.tearDown(self)
def do_sync(self, target_name):
"""
@@ -152,25 +155,20 @@ class TestSoledadDbSyncDeferredEncDecr(
and Token auth.
"""
if self.token:
- extra = dict(creds={'token': {
+ creds={'token': {
'uuid': 'user-uuid',
'token': 'auth-token',
- }})
+ }}
target_url = self.getURL(target_name)
- syncdb = getattr(self.db1, "_sync_db", None)
-
- syncer = SoledadSynchronizer(
- self.db1,
- target.SoledadSyncTarget(
- target_url,
- crypto=self._soledad._crypto,
- sync_db=syncdb,
- **extra))
- # Keep a reference to be able to know when the sync
- # has finished.
- self.syncer = syncer
- return syncer.sync(
- autocreate=True, defer_decryption=DEFER_DECRYPTION)
+
+ # get a u1db syncer
+ crypto = self._soledad._crypto
+ replica_uid = self.db1._replica_uid
+ dbsyncer = SQLCipherU1DBSync(self.opts, crypto, replica_uid,
+ defer_encryption=True)
+ self.dbsyncer = dbsyncer
+ return dbsyncer.sync(target_url, creds=creds,
+ autocreate=True,defer_decryption=DEFER_DECRYPTION)
else:
return test_sync.TestDbSync.do_sync(self, target_name)
@@ -195,28 +193,30 @@ class TestSoledadDbSyncDeferredEncDecr(
"""
doc1 = self.db1.create_doc_from_json(tests.simple_doc)
doc2 = self.db2.create_doc_from_json(tests.nested_doc)
+ d = self.do_sync('test')
- import time
- # need to give time to the encryption to proceed
- # TODO should implement a defer list to subscribe to the all-decrypted
- # event
- time.sleep(2)
+ def _assert_successful_sync(results):
+ import time
+ # need to give time to the encryption to proceed
+ # TODO should implement a defer list to subscribe to the all-decrypted
+ # event
+ time.sleep(2)
+ local_gen_before_sync = results
+ self.wait_for_sync()
- local_gen_before_sync = self.do_sync('test')
- self.wait_for_sync()
+ gen, _, changes = self.db1.whats_changed(local_gen_before_sync)
+ self.assertEqual(1, len(changes))
- gen, _, changes = self.db1.whats_changed(local_gen_before_sync)
- self.assertEqual(1, len(changes))
+ self.assertEqual(doc2.doc_id, changes[0][0])
+ self.assertEqual(1, gen - local_gen_before_sync)
- self.assertEqual(doc2.doc_id, changes[0][0])
- self.assertEqual(1, gen - local_gen_before_sync)
+ self.assertGetEncryptedDoc(
+ self.db2, doc1.doc_id, doc1.rev, tests.simple_doc, False)
+ self.assertGetEncryptedDoc(
+ self.db1, doc2.doc_id, doc2.rev, tests.nested_doc, False)
- self.assertGetEncryptedDoc(
- self.db2, doc1.doc_id, doc1.rev, tests.simple_doc, False)
- self.assertGetEncryptedDoc(
- self.db1, doc2.doc_id, doc2.rev, tests.nested_doc, False)
+ d.addCallback(_assert_successful_sync)
+ return d
def test_db_sync_autocreate(self):
pass
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_sync_target.py b/common/src/leap/soledad/common/tests/test_sync_target.py
index 45009f4e..3792159a 100644
--- a/common/src/leap/soledad/common/tests/test_sync_target.py
+++ b/common/src/leap/soledad/common/tests/test_sync_target.py
@@ -19,83 +19,39 @@ Test Leap backend bits: sync target
"""
import cStringIO
import os
-
+import time
import simplejson as json
import u1db
+import random
+import string
+import shutil
+
+from testscenarios import TestWithScenarios
+from urlparse import urljoin
-from u1db.remote import http_database
+from leap.soledad.client import target
+from leap.soledad.client import crypto
+from leap.soledad.client.sqlcipher import SQLCipherU1DBSync
+from leap.soledad.client.sqlcipher import SQLCipherOptions
+from leap.soledad.client.sqlcipher import SQLCipherDatabase
-from leap.soledad.client import (
- target,
- auth,
- crypto,
- sync,
-)
+from leap.soledad.common import couch
from leap.soledad.common.document import SoledadDocument
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.util import make_sqlcipher_database_for_test
+from leap.soledad.common.tests.util import make_soledad_app
+from leap.soledad.common.tests.util import make_token_soledad_app
+from leap.soledad.common.tests.util import make_soledad_document_for_test
+from leap.soledad.common.tests.util import token_soledad_sync_target
+from leap.soledad.common.tests.util import BaseSoledadTest
+from leap.soledad.common.tests.util import SoledadWithCouchServerMixin
+from leap.soledad.common.tests.util import ADDRESS
from leap.soledad.common.tests.u1db_tests import test_remote_sync_target
from leap.soledad.common.tests.u1db_tests import test_sync
#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_backends`.
-#-----------------------------------------------------------------------------
-
-def make_leap_document_for_test(test, doc_id, rev, content,
- has_conflicts=False):
- return SoledadDocument(
- doc_id, rev, content, has_conflicts=has_conflicts)
-
-
-LEAP_SCENARIOS = [
- ('http', {
- 'make_database_for_test': test_backends.make_http_database_for_test,
- 'copy_database_for_test': test_backends.copy_http_database_for_test,
- 'make_document_for_test': make_leap_document_for_test,
- 'make_app_with_state': make_soledad_app}),
-]
-
-
-def make_token_http_database_for_test(test, replica_uid):
- test.startServer()
- test.request_state._create_database(replica_uid)
-
- class _HTTPDatabaseWithToken(
- http_database.HTTPDatabase, auth.TokenBasedAuth):
-
- def set_token_credentials(self, uuid, token):
- auth.TokenBasedAuth.set_token_credentials(self, uuid, token)
-
- def _sign_request(self, method, url_query, params):
- return auth.TokenBasedAuth._sign_request(
- self, method, url_query, params)
-
- http_db = _HTTPDatabaseWithToken(test.getURL('test'))
- http_db.set_token_credentials('user-uuid', 'auth-token')
- return http_db
-
-
-def copy_token_http_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.
- http_db = test.request_state._copy_database(db)
- http_db.set_token_credentials(http_db, 'user-uuid', 'auth-token')
- return http_db
-
-
-#-----------------------------------------------------------------------------
# The following tests come from `u1db.tests.test_remote_sync_target`.
#-----------------------------------------------------------------------------
@@ -122,12 +78,6 @@ class TestSoledadParsingSyncStream(
target.
"""
- def setUp(self):
- test_remote_sync_target.TestParsingSyncStream.setUp(self)
-
- def tearDown(self):
- test_remote_sync_target.TestParsingSyncStream.tearDown(self)
-
def test_extra_comma(self):
"""
Test adapted to use encrypted content.
@@ -209,17 +159,6 @@ class TestSoledadParsingSyncStream(
# functions for TestRemoteSyncTargets
#
-def leap_sync_target(test, path):
- return target.SoledadSyncTarget(
- test.getURL(path), crypto=test._soledad._crypto)
-
-
-def token_leap_sync_target(test, path):
- st = leap_sync_target(test, path)
- st.set_token_credentials('user-uuid', 'auth-token')
- return st
-
-
def make_local_db_and_soledad_target(test, path='test'):
test.startServer()
db = test.request_state._create_database(os.path.basename(path))
@@ -235,32 +174,32 @@ def make_local_db_and_token_soledad_target(test):
class TestSoledadSyncTarget(
+ TestWithScenarios,
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,
+ 'make_document_for_test': make_soledad_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}),
+ 'sync_target': token_soledad_sync_target}),
]
def setUp(self):
- tests.TestCaseWithServer.setUp(self)
- self.main_test_class = test_remote_sync_target.TestRemoteSyncTargets
+ TestWithScenarios.setUp(self)
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)
- db2, _ = self.request_state.ensure_database('test2')
- db2.delete_database()
+ #db2, _ = self.request_state.ensure_database('test2')
+ self.db2.delete_database()
self.db1.close()
+ SoledadWithCouchServerMixin.tearDown(self)
+ TestWithScenarios.tearDown(self)
def test_sync_exchange_send(self):
"""
@@ -268,7 +207,6 @@ class TestSoledadSyncTarget(
This test was adapted to decrypt remote content before assert.
"""
- self.startServer()
db = self.request_state._create_database('test')
remote_target = self.getSyncTarget('test')
other_docs = []
@@ -289,14 +227,9 @@ class TestSoledadSyncTarget(
"""
Test for sync exchange failure and retry.
- This test was adapted to:
- - decrypt remote content before assert.
- - not expect a bounced document because soledad has stateful
- recoverable sync.
+ This test was adapted to decrypt remote content before assert.
"""
- self.startServer()
-
def blackhole_getstderr(inst):
return cStringIO.StringIO()
@@ -332,8 +265,9 @@ class TestSoledadSyncTarget(
doc2 = self.make_document('doc-here2', 'replica:1',
'{"value": "here2"}')
- # we do not expect an HTTPError because soledad sync fails gracefully
- remote_target.sync_exchange(
+ self.assertRaises(
+ u1db.errors.HTTPError,
+ remote_target.sync_exchange,
[(doc1, 10, 'T-sid'), (doc2, 11, 'T-sud')],
'replica', last_known_generation=0, last_known_trans_id=None,
return_doc_cb=receive_doc)
@@ -364,7 +298,6 @@ class TestSoledadSyncTarget(
This test was adapted to decrypt remote content before assert.
"""
- self.startServer()
remote_target = self.getSyncTarget('test')
other_docs = []
replica_uid_box = []
@@ -405,7 +338,9 @@ target_scenarios = [
class SoledadDatabaseSyncTargetTests(
- SoledadWithCouchServerMixin, test_sync.DatabaseSyncTargetTests):
+ TestWithScenarios,
+ SoledadWithCouchServerMixin,
+ test_sync.DatabaseSyncTargetTests):
scenarios = (
tests.multiply_scenarios(
@@ -500,8 +435,25 @@ class SoledadDatabaseSyncTargetTests(
[(doc.doc_id, doc.rev), (doc2.doc_id, doc2.rev)]})
+# Just to make clear how this test is different... :)
+DEFER_DECRYPTION = False
+
+WAIT_STEP = 1
+MAX_WAIT = 10
+DBPASS = "pass"
+
+
+class SyncTimeoutError(Exception):
+ """
+ Dummy exception to notify timeout during sync.
+ """
+ pass
+
+
class TestSoledadDbSync(
- SoledadWithCouchServerMixin, test_sync.TestDbSync):
+ TestWithScenarios,
+ SoledadWithCouchServerMixin,
+ test_sync.TestDbSync):
"""Test db.sync remote sync shortcut"""
scenarios = [
@@ -516,13 +468,67 @@ class TestSoledadDbSync(
oauth = False
token = False
+
+ def make_app(self):
+ self.request_state = couch.CouchServerState(
+ self._couch_url, 'shared', 'tokens')
+ return self.make_app_with_state(self.request_state)
+
def setUp(self):
- self.main_test_class = test_sync.TestDbSync
+ """
+ Need to explicitely invoke inicialization on all bases.
+ """
SoledadWithCouchServerMixin.setUp(self)
+ self.server = self.server_thread = None
+ self.startServer()
+ self.syncer = None
+
+ # config info
+ self.db1_file = os.path.join(self.tempdir, "db1.u1db")
+ os.unlink(self.db1_file)
+ self.db_pass = DBPASS
+ self.email = ADDRESS
+
+ # get a random prefix for each test, so we do not mess with
+ # concurrency during initialization and shutting down of
+ # each local db.
+ self.rand_prefix = ''.join(
+ map(lambda x: random.choice(string.ascii_letters), range(6)))
+
+ # open test dbs: db1 will be the local sqlcipher db (which
+ # instantiates a syncdb). We use the self._soledad instance that was
+ # already created on some setUp method.
+ import binascii
+ tohex = binascii.b2a_hex
+ key = tohex(self._soledad.secrets.get_local_storage_key())
+ sync_db_key = tohex(self._soledad.secrets.get_sync_db_key())
+ dbpath = self._soledad._local_db_path
+
+ self.opts = SQLCipherOptions(
+ dbpath, key, is_raw_key=True, create=False,
+ defer_encryption=True, sync_db_key=sync_db_key)
+ self.db1 = SQLCipherDatabase(self.opts)
+
+ self.db2 = couch.CouchDatabase.open_database(
+ urljoin(
+ 'http://localhost:' + str(self.wrapper.port), 'test'),
+ create=True,
+ ensure_ddocs=True)
def tearDown(self):
+ """
+ Need to explicitely invoke destruction on all bases.
+ """
+ dbsyncer = getattr(self, 'dbsyncer', None)
+ if dbsyncer:
+ dbsyncer.close()
+ self.db1.close()
+ self.db2.close()
+ self._soledad.close()
+
+ # XXX should not access "private" attrs
+ shutil.rmtree(os.path.dirname(self._soledad._local_db_path))
SoledadWithCouchServerMixin.tearDown(self)
- self.db.close()
def do_sync(self, target_name):
"""
@@ -530,44 +536,71 @@ class TestSoledadDbSync(
and Token auth.
"""
if self.token:
- extra = dict(creds={'token': {
+ creds={'token': {
'uuid': 'user-uuid',
'token': 'auth-token',
- }})
+ }}
target_url = self.getURL(target_name)
- return sync.SoledadSynchronizer(
- self.db,
- target.SoledadSyncTarget(
- target_url,
- crypto=self._soledad._crypto,
- **extra)).sync(autocreate=True,
- defer_decryption=False)
+
+ # get a u1db syncer
+ crypto = self._soledad._crypto
+ replica_uid = self.db1._replica_uid
+ dbsyncer = SQLCipherU1DBSync(self.opts, crypto, replica_uid,
+ defer_encryption=True)
+ self.dbsyncer = dbsyncer
+ return dbsyncer.sync(target_url, creds=creds,
+ autocreate=True,defer_decryption=DEFER_DECRYPTION)
else:
return test_sync.TestDbSync.do_sync(self, target_name)
+ def wait_for_sync(self):
+ """
+ Wait for sync to finish.
+ """
+ wait = 0
+ syncer = self.syncer
+ if syncer is not None:
+ while syncer.syncing:
+ time.sleep(WAIT_STEP)
+ wait += WAIT_STEP
+ if wait >= MAX_WAIT:
+ raise SyncTimeoutError
+
def test_db_sync(self):
"""
Test sync.
Adapted to check for encrypted content.
"""
- doc1 = self.db.create_doc_from_json(tests.simple_doc)
+ doc1 = self.db1.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')
- gen, _, changes = self.db.whats_changed(local_gen_before_sync)
- self.assertEqual(1, len(changes))
- self.assertEqual(doc2.doc_id, changes[0][0])
- self.assertEqual(1, gen - local_gen_before_sync)
- self.assertGetEncryptedDoc(
- self.db2, doc1.doc_id, doc1.rev, tests.simple_doc, False)
- self.assertGetEncryptedDoc(
- self.db, doc2.doc_id, doc2.rev, tests.nested_doc, False)
+ d = self.do_sync('test')
+
+ def _assert_successful_sync(results):
+ import time
+ # need to give time to the encryption to proceed
+ # TODO should implement a defer list to subscribe to the all-decrypted
+ # event
+ time.sleep(2)
+ local_gen_before_sync = results
+ self.wait_for_sync()
+
+ gen, _, changes = self.db1.whats_changed(local_gen_before_sync)
+ self.assertEqual(1, len(changes))
+
+ self.assertEqual(doc2.doc_id, changes[0][0])
+ self.assertEqual(1, gen - local_gen_before_sync)
+
+ self.assertGetEncryptedDoc(
+ self.db2, doc1.doc_id, doc1.rev, tests.simple_doc, False)
+ self.assertGetEncryptedDoc(
+ self.db1, doc2.doc_id, doc2.rev, tests.nested_doc, False)
+
+ d.addCallback(_assert_successful_sync)
+ return d
def test_db_sync_autocreate(self):
"""
We bypass this test because we never need to autocreate databases.
"""
pass
-
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_target.py b/common/src/leap/soledad/common/tests/test_target.py
deleted file mode 100644
index eb5e2874..00000000
--- a/common/src/leap/soledad/common/tests/test_target.py
+++ /dev/null
@@ -1,797 +0,0 @@
-# -*- coding: utf-8 -*-
-# test_target.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/>.
-
-
-"""
-Test Leap backend bits.
-"""
-
-import u1db
-import os
-import simplejson as json
-import cStringIO
-
-
-from u1db.sync import Synchronizer
-from u1db.remote import (
- http_client,
- http_database,
-)
-
-from leap.soledad import client
-from leap.soledad.client import (
- target,
- auth,
- VerifiedHTTPSConnection,
-)
-from leap.soledad.common.document import SoledadDocument
-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
-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
-
-
-#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_backends`.
-#-----------------------------------------------------------------------------
-
-def make_leap_document_for_test(test, doc_id, rev, content,
- has_conflicts=False):
- return SoledadDocument(
- doc_id, rev, content, has_conflicts=has_conflicts)
-
-
-LEAP_SCENARIOS = [
- ('http', {
- 'make_database_for_test': test_backends.make_http_database_for_test,
- 'copy_database_for_test': test_backends.copy_http_database_for_test,
- 'make_document_for_test': make_leap_document_for_test,
- 'make_app_with_state': make_soledad_app}),
-]
-
-
-def make_token_http_database_for_test(test, replica_uid):
- test.startServer()
- test.request_state._create_database(replica_uid)
-
- class _HTTPDatabaseWithToken(
- http_database.HTTPDatabase, auth.TokenBasedAuth):
-
- def set_token_credentials(self, uuid, token):
- auth.TokenBasedAuth.set_token_credentials(self, uuid, token)
-
- def _sign_request(self, method, url_query, params):
- return auth.TokenBasedAuth._sign_request(
- self, method, url_query, params)
-
- http_db = _HTTPDatabaseWithToken(test.getURL('test'))
- http_db.set_token_credentials('user-uuid', 'auth-token')
- return http_db
-
-
-def copy_token_http_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.
- http_db = test.request_state._copy_database(db)
- http_db.set_token_credentials(http_db, 'user-uuid', 'auth-token')
- return http_db
-
-
-class SoledadTests(test_backends.AllDatabaseTests, BaseSoledadTest):
-
- scenarios = LEAP_SCENARIOS + [
- ('token_http', {'make_database_for_test':
- make_token_http_database_for_test,
- 'copy_database_for_test':
- copy_token_http_database_for_test,
- 'make_document_for_test': make_leap_document_for_test,
- 'make_app_with_state': make_token_soledad_app,
- })
- ]
-
-
-#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_http_client`.
-#-----------------------------------------------------------------------------
-
-class TestSoledadClientBase(test_http_client.TestHTTPClientBase):
- """
- This class should be used to test Token auth.
- """
-
- def getClientWithToken(self, **kwds):
- self.startServer()
-
- class _HTTPClientWithToken(
- http_client.HTTPClientBase, auth.TokenBasedAuth):
-
- def set_token_credentials(self, uuid, token):
- auth.TokenBasedAuth.set_token_credentials(self, uuid, token)
-
- def _sign_request(self, method, url_query, params):
- return auth.TokenBasedAuth._sign_request(
- self, method, url_query, params)
-
- return _HTTPClientWithToken(self.getURL('dbase'), **kwds)
-
- def test_oauth(self):
- """
- Suppress oauth test (we test for token auth here).
- """
- pass
-
- def test_oauth_ctr_creds(self):
- """
- Suppress oauth test (we test for token auth here).
- """
- pass
-
- def test_oauth_Unauthorized(self):
- """
- Suppress oauth test (we test for token auth here).
- """
- pass
-
- def app(self, environ, start_response):
- res = test_http_client.TestHTTPClientBase.app(
- self, environ, start_response)
- if res is not None:
- return res
- # mime solead application here.
- if '/token' in environ['PATH_INFO']:
- auth = environ.get(SoledadTokenAuthMiddleware.HTTP_AUTH_KEY)
- if not auth:
- start_response("401 Unauthorized",
- [('Content-Type', 'application/json')])
- return [json.dumps({"error": "unauthorized",
- "message": e.message})]
- scheme, encoded = auth.split(None, 1)
- if scheme.lower() != 'token':
- start_response("401 Unauthorized",
- [('Content-Type', 'application/json')])
- return [json.dumps({"error": "unauthorized",
- "message": e.message})]
- uuid, token = encoded.decode('base64').split(':', 1)
- if uuid != 'user-uuid' and token != 'auth-token':
- return unauth_err("Incorrect address or token.")
- start_response("200 OK", [('Content-Type', 'application/json')])
- return [json.dumps([environ['PATH_INFO'], uuid, token])]
-
- def test_token(self):
- """
- Test if token is sent correctly.
- """
- cli = self.getClientWithToken()
- cli.set_token_credentials('user-uuid', 'auth-token')
- res, headers = cli._request('GET', ['doc', 'token'])
- self.assertEqual(
- ['/dbase/doc/token', 'user-uuid', 'auth-token'], json.loads(res))
-
- def test_token_ctr_creds(self):
- cli = self.getClientWithToken(creds={'token': {
- 'uuid': 'user-uuid',
- 'token': 'auth-token',
- }})
- res, headers = cli._request('GET', ['doc', 'token'])
- self.assertEqual(
- ['/dbase/doc/token', 'user-uuid', 'auth-token'], json.loads(res))
-
-
-#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_document`.
-#-----------------------------------------------------------------------------
-
-class TestSoledadDocument(test_document.TestDocument, BaseSoledadTest):
-
- scenarios = ([(
- 'leap', {'make_document_for_test': make_leap_document_for_test})])
-
-
-class TestSoledadPyDocument(test_document.TestPyDocument, BaseSoledadTest):
-
- scenarios = ([(
- 'leap', {'make_document_for_test': make_leap_document_for_test})])
-
-
-#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_remote_sync_target`.
-#-----------------------------------------------------------------------------
-
-class TestSoledadSyncTargetBasics(
- test_remote_sync_target.TestHTTPSyncTargetBasics):
- """
- Some tests had to be copied to this class so we can instantiate our own
- target.
- """
-
- def test_parse_url(self):
- remote_target = target.SoledadSyncTarget('http://127.0.0.1:12345/')
- self.assertEqual('http', remote_target._url.scheme)
- self.assertEqual('127.0.0.1', remote_target._url.hostname)
- self.assertEqual(12345, remote_target._url.port)
- self.assertEqual('/', remote_target._url.path)
-
-
-class TestSoledadParsingSyncStream(
- test_remote_sync_target.TestParsingSyncStream,
- BaseSoledadTest):
- """
- Some tests had to be copied to this class so we can instantiate our own
- target.
- """
-
- def setUp(self):
- test_remote_sync_target.TestParsingSyncStream.setUp(self)
-
- def tearDown(self):
- test_remote_sync_target.TestParsingSyncStream.tearDown(self)
-
- def test_extra_comma(self):
- """
- Test adapted to use encrypted content.
- """
- doc = SoledadDocument('i', rev='r')
- doc.content = {}
- enc_json = target.encrypt_doc(self._soledad._crypto, doc)
- tgt = target.SoledadSyncTarget(
- "http://foo/foo", crypto=self._soledad._crypto)
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream, "[\r\n{},\r\n]", None)
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream,
- '[\r\n{},\r\n{"id": "i", "rev": "r", '
- '"content": %s, "gen": 3, "trans_id": "T-sid"}'
- ',\r\n]' % json.dumps(enc_json),
- lambda doc, gen, trans_id: None)
-
- def test_wrong_start(self):
- tgt = target.SoledadSyncTarget("http://foo/foo")
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream, "{}\r\n]", None)
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream, "\r\n{}\r\n]", None)
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream, "", None)
-
- def test_wrong_end(self):
- tgt = target.SoledadSyncTarget("http://foo/foo")
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream, "[\r\n{}", None)
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream, "[\r\n", None)
-
- def test_missing_comma(self):
- tgt = target.SoledadSyncTarget("http://foo/foo")
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream,
- '[\r\n{}\r\n{"id": "i", "rev": "r", '
- '"content": "c", "gen": 3}\r\n]', None)
-
- def test_no_entries(self):
- tgt = target.SoledadSyncTarget("http://foo/foo")
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream, "[\r\n]", None)
-
- def test_error_in_stream(self):
- tgt = target.SoledadSyncTarget("http://foo/foo")
-
- self.assertRaises(u1db.errors.Unavailable,
- tgt._parse_sync_stream,
- '[\r\n{"new_generation": 0},'
- '\r\n{"error": "unavailable"}\r\n', None)
-
- self.assertRaises(u1db.errors.Unavailable,
- tgt._parse_sync_stream,
- '[\r\n{"error": "unavailable"}\r\n', None)
-
- self.assertRaises(u1db.errors.BrokenSyncStream,
- tgt._parse_sync_stream,
- '[\r\n{"error": "?"}\r\n', None)
-
-
-#
-# functions for TestRemoteSyncTargets
-#
-
-def leap_sync_target(test, path):
- return target.SoledadSyncTarget(
- test.getURL(path), crypto=test._soledad._crypto)
-
-
-def token_leap_sync_target(test, path):
- st = leap_sync_target(test, path)
- st.set_token_credentials('user-uuid', 'auth-token')
- 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(
- 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()
- for i in ['db1', 'db2']:
- if hasattr(self, i):
- db = getattr(self, i)
- db.close()
-
- def test_sync_exchange_send(self):
- """
- Test for sync exchanging send of document.
-
- This test was adapted to decrypt remote content before assert.
- """
- self.startServer()
- db = self.request_state._create_database('test')
- remote_target = self.getSyncTarget('test')
- other_docs = []
-
- 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"}')
- new_gen, trans_id = remote_target.sync_exchange(
- [(doc, 10, 'T-sid')], 'replica', last_known_generation=0,
- last_known_trans_id=None, return_doc_cb=receive_doc)
- self.assertEqual(1, new_gen)
- self.assertGetEncryptedDoc(
- db, 'doc-here', 'replica:1', '{"value": "here"}', False)
- db.close()
-
- def test_sync_exchange_send_failure_and_retry_scenario(self):
- """
- Test for sync exchange failure and retry.
-
- This test was adapted to:
- - decrypt remote content before assert.
- - not expect a bounced document because soledad has stateful
- recoverable sync.
- """
-
- self.startServer()
-
- def blackhole_getstderr(inst):
- return cStringIO.StringIO()
-
- self.patch(self.server.RequestHandlerClass, 'get_stderr',
- blackhole_getstderr)
- db = self.request_state._create_database('test')
- _put_doc_if_newer = db._put_doc_if_newer
- trigger_ids = ['doc-here2']
-
- def bomb_put_doc_if_newer(self, doc, save_conflict,
- replica_uid=None, replica_gen=None,
- replica_trans_id=None, number_of_docs=None,
- doc_idx=None, sync_id=None):
- if doc.doc_id in trigger_ids:
- raise Exception
- return _put_doc_if_newer(doc, save_conflict=save_conflict,
- replica_uid=replica_uid,
- replica_gen=replica_gen,
- replica_trans_id=replica_trans_id,
- number_of_docs=number_of_docs,
- doc_idx=doc_idx,
- sync_id=sync_id)
- 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 = []
-
- def receive_doc(doc, gen, trans_id):
- other_changes.append(
- (doc.doc_id, doc.rev, doc.get_json(), gen, trans_id))
-
- doc1 = self.make_document('doc-here', 'replica:1', '{"value": "here"}')
- doc2 = self.make_document('doc-here2', 'replica:1',
- '{"value": "here2"}')
- # We do not expect an exception here because the sync fails gracefully
- remote_target.sync_exchange(
- [(doc1, 10, 'T-sid'), (doc2, 11, 'T-sud')],
- 'replica', last_known_generation=0, last_known_trans_id=None,
- return_doc_cb=receive_doc)
- self.assertGetEncryptedDoc(
- db, 'doc-here', 'replica:1', '{"value": "here"}',
- False)
- self.assertEqual(
- (10, 'T-sid'), db._get_replica_gen_and_trans_id('replica'))
- self.assertEqual([], other_changes)
- # retry
- trigger_ids = []
- new_gen, trans_id = remote_target.sync_exchange(
- [(doc2, 11, 'T-sud')], 'replica', last_known_generation=0,
- last_known_trans_id=None, return_doc_cb=receive_doc)
- self.assertGetEncryptedDoc(
- db, 'doc-here2', 'replica:1', '{"value": "here2"}',
- False)
- self.assertEqual(
- (11, 'T-sud'), db._get_replica_gen_and_trans_id('replica'))
- self.assertEqual(2, new_gen)
- self.assertEqual(
- ('doc-here', 'replica:1', '{"value": "here"}', 1),
- other_changes[0][:-1])
- db.close()
-
- def test_sync_exchange_send_ensure_callback(self):
- """
- Test for sync exchange failure and retry.
-
- This test was adapted to decrypt remote content before assert.
- """
- self.startServer()
- remote_target = self.getSyncTarget('test')
- other_docs = []
- replica_uid_box = []
-
- def receive_doc(doc, gen, trans_id):
- other_docs.append((doc.doc_id, doc.rev, doc.get_json()))
-
- def ensure_cb(replica_uid):
- replica_uid_box.append(replica_uid)
-
- doc = self.make_document('doc-here', 'replica:1', '{"value": "here"}')
- new_gen, trans_id = remote_target.sync_exchange(
- [(doc, 10, 'T-sid')], 'replica', last_known_generation=0,
- last_known_trans_id=None, return_doc_cb=receive_doc,
- ensure_callback=ensure_cb)
- self.assertEqual(1, new_gen)
- db = self.request_state.open_database('test')
- self.assertEqual(1, len(replica_uid_box))
- self.assertEqual(db._replica_uid, replica_uid_box[0])
- self.assertGetEncryptedDoc(
- db, 'doc-here', 'replica:1', '{"value": "here"}', False)
- db.close()
-
- 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`.
-#-----------------------------------------------------------------------------
-
-def token_leap_https_sync_target(test, host, path):
- _, port = test.server.server_address
- st = target.SoledadSyncTarget(
- 'https://%s:%d/%s' % (host, port, path),
- crypto=test._soledad._crypto)
- st.set_token_credentials('user-uuid', 'auth-token')
- return st
-
-
-class TestSoledadSyncTargetHttpsSupport(
- test_https.TestHttpSyncTargetHttpsSupport,
- BaseSoledadTest):
-
- scenarios = [
- ('token_soledad_https',
- {'server_def': test_https.https_server_def,
- 'make_app_with_state': make_token_soledad_app,
- 'make_document_for_test': make_leap_document_for_test,
- 'sync_target': token_leap_https_sync_target}),
- ]
-
- def setUp(self):
- # the parent constructor undoes our SSL monkey patch to ensure tests
- # run smoothly with standard u1db.
- test_https.TestHttpSyncTargetHttpsSupport.setUp(self)
- # so here monkey patch again to test our functionality.
- http_client._VerifiedHTTPSConnection = VerifiedHTTPSConnection
- client.SOLEDAD_CERT = http_client.CA_CERTS
-
- def test_working(self):
- """
- Test that SSL connections work well.
-
- This test was adapted to patch Soledad's HTTPS connection custom class
- with the intended CA certificates.
- """
- self.startServer()
- db = self.request_state._create_database('test')
- self.patch(client, 'SOLEDAD_CERT', self.cacert_pem)
- remote_target = self.getSyncTarget('localhost', 'test')
- remote_target.record_sync_info('other-id', 2, 'T-id')
- self.assertEqual(
- (2, 'T-id'), db._get_replica_gen_and_trans_id('other-id'))
-
- def test_host_mismatch(self):
- """
- Test that SSL connections to a hostname different than the one in the
- certificate raise CertificateError.
-
- This test was adapted to patch Soledad's HTTPS connection custom class
- with the intended CA certificates.
- """
- self.startServer()
- self.request_state._create_database('test')
- self.patch(client, 'SOLEDAD_CERT', self.cacert_pem)
- remote_target = self.getSyncTarget('127.0.0.1', 'test')
- self.assertRaises(
- http_client.CertificateError, remote_target.record_sync_info,
- 'other-id', 2, 'T-id')
-
-
-#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_http_database`.
-#-----------------------------------------------------------------------------
-
-class _HTTPDatabase(http_database.HTTPDatabase, auth.TokenBasedAuth):
- """
- Wraps our token auth implementation.
- """
-
- def set_token_credentials(self, uuid, token):
- auth.TokenBasedAuth.set_token_credentials(self, uuid, token)
-
- def _sign_request(self, method, url_query, params):
- return auth.TokenBasedAuth._sign_request(
- self, method, url_query, params)
-
-
-class TestHTTPDatabaseWithCreds(
- test_http_database.TestHTTPDatabaseCtrWithCreds):
-
- def test_get_sync_target_inherits_token_credentials(self):
- # this test was from TestDatabaseSimpleOperations but we put it here
- # for convenience.
- self.db = _HTTPDatabase('dbase')
- self.db.set_token_credentials('user-uuid', 'auth-token')
- st = self.db.get_sync_target()
- self.assertEqual(self.db._creds, st._creds)
-
- def test_ctr_with_creds(self):
- db1 = _HTTPDatabase('http://dbs/db', creds={'token': {
- 'uuid': 'user-uuid',
- 'token': 'auth-token',
- }})
- self.assertIn('token', db1._creds)
-
-
-#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_sync`.
-#-----------------------------------------------------------------------------
-
-target_scenarios = [
- ('token_leap', {'create_db_and_target':
- make_local_db_and_token_soledad_target,
- 'make_app_with_state': make_soledad_app}),
-]
-
-
-class SoledadDatabaseSyncTargetTests(
- 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_soledad_target(self)
- docs_by_gen = [
- (self.make_document('doc-id', 'replica:1', tests.simple_doc), 10,
- 'T-sid')]
- new_gen, trans_id = self.st.sync_exchange(
- docs_by_gen, 'replica', last_known_generation=0,
- last_known_trans_id=None, return_doc_cb=self.receive_doc)
- self.assertGetEncryptedDoc(
- self.db, 'doc-id', 'replica:1', tests.simple_doc, False)
- self.assertTransactionLog(['doc-id'], self.db)
- last_trans_id = self.getLastTransId(self.db)
- self.assertEqual(([], 1, last_trans_id),
- (self.other_changes, new_gen, last_trans_id))
- self.assertEqual(10, self.st.get_sync_info('replica')[3])
- sol.close()
-
- def test_sync_exchange_push_many(self):
- """
- Test sync exchange.
-
- This test was adapted to decrypt remote content before assert.
- """
- docs_by_gen = [
- (self.make_document(
- 'doc-id', 'replica:1', tests.simple_doc), 10, 'T-1'),
- (self.make_document(
- 'doc-id2', 'replica:1', tests.nested_doc), 11, 'T-2')]
- new_gen, trans_id = self.st.sync_exchange(
- docs_by_gen, 'replica', last_known_generation=0,
- last_known_trans_id=None, return_doc_cb=self.receive_doc)
- self.assertGetEncryptedDoc(
- self.db, 'doc-id', 'replica:1', tests.simple_doc, False)
- self.assertGetEncryptedDoc(
- self.db, 'doc-id2', 'replica:1', tests.nested_doc, False)
- self.assertTransactionLog(['doc-id', 'doc-id2'], self.db)
- last_trans_id = self.getLastTransId(self.db)
- self.assertEqual(([], 2, last_trans_id),
- (self.other_changes, new_gen, trans_id))
- self.assertEqual(11, self.st.get_sync_info('replica')[3])
-
- def test_sync_exchange_returns_many_new_docs(self):
- """
- Test sync exchange.
-
- This test was adapted to avoid JSON serialization comparison as local
- and remote representations might differ. It looks directly at the
- doc's contents instead.
- """
- doc = self.db.create_doc_from_json(tests.simple_doc)
- doc2 = self.db.create_doc_from_json(tests.nested_doc)
- self.assertTransactionLog([doc.doc_id, doc2.doc_id], self.db)
- new_gen, _ = self.st.sync_exchange(
- [], 'other-replica', last_known_generation=0,
- last_known_trans_id=None, return_doc_cb=self.receive_doc)
- self.assertTransactionLog([doc.doc_id, doc2.doc_id], self.db)
- self.assertEqual(2, new_gen)
- self.assertEqual(
- [(doc.doc_id, doc.rev, 1),
- (doc2.doc_id, doc2.rev, 2)],
- [c[:-3] + c[-2:-1] for c in self.other_changes])
- self.assertEqual(
- json.loads(tests.simple_doc),
- json.loads(self.other_changes[0][2]))
- self.assertEqual(
- json.loads(tests.nested_doc),
- json.loads(self.other_changes[1][2]))
- if self.whitebox:
- self.assertEqual(
- self.db._last_exchange_log['return'],
- {'last_gen': 2, 'docs':
- [(doc.doc_id, doc.rev), (doc2.doc_id, doc2.rev)]})
-
-
-class TestSoledadDbSync(
- SoledadWithCouchServerMixin, test_sync.TestDbSync):
- """Test db.sync remote sync shortcut"""
-
- scenarios = [
- ('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': make_sqlcipher_database_for_test,
- 'token': True
- }),
- ]
-
- oauth = False
- token = False
-
- def setUp(self):
- self.main_test_class = test_sync.TestDbSync
- SoledadWithCouchServerMixin.setUp(self)
-
- def tearDown(self):
- SoledadWithCouchServerMixin.tearDown(self)
- self.db.close()
-
- def do_sync(self, target_name):
- """
- Perform sync using SoledadSyncTarget and Token auth.
- """
- if self.token:
- extra = dict(creds={'token': {
- 'uuid': 'user-uuid',
- 'token': 'auth-token',
- }})
- target_url = self.getURL(target_name)
- return Synchronizer(
- self.db,
- target.SoledadSyncTarget(
- target_url,
- crypto=self._soledad._crypto,
- **extra)).sync(autocreate=True)
- else:
- return test_sync.TestDbSync.do_sync(self, target_name)
-
- def test_db_sync(self):
- """
- Test sync.
-
- Adapted to check for encrypted content.
- """
- 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')
- gen, _, changes = self.db.whats_changed(local_gen_before_sync)
- self.assertEqual(1, len(changes))
- self.assertEqual(doc2.doc_id, changes[0][0])
- self.assertEqual(1, gen - local_gen_before_sync)
- self.assertGetEncryptedDoc(
- self.db2, doc1.doc_id, doc1.rev, tests.simple_doc, False)
- self.assertGetEncryptedDoc(
- self.db, doc2.doc_id, doc2.rev, tests.nested_doc, False)
-
- def test_db_sync_autocreate(self):
- """
- We bypass this test because we never need to autocreate databases.
- """
- pass
-
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/test_target_soledad.py b/common/src/leap/soledad/common/tests/test_target_soledad.py
deleted file mode 100644
index 899203b8..00000000
--- a/common/src/leap/soledad/common/tests/test_target_soledad.py
+++ /dev/null
@@ -1,102 +0,0 @@
-from u1db.remote import (
- http_database,
-)
-
-from leap.soledad.client import (
- auth,
- VerifiedHTTPSConnection,
-)
-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.u1db_tests import test_backends
-
-
-#-----------------------------------------------------------------------------
-# The following tests come from `u1db.tests.test_backends`.
-#-----------------------------------------------------------------------------
-
-def make_leap_document_for_test(test, doc_id, rev, content,
- has_conflicts=False):
- return SoledadDocument(
- 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,
- 'copy_database_for_test': test_backends.copy_http_database_for_test,
- 'make_document_for_test': make_leap_document_for_test,
- 'make_app_with_state': make_soledad_app}),
-]
-
-
-def make_token_http_database_for_test(test, replica_uid):
- test.startServer()
- test.request_state._create_database(replica_uid)
-
- class _HTTPDatabaseWithToken(
- http_database.HTTPDatabase, auth.TokenBasedAuth):
-
- def set_token_credentials(self, uuid, token):
- auth.TokenBasedAuth.set_token_credentials(self, uuid, token)
-
- def _sign_request(self, method, url_query, params):
- return auth.TokenBasedAuth._sign_request(
- self, method, url_query, params)
-
- http_db = _HTTPDatabaseWithToken(test.getURL('test'))
- http_db.set_token_credentials('user-uuid', 'auth-token')
- return http_db
-
-
-def copy_token_http_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.
- http_db = test.request_state._copy_database(db)
- http_db.set_token_credentials(http_db, 'user-uuid', 'auth-token')
- return http_db
-
-
-class SoledadTests(test_backends.AllDatabaseTests, BaseSoledadTest):
-
- scenarios = LEAP_SCENARIOS + [
- ('token_http', {'make_database_for_test':
- make_token_http_database_for_test,
- 'copy_database_for_test':
- copy_token_http_database_for_test,
- 'make_document_for_test': make_leap_document_for_test,
- 'make_app_with_state': make_token_soledad_app,
- })
- ]
-
-load_tests = tests.load_with_scenarios
diff --git a/common/src/leap/soledad/common/tests/u1db_tests/__init__.py b/common/src/leap/soledad/common/tests/u1db_tests/__init__.py
index ad66fb06..6efeb87f 100644
--- a/common/src/leap/soledad/common/tests/u1db_tests/__init__.py
+++ b/common/src/leap/soledad/common/tests/u1db_tests/__init__.py
@@ -35,7 +35,7 @@ from pysqlcipher import dbapi2
from StringIO import StringIO
import testscenarios
-import testtools
+from twisted.trial import unittest
from u1db import (
errors,
@@ -50,7 +50,7 @@ from u1db.remote import (
)
-class TestCase(testtools.TestCase):
+class TestCase(unittest.TestCase):
def createTempDir(self, prefix='u1db-tmp-'):
"""Create a temporary directory to do some work in.
diff --git a/common/src/leap/soledad/common/tests/u1db_tests/test_https.py b/common/src/leap/soledad/common/tests/u1db_tests/test_https.py
index c086fbc0..c5b316ed 100644
--- a/common/src/leap/soledad/common/tests/u1db_tests/test_https.py
+++ b/common/src/leap/soledad/common/tests/u1db_tests/test_https.py
@@ -75,7 +75,7 @@ class TestHttpSyncTargetHttpsSupport(tests.TestCaseWithServer):
# order to maintain the compatibility with u1db default tests, we undo
# that replacement here.
http_client._VerifiedHTTPSConnection = \
- soledad.client.old__VerifiedHTTPSConnection
+ soledad.client.api.old__VerifiedHTTPSConnection
super(TestHttpSyncTargetHttpsSupport, self).setUp()
def getSyncTarget(self, host, path=None):
diff --git a/common/src/leap/soledad/common/tests/util.py b/common/src/leap/soledad/common/tests/util.py
index b27e6356..d4439ef4 100644
--- a/common/src/leap/soledad/common/tests/util.py
+++ b/common/src/leap/soledad/common/tests/util.py
@@ -21,24 +21,40 @@ Utilities used by multiple test suites.
"""
+import os
import tempfile
import shutil
+import random
+import string
+import u1db
+import subprocess
+import time
+import re
+
+from mock import Mock
from urlparse import urljoin
-
from StringIO import StringIO
from pysqlcipher import dbapi2
+
from u1db.errors import DatabaseDoesNotExist
+from u1db.remote import http_database
+
+from twisted.trial import unittest
+from leap.common.files import mkdir_p
from leap.soledad.common import soledad_assert
+from leap.soledad.common.document import SoledadDocument
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.crypto import ENC_SCHEME_KEY
+from leap.soledad.client import Soledad
+from leap.soledad.client import target
+from leap.soledad.client import auth
+from leap.soledad.client.crypto import decrypt_doc_dict
-from leap.soledad.common.tests import BaseSoledadTest
-from leap.soledad.common.tests.test_couch import CouchDBWrapper, CouchDBTestCase
-
+from leap.soledad.server import SoledadApp
+from leap.soledad.server.auth import SoledadTokenAuthMiddleware
from leap.soledad.client.sqlcipher import (
SQLCipherDatabase,
@@ -47,6 +63,7 @@ from leap.soledad.client.sqlcipher import (
PASSWORD = '123456'
+ADDRESS = 'leap@leap.se'
def make_sqlcipher_database_for_test(test, replica_uid):
@@ -62,7 +79,7 @@ def copy_sqlcipher_database_for_test(test, db):
# 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)
+ new_db = make_sqlcipher_database_for_test(test, None)
tmpfile = StringIO()
for line in db._db_handle.iterdump():
if not 'sqlite_sequence' in line: # work around bug in iterdump
@@ -98,6 +115,295 @@ def make_token_soledad_app(state):
return application
+def make_soledad_document_for_test(test, doc_id, rev, content,
+ has_conflicts=False):
+ return SoledadDocument(
+ doc_id, rev, content, has_conflicts=has_conflicts)
+
+
+def make_token_http_database_for_test(test, replica_uid):
+ test.startServer()
+ test.request_state._create_database(replica_uid)
+
+ class _HTTPDatabaseWithToken(
+ http_database.HTTPDatabase, auth.TokenBasedAuth):
+
+ def set_token_credentials(self, uuid, token):
+ auth.TokenBasedAuth.set_token_credentials(self, uuid, token)
+
+ def _sign_request(self, method, url_query, params):
+ return auth.TokenBasedAuth._sign_request(
+ self, method, url_query, params)
+
+ http_db = _HTTPDatabaseWithToken(test.getURL('test'))
+ http_db.set_token_credentials('user-uuid', 'auth-token')
+ return http_db
+
+
+def copy_token_http_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.
+ http_db = test.request_state._copy_database(db)
+ http_db.set_token_credentials(http_db, 'user-uuid', 'auth-token')
+ return http_db
+
+
+class MockedSharedDBTest(object):
+
+ def get_default_shared_mock(self, put_doc_side_effect=None,
+ get_doc_return_value=None):
+ """
+ Get a default class for mocking the shared DB
+ """
+ class defaultMockSharedDB(object):
+ get_doc = Mock(return_value=get_doc_return_value)
+ put_doc = Mock(side_effect=put_doc_side_effect)
+ lock = Mock(return_value=('atoken', 300))
+ unlock = Mock(return_value=True)
+ open = Mock(return_value=None)
+ syncable = True
+
+ def __call__(self):
+ return self
+ return defaultMockSharedDB
+
+
+def soledad_sync_target(test, path):
+ return target.SoledadSyncTarget(
+ test.getURL(path), crypto=test._soledad._crypto)
+
+
+def token_soledad_sync_target(test, path):
+ st = soledad_sync_target(test, path)
+ st.set_token_credentials('user-uuid', 'auth-token')
+ return st
+
+
+class BaseSoledadTest(unittest.TestCase, MockedSharedDBTest):
+ """
+ Instantiates Soledad for usage in tests.
+ """
+ defer_sync_encryption = False
+
+ def setUp(self):
+ # The following snippet comes from BaseLeapTest.setUpClass, but we
+ # repeat it here because twisted.trial does not work with
+ # setUpClass/tearDownClass.
+ self.old_path = os.environ['PATH']
+ self.old_home = os.environ['HOME']
+ self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")
+ self.home = self.tempdir
+ bin_tdir = os.path.join(
+ self.tempdir,
+ 'bin')
+ os.environ["PATH"] = bin_tdir
+ os.environ["HOME"] = self.tempdir
+
+ # config info
+ self.db1_file = os.path.join(self.tempdir, "db1.u1db")
+ self.db2_file = os.path.join(self.tempdir, "db2.u1db")
+ self.email = ADDRESS
+ # open test dbs
+ self._db1 = u1db.open(self.db1_file, create=True,
+ document_factory=SoledadDocument)
+ self._db2 = u1db.open(self.db2_file, create=True,
+ document_factory=SoledadDocument)
+ # get a random prefix for each test, so we do not mess with
+ # concurrency during initialization and shutting down of
+ # each local db.
+ self.rand_prefix = ''.join(
+ map(lambda x: random.choice(string.ascii_letters), range(6)))
+ # initialize soledad by hand so we can control keys
+ # XXX check if this soledad is actually used
+ self._soledad = self._soledad_instance(
+ prefix=self.rand_prefix, user=self.email)
+
+ def tearDown(self):
+ self._db1.close()
+ self._db2.close()
+ self._soledad.close()
+
+ # restore paths
+ os.environ["PATH"] = self.old_path
+ os.environ["HOME"] = self.old_home
+
+ def _delete_temporary_dirs():
+ # XXX should not access "private" attrs
+ for f in [self._soledad.local_db_path,
+ self._soledad.secrets.secrets_path]:
+ if os.path.isfile(f):
+ os.unlink(f)
+ # The following snippet comes from BaseLeapTest.setUpClass, but we
+ # repeat it here because twisted.trial does not work with
+ # setUpClass/tearDownClass.
+ soledad_assert(
+ self.tempdir.startswith('/tmp/leap_tests-'),
+ "beware! tried to remove a dir which does not "
+ "live in temporal folder!")
+ shutil.rmtree(self.tempdir)
+
+ from twisted.internet import reactor
+ reactor.addSystemEventTrigger(
+ "after", "shutdown", _delete_temporary_dirs)
+
+
+ def _soledad_instance(self, user=ADDRESS, passphrase=u'123',
+ prefix='',
+ secrets_path='secrets.json',
+ local_db_path='soledad.u1db',
+ server_url='https://127.0.0.1/',
+ cert_file=None,
+ shared_db_class=None,
+ auth_token='auth-token'):
+
+ def _put_doc_side_effect(doc):
+ self._doc_put = doc
+
+ if shared_db_class is not None:
+ MockSharedDB = shared_db_class
+ else:
+ MockSharedDB = self.get_default_shared_mock(
+ _put_doc_side_effect)
+
+ return Soledad(
+ user,
+ passphrase,
+ secrets_path=os.path.join(
+ self.tempdir, prefix, secrets_path),
+ local_db_path=os.path.join(
+ self.tempdir, prefix, local_db_path),
+ server_url=server_url, # Soledad will fail if not given an url.
+ cert_file=cert_file,
+ defer_encryption=self.defer_sync_encryption,
+ shared_db=MockSharedDB(),
+ auth_token=auth_token)
+
+ def assertGetEncryptedDoc(
+ self, db, doc_id, doc_rev, content, has_conflicts):
+ """
+ Assert that the document in the database looks correct.
+ """
+ exp_doc = self.make_document(doc_id, doc_rev, content,
+ has_conflicts=has_conflicts)
+ doc = db.get_doc(doc_id)
+
+ if ENC_SCHEME_KEY in doc.content:
+ # XXX check for SYM_KEY too
+ key = self._soledad._crypto.doc_passphrase(doc.doc_id)
+ secret = self._soledad._crypto.secret
+ decrypted = decrypt_doc_dict(
+ doc.content, doc.doc_id, doc.rev,
+ key, secret)
+ doc.set_json(decrypted)
+ self.assertEqual(exp_doc.doc_id, doc.doc_id)
+ self.assertEqual(exp_doc.rev, doc.rev)
+ self.assertEqual(exp_doc.has_conflicts, doc.has_conflicts)
+ self.assertEqual(exp_doc.content, doc.content)
+
+
+#-----------------------------------------------------------------------------
+# A wrapper for running couchdb locally.
+#-----------------------------------------------------------------------------
+
+# from: https://github.com/smcq/paisley/blob/master/paisley/test/util.py
+# TODO: include license of above project.
+class CouchDBWrapper(object):
+ """
+ Wrapper for external CouchDB instance which is started and stopped for
+ testing.
+ """
+
+ def start(self):
+ """
+ Start a CouchDB instance for a test.
+ """
+ self.tempdir = tempfile.mkdtemp(suffix='.couch.test')
+
+ path = os.path.join(os.path.dirname(__file__),
+ 'couchdb.ini.template')
+ handle = open(path)
+ conf = handle.read() % {
+ 'tempdir': self.tempdir,
+ }
+ handle.close()
+
+ confPath = os.path.join(self.tempdir, 'test.ini')
+ handle = open(confPath, 'w')
+ handle.write(conf)
+ handle.close()
+
+ # create the dirs from the template
+ mkdir_p(os.path.join(self.tempdir, 'lib'))
+ mkdir_p(os.path.join(self.tempdir, 'log'))
+ args = ['/usr/bin/couchdb', '-n', '-a', confPath]
+ null = open('/dev/null', 'w')
+
+ self.process = subprocess.Popen(
+ args, env=None, stdout=null.fileno(), stderr=null.fileno(),
+ close_fds=True)
+ # find port
+ logPath = os.path.join(self.tempdir, 'log', 'couch.log')
+ while not os.path.exists(logPath):
+ if self.process.poll() is not None:
+ got_stdout, got_stderr = "", ""
+ if self.process.stdout is not None:
+ got_stdout = self.process.stdout.read()
+
+ if self.process.stderr is not None:
+ got_stderr = self.process.stderr.read()
+ raise Exception("""
+couchdb exited with code %d.
+stdout:
+%s
+stderr:
+%s""" % (
+ self.process.returncode, got_stdout, got_stderr))
+ time.sleep(0.01)
+ while os.stat(logPath).st_size == 0:
+ time.sleep(0.01)
+ PORT_RE = re.compile(
+ 'Apache CouchDB has started on http://127.0.0.1:(?P<port>\d+)')
+
+ handle = open(logPath)
+ line = handle.read()
+ handle.close()
+ m = PORT_RE.search(line)
+ if not m:
+ self.stop()
+ raise Exception("Cannot find port in line %s" % line)
+ self.port = int(m.group('port'))
+
+ def stop(self):
+ """
+ Terminate the CouchDB instance.
+ """
+ self.process.terminate()
+ self.process.communicate()
+ shutil.rmtree(self.tempdir)
+
+
+class CouchDBTestCase(unittest.TestCase, MockedSharedDBTest):
+ """
+ TestCase base class for tests against a real CouchDB server.
+ """
+
+ def setUp(self):
+ """
+ Make sure we have a CouchDB instance for a test.
+ """
+ self.wrapper = CouchDBWrapper()
+ self.wrapper.start()
+ #self.db = self.wrapper.db
+
+ def tearDown(self):
+ """
+ Stop CouchDB instance for test.
+ """
+ self.wrapper.stop()
+
class CouchServerStateForTests(CouchServerState):
"""
This is a slightly modified CouchDB server state that allows for creating
@@ -126,43 +432,15 @@ 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)
+ BaseSoledadTest.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)
@@ -172,6 +450,8 @@ class SoledadWithCouchServerMixin(
db.delete_database()
except DatabaseDoesNotExist:
pass
+ BaseSoledadTest.tearDown(self)
+ CouchDBTestCase.tearDown(self)
def make_app(self):
couch_url = urljoin(