from contextlib import contextmanager from unittest.mock import patch import hypothesis.strategies as st import pytest from hypothesis import given from vdirsyncer import exceptions from vdirsyncer.cli.fetchparams import STRATEGIES from vdirsyncer.cli.fetchparams import expand_fetch_params @pytest.fixture def mystrategy(monkeypatch): def strategy(x): calls.append(x) return x calls = [] monkeypatch.setitem(STRATEGIES, "mystrategy", strategy) return calls @contextmanager def dummy_strategy(): def strategy(x): calls.append(x) return x calls = [] with patch.dict(STRATEGIES, {"mystrategy": strategy}): yield calls @pytest.fixture def value_cache(monkeypatch): _cache = {} class FakeContext: fetched_params = _cache def find_object(self, _): return self def get_context(*a, **kw): return FakeContext() monkeypatch.setattr("click.get_current_context", get_context) return _cache def test_key_conflict(monkeypatch, mystrategy): with pytest.raises(ValueError) as excinfo: expand_fetch_params({"foo": "bar", "foo.fetch": ["mystrategy", "baz"]}) assert "Can't set foo.fetch and foo." in str(excinfo.value) @given(s=st.text(), t=st.text(min_size=1)) def test_fuzzing(s, t): with dummy_strategy(): config = expand_fetch_params({f"{s}.fetch": ["mystrategy", t]}) assert config[s] == t @pytest.mark.parametrize("value", [[], "lol", 42]) def test_invalid_fetch_value(mystrategy, value): with pytest.raises(ValueError) as excinfo: expand_fetch_params({"foo.fetch": value}) assert "Expected a list" in str( excinfo.value ) or "Expected list of length > 0" in str(excinfo.value) def test_unknown_strategy(): with pytest.raises(exceptions.UserError) as excinfo: expand_fetch_params({"foo.fetch": ["unreal", "asdf"]}) assert "Unknown strategy" in str(excinfo.value) def test_caching(monkeypatch, mystrategy, value_cache): orig_cfg = {"foo.fetch": ["mystrategy", "asdf"]} rv = expand_fetch_params(orig_cfg) assert rv["foo"] == "asdf" assert mystrategy == ["asdf"] assert len(value_cache) == 1 rv = expand_fetch_params(orig_cfg) assert rv["foo"] == "asdf" assert mystrategy == ["asdf"] assert len(value_cache) == 1 value_cache.clear() rv = expand_fetch_params(orig_cfg) assert rv["foo"] == "asdf" assert mystrategy == ["asdf"] * 2 assert len(value_cache) == 1 def test_failed_strategy(monkeypatch, value_cache): calls = [] def strategy(x): calls.append(x) raise KeyboardInterrupt monkeypatch.setitem(STRATEGIES, "mystrategy", strategy) orig_cfg = {"foo.fetch": ["mystrategy", "asdf"]} for _ in range(2): with pytest.raises(KeyboardInterrupt): expand_fetch_params(orig_cfg) assert len(value_cache) == 1 assert len(calls) == 1 def test_empty_value(monkeypatch, mystrategy): with pytest.raises(exceptions.UserError) as excinfo: expand_fetch_params({"foo.fetch": ["mystrategy", ""]}) assert "Empty value for foo.fetch, this most likely indicates an error" in str( excinfo.value )