from shutil import rmtree from tempfile import gettempdir import pytest from platformdirs import user_cache_dir from requests_cache.backends import FileCache, FileDict from requests_cache.serializers import ( SERIALIZERS, SerializerPipeline, Stage, json_serializer, safe_pickle_serializer, yaml_serializer, ) from tests.conftest import HTTPBIN_FORMATS, HTTPBIN_METHODS from tests.integration.base_cache_test import BaseCacheTest from tests.integration.base_storage_test import CACHE_NAME, BaseStorageTest # Handle optional dependencies if they're not installed, # so any skipped tests will explicitly be shown in pytest output TEST_SERIALIZERS = SERIALIZERS.copy() try: TEST_SERIALIZERS['safe_pickle'] = safe_pickle_serializer(secret_key='hunter2') except ImportError: TEST_SERIALIZERS['safe_pickle'] = 'safe_pickle_placeholder' def _valid_serializer(serializer) -> bool: return isinstance(serializer, (SerializerPipeline, Stage)) class TestFileDict(BaseStorageTest): storage_class = FileDict picklable = True @classmethod def teardown_class(cls): rmtree(CACHE_NAME, ignore_errors=True) def init_cache(self, index=0, clear=True, **kwargs): cache = FileDict(f'{CACHE_NAME}_{index}', use_temp=True, **kwargs) if clear: cache.clear() return cache def test_use_cache_dir(self): relative_path = FileDict(CACHE_NAME).cache_dir cache_dir_path = FileDict(CACHE_NAME, use_cache_dir=True).cache_dir assert not str(relative_path).startswith(user_cache_dir()) assert str(cache_dir_path).startswith(user_cache_dir()) def test_use_temp(self): relative_path = FileDict(CACHE_NAME).cache_dir temp_path = FileDict(CACHE_NAME, use_temp=True).cache_dir assert not str(relative_path).startswith(gettempdir()) assert str(temp_path).startswith(gettempdir()) def test_custom_extension(self): cache = self.init_cache(extension='dat') cache['key'] = 'value' assert cache._path('key').suffix == '.dat' class TestFileCache(BaseCacheTest): backend_class = FileCache init_kwargs = {'use_temp': True} @pytest.mark.parametrize('serializer', TEST_SERIALIZERS.values()) @pytest.mark.parametrize('method', HTTPBIN_METHODS) @pytest.mark.parametrize('field', ['params', 'data', 'json']) def test_all_methods(self, field, method, serializer): """Test all relevant combinations of methods X data fields X serializers""" if not _valid_serializer(serializer): pytest.skip(f'Dependencies not installed for {serializer}') super().test_all_methods(field, method, serializer) @pytest.mark.parametrize('serializer', TEST_SERIALIZERS.values()) @pytest.mark.parametrize('response_format', HTTPBIN_FORMATS) def test_all_response_formats(self, response_format, serializer): """Test all relevant combinations of response formats X serializers""" if not _valid_serializer(serializer): pytest.skip(f'Dependencies not installed for {serializer}') serializer.set_decode_content(False) super().test_all_response_formats(response_format, serializer) @pytest.mark.parametrize('serializer', [json_serializer, yaml_serializer]) @pytest.mark.parametrize('response_format', HTTPBIN_FORMATS) def test_all_response_formats__no_decode_content(self, response_format, serializer): """Test with decode_content=True for text-based serialization formats""" if not _valid_serializer(serializer): pytest.skip(f'Dependencies not installed for {serializer}') serializer.set_decode_content(True) self.test_all_response_formats(response_format, serializer) @pytest.mark.parametrize('serializer_name', SERIALIZERS.keys()) def test_paths(self, serializer_name): if not isinstance(SERIALIZERS[serializer_name], SerializerPipeline): pytest.skip(f'Dependencies not installed for {serializer_name}') session = self.init_session(serializer=serializer_name) num_files = 20 for i in range(num_files): session.cache.responses[f'key_{i}'] = {f'value_{i}': i} expected_extension = serializer_name.replace('pickle', 'pkl') assert len(list(session.cache.paths())) == num_files for path in session.cache.paths(): assert path.is_file() assert path.suffix == f'.{expected_extension}' # Redirects db should be in the same directory as response files assert session.cache.redirects.db_path.parent == session.cache.responses.cache_dir