Skip to content

Commit

Permalink
Added testing for cached decorator and fixed bug
Browse files Browse the repository at this point in the history
  • Loading branch information
argaen committed Oct 11, 2016
1 parent e0e9a38 commit cb3565a
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 42 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Current supported backends are:
This libraries aims for simplicity over specialization. It provides a common interface for all caches which allows to store any python object. The operations supported by all backends are:

- ``add``
- ``exists``
- ``get``
- ``set``
- ``multi_get``
Expand Down
22 changes: 13 additions & 9 deletions aiocache/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,40 @@ def get_serializer(self):
return DefaultSerializer()

@abc.abstractmethod
async def add(self, key, value, ttl=None):
async def add(self, key, value, ttl=None): # pragma: no cover
pass

@abc.abstractmethod
async def get(self, key, default=None):
async def get(self, key, default=None): # pragma: no cover
pass

@abc.abstractmethod
async def multi_get(self, keys):
async def multi_get(self, keys): # pragma: no cover
pass

@abc.abstractmethod
async def set(self, key, value, ttl=None):
async def set(self, key, value, ttl=None): # pragma: no cover
pass

@abc.abstractmethod
async def multi_set(self, pairs):
async def multi_set(self, pairs): # pragma: no cover
pass

@abc.abstractmethod
async def delete(self, key):
async def delete(self, key): # pragma: no cover
pass

async def expire(self, key, ttl):
@abc.abstractmethod
async def exists(self, key): # pragma: no cover
pass

async def expire(self, key, ttl): # pragma: no cover
raise NotImplementedError

async def ttl(self, key):
async def ttl(self, key): # pragma: no cover
raise NotImplementedError

async def persist(self, key):
async def persist(self, key): # pragma: no cover
raise NotImplementedError

def _build_key(self, key):
Expand Down
9 changes: 9 additions & 0 deletions aiocache/backends/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ async def add(self, key, value, ttl=None, dumps_fn=None):
loop.call_later(ttl, self._delete, key)
return True

async def exists(self, key):
"""
Check key exists in the cache.
:param key: str key to check
:returns: True if key exists otherwise False
"""
return self._build_key(key) in SimpleMemoryCache._cache

async def delete(self, key):
"""
Deletes the given key.
Expand Down
10 changes: 10 additions & 0 deletions aiocache/backends/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ async def add(self, key, value, ttl=None, dumps_fn=None):
"Key {} already exists, use .set to update the value".format(key))
return await redis.set(key, dumps(value), expire=ttl)

async def exists(self, key):
"""
Check key exists in the cache.
:param key: str key to check
:returns: True if key exists otherwise False
"""
with await self._connect() as redis:
return await redis.exists(self._build_key(key))

async def delete(self, key):
"""
Deletes the given key.
Expand Down
7 changes: 3 additions & 4 deletions aiocache/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@ def cached(ttl=0, backend=None, serializer=None, *args, **kwargs):

def cached_decorator(fn):
async def wrapper(*args, **kwargs):
key = fn.__module__ + fn.__name__ + str(args) + str(kwargs)
value = await cache.get(key)
if value:
return value
key = (fn.__module__ or "stub") + fn.__name__ + str(args) + str(kwargs)
if await cache.exists(key):
return await cache.get(key)
else:
res = await fn(*args, **kwargs)
await cache.set(key, res, ttl=ttl)
Expand Down
9 changes: 9 additions & 0 deletions tests/test_base_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,12 @@ async def test_add_existing(self, cache):
await cache.set(pytest.KEY, "value") is True
with pytest.raises(ValueError):
await cache.add(pytest.KEY, "value")

@pytest.mark.asyncio
async def test_exists_missing(self, cache):
assert await cache.exists(pytest.KEY) is False

@pytest.mark.asyncio
async def test_exists_existing(self, cache):
await cache.set(pytest.KEY, "value")
assert await cache.exists(pytest.KEY) is True
73 changes: 44 additions & 29 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,60 @@
import pytest
import aiocache
import asyncio

from aiocache import RedisCache, SimpleMemoryCache
from aiocache import RedisCache, SimpleMemoryCache, cached
from aiocache.utils import get_default_cache
from aiocache.serializers import PickleSerializer, DefaultSerializer


@pytest.mark.asyncio
async def test_default_cache_with_backend():
class TestCachedDecorator:

cache = get_default_cache(
backend=RedisCache, namespace="test", endpoint="http://...",
port=6379, serializer=PickleSerializer())
@pytest.mark.asyncio
async def test_cached_no_args(self, mocker):
mocker.spy(asyncio, 'sleep')
cached_decorator = cached(ttl=10)

assert isinstance(cache, RedisCache)
assert cache.namespace == "test"
assert cache.endpoint == "http://..."
assert cache.port == 6379
assert isinstance(cache.serializer, PickleSerializer)
resp1 = await cached_decorator(asyncio.sleep)(1)
resp2 = await cached_decorator(asyncio.sleep)(1)

assert asyncio.sleep.call_count == 1
assert resp1 is resp2

@pytest.mark.asyncio
async def test_default_cache_with_global():
aiocache.config_default_cache(
backend=RedisCache, namespace="test", endpoint="http://...",
port=6379, serializer=PickleSerializer())
cache = get_default_cache()

assert isinstance(cache, RedisCache)
assert cache.namespace == "test"
assert cache.endpoint == "http://..."
assert cache.port == 6379
assert isinstance(cache.serializer, PickleSerializer)
class TestDefaultCache:

aiocache.default_cache = None
@pytest.mark.asyncio
async def test_default_cache_with_backend(self):

cache = get_default_cache(
backend=RedisCache, namespace="test", endpoint="http://...",
port=6379, serializer=PickleSerializer())

@pytest.mark.asyncio
async def test_default_cache_with_default():
cache = get_default_cache(namespace="test")
assert isinstance(cache, RedisCache)
assert cache.namespace == "test"
assert cache.endpoint == "http://..."
assert cache.port == 6379
assert isinstance(cache.serializer, PickleSerializer)

assert isinstance(cache, SimpleMemoryCache)
assert cache.namespace == "test"
assert isinstance(cache.serializer, DefaultSerializer)
@pytest.mark.asyncio
async def test_default_cache_with_global(self):
aiocache.config_default_cache(
backend=RedisCache, namespace="test", endpoint="http://...",
port=6379, serializer=PickleSerializer())
cache = get_default_cache()

assert isinstance(cache, RedisCache)
assert cache.namespace == "test"
assert cache.endpoint == "http://..."
assert cache.port == 6379
assert isinstance(cache.serializer, PickleSerializer)

aiocache.default_cache = None

@pytest.mark.asyncio
async def test_default_cache_with_default(self):
cache = get_default_cache(namespace="test")

assert isinstance(cache, SimpleMemoryCache)
assert cache.namespace == "test"
assert isinstance(cache.serializer, DefaultSerializer)

0 comments on commit cb3565a

Please sign in to comment.