1from contextlib import contextmanager 2from unittest.mock import patch 3 4import hypothesis.strategies as st 5import pytest 6from hypothesis import given 7 8from vdirsyncer import exceptions 9from vdirsyncer.cli.fetchparams import expand_fetch_params 10from vdirsyncer.cli.fetchparams import STRATEGIES 11 12 13@pytest.fixture 14def mystrategy(monkeypatch): 15 def strategy(x): 16 calls.append(x) 17 return x 18 19 calls = [] 20 monkeypatch.setitem(STRATEGIES, "mystrategy", strategy) 21 return calls 22 23 24@contextmanager 25def dummy_strategy(): 26 def strategy(x): 27 calls.append(x) 28 return x 29 30 calls = [] 31 with patch.dict(STRATEGIES, {"mystrategy": strategy}): 32 yield calls 33 34 35@pytest.fixture 36def value_cache(monkeypatch): 37 _cache = {} 38 39 class FakeContext: 40 fetched_params = _cache 41 42 def find_object(self, _): 43 return self 44 45 def get_context(*a, **kw): 46 return FakeContext() 47 48 monkeypatch.setattr("click.get_current_context", get_context) 49 return _cache 50 51 52def test_key_conflict(monkeypatch, mystrategy): 53 with pytest.raises(ValueError) as excinfo: 54 expand_fetch_params({"foo": "bar", "foo.fetch": ["mystrategy", "baz"]}) 55 56 assert "Can't set foo.fetch and foo." in str(excinfo.value) 57 58 59@given(s=st.text(), t=st.text(min_size=1)) 60def test_fuzzing(s, t): 61 with dummy_strategy(): 62 config = expand_fetch_params({f"{s}.fetch": ["mystrategy", t]}) 63 64 assert config[s] == t 65 66 67@pytest.mark.parametrize("value", [[], "lol", 42]) 68def test_invalid_fetch_value(mystrategy, value): 69 with pytest.raises(ValueError) as excinfo: 70 expand_fetch_params({"foo.fetch": value}) 71 72 assert "Expected a list" in str( 73 excinfo.value 74 ) or "Expected list of length > 0" in str(excinfo.value) 75 76 77def test_unknown_strategy(): 78 with pytest.raises(exceptions.UserError) as excinfo: 79 expand_fetch_params({"foo.fetch": ["unreal", "asdf"]}) 80 81 assert "Unknown strategy" in str(excinfo.value) 82 83 84def test_caching(monkeypatch, mystrategy, value_cache): 85 orig_cfg = {"foo.fetch": ["mystrategy", "asdf"]} 86 87 rv = expand_fetch_params(orig_cfg) 88 assert rv["foo"] == "asdf" 89 assert mystrategy == ["asdf"] 90 assert len(value_cache) == 1 91 92 rv = expand_fetch_params(orig_cfg) 93 assert rv["foo"] == "asdf" 94 assert mystrategy == ["asdf"] 95 assert len(value_cache) == 1 96 97 value_cache.clear() 98 rv = expand_fetch_params(orig_cfg) 99 assert rv["foo"] == "asdf" 100 assert mystrategy == ["asdf"] * 2 101 assert len(value_cache) == 1 102 103 104def test_failed_strategy(monkeypatch, value_cache): 105 calls = [] 106 107 def strategy(x): 108 calls.append(x) 109 raise KeyboardInterrupt() 110 111 monkeypatch.setitem(STRATEGIES, "mystrategy", strategy) 112 113 orig_cfg = {"foo.fetch": ["mystrategy", "asdf"]} 114 115 for _ in range(2): 116 with pytest.raises(KeyboardInterrupt): 117 expand_fetch_params(orig_cfg) 118 119 assert len(value_cache) == 1 120 assert len(calls) == 1 121 122 123def test_empty_value(monkeypatch, mystrategy): 124 with pytest.raises(exceptions.UserError) as excinfo: 125 expand_fetch_params({"foo.fetch": ["mystrategy", ""]}) 126 127 assert "Empty value for foo.fetch, this most likely indicates an error" in str( 128 excinfo.value 129 ) 130