More tests

This commit is contained in:
Markus Unterwaditzer 2017-01-19 22:06:33 +01:00
parent e7910e92aa
commit 41794f095d
3 changed files with 80 additions and 21 deletions

View file

@ -7,7 +7,8 @@ import pytest
from tests import blow_up
from vdirsyncer.metasync import MetaSyncConflict, metasync
from vdirsyncer.exceptions import UserError
from vdirsyncer.metasync import MetaSyncConflict, logger, metasync
from vdirsyncer.storage.base import normalize_meta_value
from vdirsyncer.storage.memory import MemoryStorage
@ -46,19 +47,50 @@ def test_basic(monkeypatch):
assert not a.get_meta('foo') and not b.get_meta('foo')
def test_conflict():
@pytest.fixture
def conflict_state(request):
a = MemoryStorage()
b = MemoryStorage()
status = {}
a.set_meta('foo', 'bar')
b.set_meta('foo', 'baz')
def cleanup():
assert a.get_meta('foo') == 'bar'
assert b.get_meta('foo') == 'baz'
assert not status
request.addfinalizer(cleanup)
return a, b, status
def test_conflict(conflict_state):
a, b, status = conflict_state
with pytest.raises(MetaSyncConflict):
metasync(a, b, status, keys=['foo'])
assert a.get_meta('foo') == 'bar'
assert b.get_meta('foo') == 'baz'
assert not status
def test_invalid_conflict_resolution(conflict_state):
a, b, status = conflict_state
with pytest.raises(UserError) as excinfo:
metasync(a, b, status, keys=['foo'], conflict_resolution='foo')
assert 'Invalid conflict resolution setting' in str(excinfo.value)
def test_warning_on_custom_conflict_commands(conflict_state, monkeypatch):
a, b, status = conflict_state
warnings = []
monkeypatch.setattr(logger, 'warning', warnings.append)
with pytest.raises(MetaSyncConflict):
metasync(a, b, status, keys=['foo'],
conflict_resolution=lambda *a, **kw: None)
assert warnings == ['Custom commands don\'t work on metasync.']
def test_conflict_same_content():

View file

@ -80,6 +80,18 @@ def test_read_only_and_prefetch():
assert not items(a) and not items(b)
def test_partial_sync_error():
a = MemoryStorage()
b = MemoryStorage()
status = {}
a.upload(Item('UID:0'))
b.read_only = True
with pytest.raises(PartialSync):
sync(a, b, status, partial_sync='error')
def test_partial_sync_ignore():
a = MemoryStorage()
b = MemoryStorage()
@ -357,20 +369,37 @@ def test_both_readonly():
sync(a, b, status)
def test_readonly():
def test_partial_sync_revert():
a = MemoryStorage(instance_name='a')
b = MemoryStorage(instance_name='b')
status = {}
href_a, _ = a.upload(Item(u'UID:1'))
href_b, _ = b.upload(Item(u'UID:2'))
a.upload(Item(u'UID:1'))
b.upload(Item(u'UID:2'))
b.read_only = True
with pytest.raises(exceptions.ReadOnlyError):
b.upload(Item(u'UID:3'))
sync(a, b, status, partial_sync='revert')
assert len(status) == 2 and a.has(href_a) and not b.has(href_a)
assert len(status) == 2
assert items(a) == {'UID:1', 'UID:2'}
assert items(b) == {'UID:2'}
sync(a, b, status, partial_sync='revert')
assert len(status) == 1 and not a.has(href_a) and not b.has(href_a)
assert len(status) == 1
assert items(a) == {'UID:2'}
assert items(b) == {'UID:2'}
# Check that updates get reverted
a.items[next(iter(a.items))] = ('foo', Item('UID:2\nupdated'))
assert items(a) == {'UID:2\nupdated'}
sync(a, b, status, partial_sync='revert')
assert items(a) == {'UID:2\nupdated'}
sync(a, b, status, partial_sync='revert')
assert items(a) == {'UID:2'}
# Check that deletions get reverted
a.items.clear()
sync(a, b, status, partial_sync='revert', force_delete=True)
sync(a, b, status, partial_sync='revert', force_delete=True)
assert items(a) == {'UID:2'}
@pytest.mark.parametrize('sync_inbetween', (True, False))
@ -472,12 +501,10 @@ class SyncMachine(RuleBasedStateMachine):
Storage = Bundle('storage')
@rule(target=Storage,
read_only=st.booleans(),
flaky_etags=st.booleans(),
null_etag_on_upload=st.booleans())
def newstorage(self, read_only, flaky_etags, null_etag_on_upload):
def newstorage(self, flaky_etags, null_etag_on_upload):
s = MemoryStorage()
s.read_only = read_only
if flaky_etags:
def get(href):
old_etag, item = s.items[href]
@ -494,6 +521,11 @@ class SyncMachine(RuleBasedStateMachine):
return s
@rule(s=Storage, read_only=st.booleans())
def is_read_only(self, s, read_only):
assume(s.read_only != read_only)
s.read_only = read_only
@rule(s=Storage)
def actions_fail(self, s):
s.upload = action_failure

View file

@ -197,10 +197,6 @@ def _migrate_status(status):
'href': href_b,
'etag': etag_b,
})
elif len(value) == 2:
a, b = value
a.setdefault('hash', '')
b.setdefault('hash', '')
def sync(storage_a, storage_b, status, conflict_resolution=None,
@ -283,7 +279,7 @@ def sync(storage_a, storage_b, status, conflict_resolution=None,
class Action:
def _run_impl(self, a, b):
def _run_impl(self, a, b): # pragma: no cover
raise NotImplementedError()
def run(self, a, b, conflict_resolution, partial_sync):
@ -345,7 +341,6 @@ class Update(Action):
self.dest = dest
def _run_impl(self, a, b):
if self.dest.storage.read_only:
meta = _ItemMetadata(item=self.item)
else: