From 41794f095d089c18d4b8c105aa92f6e2c4050c7a Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Thu, 19 Jan 2017 22:06:33 +0100 Subject: [PATCH] More tests --- tests/unit/test_metasync.py | 42 ++++++++++++++++++++++++++---- tests/unit/test_sync.py | 52 ++++++++++++++++++++++++++++++------- vdirsyncer/sync.py | 7 +---- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/tests/unit/test_metasync.py b/tests/unit/test_metasync.py index d36ed14..421394b 100644 --- a/tests/unit/test_metasync.py +++ b/tests/unit/test_metasync.py @@ -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(): diff --git a/tests/unit/test_sync.py b/tests/unit/test_sync.py index 1aab58f..61ed6f3 100644 --- a/tests/unit/test_sync.py +++ b/tests/unit/test_sync.py @@ -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 diff --git a/vdirsyncer/sync.py b/vdirsyncer/sync.py index 496417b..fac2ef2 100644 --- a/vdirsyncer/sync.py +++ b/vdirsyncer/sync.py @@ -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: