diff --git a/conservancy_beancount/config.py b/conservancy_beancount/config.py index d9ad995..fded3c8 100644 --- a/conservancy_beancount/config.py +++ b/conservancy_beancount/config.py @@ -68,6 +68,24 @@ class RTCredentials(NamedTuple): class Config: + def _dir_or_none(self, path: Path) -> Optional[Path]: + try: + path.mkdir(exist_ok=True) + except OSError: + return None + else: + return path + + def cache_dir_path(self, name: str='conservancy_beancount') -> Optional[Path]: + try: + cache_root = Path(os.environ['XDG_CACHE_DIR']) + except (KeyError, ValueError): + cache_root = Path.home() / '.cache' + return ( + self._dir_or_none(cache_root) + and self._dir_or_none(cache_root / name) + ) + def repository_path(self) -> Optional[Path]: try: return Path(os.environ['CONSERVANCY_REPOSITORY']) diff --git a/tests/conftest.py b/tests/conftest.py index f6a1a3b..ca2db17 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,10 +5,11 @@ import pytest from . import testutil @pytest.fixture(scope='session', autouse=True) -def clean_environment(): +def clean_environment(tmpdir_factory): os.environ.pop('RTAUTH', None) os.environ.pop('RTPASSWD', None) os.environ.pop('RTSERVER', None) os.environ.pop('RTUSER', None) os.environ['CONSERVANCY_REPOSITORY'] = str(testutil.test_path('repository')) os.environ['HOME'] = str(testutil.test_path('userconfig')) + os.environ['XDG_CACHE_DIR'] = str(tmpdir_factory.mktemp('.cache')) diff --git a/tests/test_config.py b/tests/test_config.py index fbc0445..e503b8e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -206,3 +206,51 @@ def test_rt_wrapper_cache_responds_to_external_credential_changes(rt_environ): with update_environ(**rt_environ): rt2 = config.rt_wrapper(None, testutil.RTClient) assert rt1 is not rt2 + +def test_cache_mkdir(tmp_path): + expected = tmp_path / 'TESTcache' + with update_environ(XDG_CACHE_DIR=tmp_path): + config = config_mod.Config() + cache_path = config.cache_dir_path(expected.name) + assert cache_path == tmp_path / 'TESTcache' + assert cache_path.is_dir() + +def test_cache_mkdir_parent(tmp_path): + xdg_cache_dir = tmp_path / 'xdgcache' + expected = xdg_cache_dir / 'conservancy_beancount' + with update_environ(XDG_CACHE_DIR=xdg_cache_dir): + config = config_mod.Config() + cache_path = config.cache_dir_path(expected.name) + assert cache_path == expected + assert cache_path.is_dir() + +def test_cache_mkdir_from_home(tmp_path): + expected = tmp_path / '.cache' / 'TESTcache' + with update_environ(HOME=tmp_path, XDG_CACHE_DIR=None): + config = config_mod.Config() + cache_path = config.cache_dir_path(expected.name) + assert cache_path == expected + assert cache_path.is_dir() + +def test_cache_mkdir_exists_ok(tmp_path): + expected = tmp_path / 'TESTcache' + expected.mkdir() + with update_environ(XDG_CACHE_DIR=tmp_path): + config = config_mod.Config() + cache_path = config.cache_dir_path(expected.name) + assert cache_path == expected + +def test_cache_path_conflict(tmp_path): + extant_path = tmp_path / 'TESTcache' + extant_path.touch() + with update_environ(XDG_CACHE_DIR=tmp_path): + config = config_mod.Config() + cache_path = config.cache_dir_path(extant_path.name) + assert cache_path is None + assert extant_path.is_file() + +def test_cache_path_parent_conflict(tmp_path): + (tmp_path / '.cache').touch() + with update_environ(HOME=tmp_path, XDG_CACHE_DIR=None): + config = config_mod.Config() + assert config.cache_dir_path('TESTcache') is None