Skip to content

Commit

Permalink
Add some unit tests for CacheController and init_backend
Browse files Browse the repository at this point in the history
  • Loading branch information
JWCook committed Feb 11, 2021
1 parent 959af67 commit cf3f8ea
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 19 deletions.
22 changes: 12 additions & 10 deletions aiohttp_client_cache/backends/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@
ResponseOrKey,
)

BACKEND_QUALNAMES = {
'dynamodb': 'aiohttp_client_cache.backends.dynamodb.DynamoDbController',
'gridfs': 'aiohttp_client_cache.backends.gridfs.GridFSController',
'memory': 'aiohttp_client_cache.backends.base.CacheController',
'mongodb': 'aiohttp_client_cache.backends.mongo.MongoDBController',
'redis': 'aiohttp_client_cache.backends.redis.RedisController',
'sqlite': 'aiohttp_client_cache.backends.sqlite.SQLiteController',
}
logger = getLogger(__name__)


Expand All @@ -31,10 +23,20 @@ def import_member(qualname: str) -> Optional[Type]:


# Import all backends for which dependencies have been installed
BACKEND_QUALNAMES = {
'dynamodb': 'aiohttp_client_cache.backends.dynamodb.DynamoDbController',
'gridfs': 'aiohttp_client_cache.backends.gridfs.GridFSController',
'memory': 'aiohttp_client_cache.backends.base.CacheController',
'mongodb': 'aiohttp_client_cache.backends.mongo.MongoDBController',
'redis': 'aiohttp_client_cache.backends.redis.RedisController',
'sqlite': 'aiohttp_client_cache.backends.sqlite.SQLiteController',
}
BACKEND_CLASSES = {name: import_member(qualname) for name, qualname in BACKEND_QUALNAMES.items()}


def init_backend(backend: Optional[str], *args, **kwargs) -> CacheController:
def init_backend(
backend: Optional[str] = None, cache_name: str = 'http-cache', *args, **kwargs
) -> CacheController:
"""Initialize a backend by name; defaults to ``sqlite`` if installed, otherwise ``memory``"""
logger.info(f'Initializing backend: {backend}')
if isinstance(backend, CacheController):
Expand All @@ -50,4 +52,4 @@ def init_backend(backend: Optional[str], *args, **kwargs) -> CacheController:
raise ImportError(f'Dependencies not installed for backend {backend}')

logger.info(f'Found backend type: {backend_class}')
return backend_class(*args, **kwargs)
return backend_class(cache_name, *args, **kwargs)
16 changes: 8 additions & 8 deletions aiohttp_client_cache/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
logger = getLogger(__name__)


# TODO: Could/should this + init_backend() be merged into CachedSession?
# It would at least remove one layer of parameter passing.
class CacheController:
"""Class to manage higher-level cache operations.
Handles cache expiration, and generating cache keys, and managing redirect history.
Expand Down Expand Up @@ -206,7 +204,6 @@ def filter_ignored_params(d):


# TODO: Support yarl.URL like aiohttp does?
# TODO: Is there an existing ABC for async collections? Can't seem to find any.
class BaseCache(metaclass=ABCMeta):
"""A wrapper for the actual storage operations. This is separate from
:py:class:`.CacheController` to simplify writing to multiple tables/prefixes.
Expand Down Expand Up @@ -247,14 +244,17 @@ async def values(self) -> Iterable[ResponseOrKey]:
async def write(self, key: str, item: ResponseOrKey):
"""Write an item to the cache"""

async def pop(self, key: str) -> Optional[ResponseOrKey]:
async def pop(self, key: str, default=None) -> Optional[ResponseOrKey]:
"""Delete an item from the cache, and return the deleted item"""
item = await self.read(key)
await self.delete(key)
return item
try:
item = await self.read(key)
await self.delete(key)
return item
except KeyError:
return default


class DictCache(UserDict, BaseCache):
class DictCache(BaseCache, UserDict):
"""Simple in-memory storage that wraps a dict with the :py:class:`.BaseStorage` interface"""

async def delete(self, key: str):
Expand Down
2 changes: 1 addition & 1 deletion aiohttp_client_cache/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

@attr.s(auto_attribs=True, slots=True)
class RequestInfo:
"""A picklable version of from aiohttp.client_reqrep.RequestInfo"""
"""A picklable version of aiohttp.client_reqrep.RequestInfo"""

url: str
method: str
Expand Down
Empty file added test/backends/__init__.py
Empty file.
106 changes: 106 additions & 0 deletions test/backends/test_backend_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import pytest
from unittest.mock import MagicMock, patch

from aiohttp_client_cache import CachedResponse
from aiohttp_client_cache.backends import BACKEND_CLASSES, BACKEND_QUALNAMES, init_backend
from aiohttp_client_cache.backends.base import BaseCache, CacheController, DictCache # noqa
from aiohttp_client_cache.backends.sqlite import SQLiteController


@pytest.mark.asyncio
@pytest.mark.parametrize('backend', BACKEND_QUALNAMES.keys())
@patch('gridfs.Database', MagicMock)
def test_init_backend(backend):
cache = init_backend(backend, 'http-cache', connection=MagicMock())
assert isinstance(cache, BACKEND_CLASSES[backend])
assert cache.name == 'http-cache'


def test_init_backend__default():
cache = init_backend()
assert isinstance(cache, SQLiteController)


def test_init_backend__invalid():
with pytest.raises(ValueError):
init_backend('sybase')


@pytest.mark.asyncio
async def test_cache_controller__get_response__cache_hit():
# Set up a cache with a couple cached items and a redirect
cache = CacheController()
mock_response_1 = MagicMock(spec=CachedResponse)
mock_response_2 = MagicMock(spec=CachedResponse)
await cache.responses.write('request-key-1', mock_response_1)
await cache.responses.write('request-key-2', mock_response_2)
await cache.redirects.write('redirect-key', 'request-key-2')

response = await cache.get_response('request-key-1')
assert response == mock_response_1
response = await cache.get_response('redirect-key')
assert response == mock_response_2


@pytest.mark.asyncio
async def test_cache_controller__get_response__cache_miss():
cache = CacheController()
await cache.responses.write('invalid-response-key', MagicMock())

response = await cache.get_response('nonexistent-key')
assert response is None
response = await cache.get_response('invalid-response-key')
assert response is None


# TODO


@pytest.mark.asyncio
async def test_cache_controller__get_response__cache_expired():
pass


@pytest.mark.asyncio
async def test_cache_controller__save_response():
pass


@pytest.mark.asyncio
async def test_cache_controller__clear():
pass


@pytest.mark.asyncio
async def test_cache_controller__delete():
pass


@pytest.mark.asyncio
async def test_cache_controller__delete_url():
pass


@pytest.mark.asyncio
async def test_cache_controller__delete_expired_responses():
pass


@pytest.mark.asyncio
async def test_cache_controller__has_url():
pass


@pytest.mark.asyncio
async def test_cache_controller__create_key():
pass


@pytest.mark.asyncio
async def test_cache_controller__is_cacheable():
pass


@pytest.mark.asyncio
async def test_cache_controller__is_expired():
pass
9 changes: 9 additions & 0 deletions test/test_session.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# TODO
import pytest

from aiohttp_client_cache import CachedSession


@pytest.mark.asyncio
async def test_session():
CachedSession()

0 comments on commit cf3f8ea

Please sign in to comment.