aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Leon Sandøy <[email protected]>2020-05-22 22:56:25 +0200
committerGravatar Leon Sandøy <[email protected]>2020-05-22 22:56:25 +0200
commit57fe4bf893e94289b5b6f7158ff2d6b92b1e3fae (patch)
treedd7345007495527fd5f74dd2a345cfd48fa921ca
parentOpens a Redis connection in the Bot class. (diff)
Set up async testbed
-rw-r--r--bot/bot.py3
-rw-r--r--bot/utils/redis_cache.py13
-rw-r--r--tests/bot/utils/test_redis_cache.py128
-rw-r--r--tests/bot/utils/test_redis_dict.py189
4 files changed, 135 insertions, 198 deletions
diff --git a/bot/bot.py b/bot/bot.py
index f55eec5bb..8a3805989 100644
--- a/bot/bot.py
+++ b/bot/bot.py
@@ -45,8 +45,7 @@ class Bot(commands.Bot):
# will effectively disable stats.
statsd_url = "127.0.0.1"
- asyncio.create_task(self._create_redis_session())
-
+ self.loop.create_task(self._create_redis_session())
self.stats = AsyncStatsClient(self.loop, statsd_url, 8125, prefix="bot")
async def _create_redis_session(self) -> None:
diff --git a/bot/utils/redis_cache.py b/bot/utils/redis_cache.py
index 467f16767..483bbc2cd 100644
--- a/bot/utils/redis_cache.py
+++ b/bot/utils/redis_cache.py
@@ -30,8 +30,8 @@ class RedisCache:
def __init__(self) -> None:
"""Raise a NotImplementedError if `__set_name__` hasn't been run."""
- if not self._namespace:
- raise NotImplementedError("RedisCache must be a class attribute.")
+ self._namespace = None
+ self.bot = None
def _set_namespace(self, namespace: str) -> None:
"""Try to set the namespace, but do not permit collisions."""
@@ -47,8 +47,7 @@ class RedisCache:
Called automatically when this class is constructed inside a class as an attribute.
"""
- if not self._has_custom_namespace:
- self._set_namespace(f"{owner.__name__}.{attribute_name}")
+ self._set_namespace(f"{owner.__name__}.{attribute_name}")
def __get__(self, instance: RedisCache, owner: Any) -> RedisCache:
"""Fetch the Bot instance, we need it for the redis pool."""
@@ -106,9 +105,9 @@ class RedisCache:
async def pop(self, key: ValidRedisKey, default: Optional[JSONSerializableType] = None) -> JSONSerializableType:
"""Get the item, remove it from the cache, and provide a default if not found."""
- value = await self.get(key, default)
- await self.delete(key)
- return value
+ # value = await self.get(key, default)
+ # await self.delete(key)
+ # return value
async def update(self) -> None:
"""Update the Redis cache with multiple values."""
diff --git a/tests/bot/utils/test_redis_cache.py b/tests/bot/utils/test_redis_cache.py
new file mode 100644
index 000000000..f6344803f
--- /dev/null
+++ b/tests/bot/utils/test_redis_cache.py
@@ -0,0 +1,128 @@
+import asyncio
+import unittest
+from unittest.mock import MagicMock
+
+import fakeredis.aioredis
+
+from bot.bot import Bot
+from bot.utils import RedisCache
+
+
+class RedisCacheTests(unittest.IsolatedAsyncioTestCase):
+ """Tests the RedisDict class from utils.redis_dict.py."""
+
+ redis = RedisCache()
+
+ async def asyncSetUp(self): # noqa: N802 - this special method can't be all lowercase
+ """Sets up the objects that only have to be initialized once."""
+ self.bot = MagicMock(
+ spec=Bot,
+ redis_session=await fakeredis.aioredis.create_redis_pool(),
+ _redis_ready=asyncio.Event(),
+ )
+ self.bot._redis_ready.set()
+
+ def test_class_attribute_namespace(self):
+ """Test that RedisDict creates a namespace automatically for class attributes."""
+ self.assertEqual(self.redis._namespace, "RedisCacheTests.redis")
+ # Test that errors are raised when this isn't true.
+
+ # def test_set_get_item(self):
+ # """Test that users can set and get items from the RedisDict."""
+ # self.redis['favorite_fruit'] = 'melon'
+ # self.redis['favorite_number'] = 86
+ # self.assertEqual(self.redis['favorite_fruit'], 'melon')
+ # self.assertEqual(self.redis['favorite_number'], 86)
+ #
+ # def test_set_item_types(self):
+ # """Test that setitem rejects keys and values that are not strings, ints or floats."""
+ # fruits = ["lemon", "melon", "apple"]
+ #
+ # with self.assertRaises(DataError):
+ # self.redis[fruits] = "nice"
+ #
+ # def test_contains(self):
+ # """Test that we can reliably use the `in` operator with our RedisDict."""
+ # self.redis['favorite_country'] = "Burkina Faso"
+ #
+ # self.assertIn('favorite_country', self.redis)
+ # self.assertNotIn('favorite_dentist', self.redis)
+ #
+ # def test_items(self):
+ # """Test that the RedisDict can be iterated."""
+ # self.redis.clear()
+ # test_cases = (
+ # ('favorite_turtle', 'Donatello'),
+ # ('second_favorite_turtle', 'Leonardo'),
+ # ('third_favorite_turtle', 'Raphael'),
+ # )
+ # for key, value in test_cases:
+ # self.redis[key] = value
+ #
+ # # Test regular iteration
+ # for test_case, key in zip(test_cases, self.redis):
+ # value = test_case[1]
+ # self.assertEqual(self.redis[key], value)
+ #
+ # # Test .items iteration
+ # for key, value in self.redis.items():
+ # self.assertEqual(self.redis[key], value)
+ #
+ # # Test .keys iteration
+ # for test_case, key in zip(test_cases, self.redis.keys()):
+ # value = test_case[1]
+ # self.assertEqual(self.redis[key], value)
+ #
+ # def test_length(self):
+ # """Test that we can get the correct len() from the RedisDict."""
+ # self.redis.clear()
+ # self.redis['one'] = 1
+ # self.redis['two'] = 2
+ # self.redis['three'] = 3
+ # self.assertEqual(len(self.redis), 3)
+ #
+ # self.redis['four'] = 4
+ # self.assertEqual(len(self.redis), 4)
+ #
+ # def test_to_dict(self):
+ # """Test that the .copy method returns a workable dictionary copy."""
+ # copy = self.redis.copy()
+ # local_copy = dict(self.redis.items())
+ # self.assertIs(type(copy), dict)
+ # self.assertEqual(copy, local_copy)
+ #
+ # def test_clear(self):
+ # """Test that the .clear method removes the entire hash."""
+ # self.redis.clear()
+ # self.redis['teddy'] = "with me"
+ # self.redis['in my dreams'] = "you have a weird hat"
+ # self.assertEqual(len(self.redis), 2)
+ #
+ # self.redis.clear()
+ # self.assertEqual(len(self.redis), 0)
+ #
+ # def test_pop(self):
+ # """Test that we can .pop an item from the RedisDict."""
+ # self.redis.clear()
+ # self.redis['john'] = 'was afraid'
+ #
+ # self.assertEqual(self.redis.pop('john'), 'was afraid')
+ # self.assertEqual(self.redis.pop('pete', 'breakneck'), 'breakneck')
+ # self.assertEqual(len(self.redis), 0)
+ #
+ # def test_update(self):
+ # """Test that we can .update the RedisDict with multiple items."""
+ # self.redis.clear()
+ # self.redis["reckfried"] = "lona"
+ # self.redis["bel air"] = "prince"
+ # self.redis.update({
+ # "reckfried": "jona",
+ # "mega": "hungry, though",
+ # })
+ #
+ # result = {
+ # "reckfried": "jona",
+ # "bel air": "prince",
+ # "mega": "hungry, though",
+ # }
+ # self.assertEqual(self.redis.copy(), result)
diff --git a/tests/bot/utils/test_redis_dict.py b/tests/bot/utils/test_redis_dict.py
deleted file mode 100644
index f422887ce..000000000
--- a/tests/bot/utils/test_redis_dict.py
+++ /dev/null
@@ -1,189 +0,0 @@
-import unittest
-
-import fakeredis
-from redis import DataError
-
-from bot.utils import RedisDict
-
-redis_server = fakeredis.FakeServer()
-RedisDict._redis = fakeredis.FakeStrictRedis(server=redis_server)
-
-
-class RedisDictTests(unittest.TestCase):
- """Tests the RedisDict class from utils.redis_dict.py."""
-
- redis = RedisDict()
-
- def test_class_attribute_namespace(self):
- """Test that RedisDict creates a namespace automatically for class attributes."""
- self.assertEqual(self.redis._namespace, "RedisDictTests.redis")
-
- def test_custom_namespace(self):
- """Test that users can set a custom namespaces which never collide."""
- test_cases = (
- (RedisDict("firedog")._namespace, "firedog"),
- (RedisDict("firedog")._namespace, "firedog_"),
- (RedisDict("firedog")._namespace, "firedog__"),
- )
-
- for test_case, result in test_cases:
- self.assertEqual(test_case, result)
-
- def test_custom_namespace_takes_precedence(self):
- """Test that custom namespaces take precedence over class attribute ones."""
- class LemonJuice:
- citrus = RedisDict("citrus")
- watercat = RedisDict()
-
- test_class = LemonJuice()
- self.assertEqual(test_class.citrus._namespace, "citrus")
- self.assertEqual(test_class.watercat._namespace, "LemonJuice.watercat")
-
- def test_set_get_item(self):
- """Test that users can set and get items from the RedisDict."""
- self.redis['favorite_fruit'] = 'melon'
- self.redis['favorite_number'] = 86
- self.assertEqual(self.redis['favorite_fruit'], 'melon')
- self.assertEqual(self.redis['favorite_number'], 86)
-
- def test_set_item_value_types(self):
- """Test that setitem rejects values that are not JSON serializable."""
- with self.assertRaises(TypeError):
- self.redis['favorite_thing'] = object
- self.redis['favorite_stuff'] = RedisDict
-
- def test_set_item_key_types(self):
- """Test that setitem rejects keys that are not strings, ints or floats."""
- fruits = ["lemon", "melon", "apple"]
-
- with self.assertRaises(DataError):
- self.redis[fruits] = "nice"
-
- def test_get_method(self):
- """Test that the .get method works like in a dict."""
- self.redis['favorite_movie'] = 'Code Jam Highlights'
-
- self.assertEqual(self.redis.get('favorite_movie'), 'Code Jam Highlights')
- self.assertEqual(self.redis.get('favorite_youtuber', 'pydis'), 'pydis')
- self.assertIsNone(self.redis.get('favorite_dog'))
-
- def test_membership(self):
- """Test that we can reliably use the `in` operator with our RedisDict."""
- self.redis['favorite_country'] = "Burkina Faso"
-
- self.assertIn('favorite_country', self.redis)
- self.assertNotIn('favorite_dentist', self.redis)
-
- def test_del_item(self):
- """Test that users can delete items from the RedisDict."""
- self.redis['favorite_band'] = "Radiohead"
- self.assertIn('favorite_band', self.redis)
-
- del self.redis['favorite_band']
- self.assertNotIn('favorite_band', self.redis)
-
- def test_iter(self):
- """Test that the RedisDict can be iterated."""
- self.redis.clear()
- test_cases = (
- ('favorite_turtle', 'Donatello'),
- ('second_favorite_turtle', 'Leonardo'),
- ('third_favorite_turtle', 'Raphael'),
- )
- for key, value in test_cases:
- self.redis[key] = value
-
- # Test regular iteration
- for test_case, key in zip(test_cases, self.redis):
- value = test_case[1]
- self.assertEqual(self.redis[key], value)
-
- # Test .items iteration
- for key, value in self.redis.items():
- self.assertEqual(self.redis[key], value)
-
- # Test .keys iteration
- for test_case, key in zip(test_cases, self.redis.keys()):
- value = test_case[1]
- self.assertEqual(self.redis[key], value)
-
- def test_len(self):
- """Test that we can get the correct len() from the RedisDict."""
- self.redis.clear()
- self.redis['one'] = 1
- self.redis['two'] = 2
- self.redis['three'] = 3
- self.assertEqual(len(self.redis), 3)
-
- self.redis['four'] = 4
- self.assertEqual(len(self.redis), 4)
-
- def test_copy(self):
- """Test that the .copy method returns a workable dictionary copy."""
- copy = self.redis.copy()
- local_copy = dict(self.redis.items())
- self.assertIs(type(copy), dict)
- self.assertEqual(copy, local_copy)
-
- def test_clear(self):
- """Test that the .clear method removes the entire hash."""
- self.redis.clear()
- self.redis['teddy'] = "with me"
- self.redis['in my dreams'] = "you have a weird hat"
- self.assertEqual(len(self.redis), 2)
-
- self.redis.clear()
- self.assertEqual(len(self.redis), 0)
-
- def test_pop(self):
- """Test that we can .pop an item from the RedisDict."""
- self.redis.clear()
- self.redis['john'] = 'was afraid'
-
- self.assertEqual(self.redis.pop('john'), 'was afraid')
- self.assertEqual(self.redis.pop('pete', 'breakneck'), 'breakneck')
- self.assertEqual(len(self.redis), 0)
-
- def test_popitem(self):
- """Test that we can .popitem an item from the RedisDict."""
- self.redis.clear()
- self.redis['john'] = 'the revalator'
- self.redis['teddy'] = 'big bear'
-
- self.assertEqual(len(self.redis), 2)
- self.assertEqual(self.redis.popitem(), 'big bear')
- self.assertEqual(len(self.redis), 1)
-
- def test_setdefault(self):
- """Test that we can .setdefault an item from the RedisDict."""
- self.redis.clear()
- self.redis.setdefault('john', 'is yellow and weak')
- self.assertEqual(self.redis['john'], 'is yellow and weak')
-
- with self.assertRaises(TypeError):
- self.redis.setdefault('geisha', object)
-
- def test_update(self):
- """Test that we can .update the RedisDict with multiple items."""
- self.redis.clear()
- self.redis["reckfried"] = "lona"
- self.redis["bel air"] = "prince"
- self.redis.update({
- "reckfried": "jona",
- "mega": "hungry, though",
- })
-
- result = {
- "reckfried": "jona",
- "bel air": "prince",
- "mega": "hungry, though",
- }
- self.assertEqual(self.redis.copy(), result)
-
- def test_equals(self):
- """Test that RedisDicts can be compared with == and !=."""
- new_redis_dict = RedisDict("firedog_the_sequel")
- new_new_redis_dict = new_redis_dict
-
- self.assertEqual(new_redis_dict, new_new_redis_dict)
- self.assertNotEqual(new_redis_dict, self.redis)