diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/bot/cogs/test_duck_pond.py | 70 | ||||
-rw-r--r-- | tests/helpers.py | 38 |
2 files changed, 93 insertions, 15 deletions
diff --git a/tests/bot/cogs/test_duck_pond.py b/tests/bot/cogs/test_duck_pond.py index 31c7e9f89..af8ef0e4d 100644 --- a/tests/bot/cogs/test_duck_pond.py +++ b/tests/bot/cogs/test_duck_pond.py @@ -1,8 +1,10 @@ +import asyncio import logging import unittest +from bot import constants from bot.cogs import duck_pond -from tests.helpers import MockBot, MockMember, MockMessage, MockReaction, MockRole +from tests.helpers import MockBot, MockEmoji, MockMember, MockMessage, MockReaction, MockRole class DuckPondTest(unittest.TestCase): @@ -13,49 +15,89 @@ class DuckPondTest(unittest.TestCase): self.bot = MockBot() self.cog = duck_pond.DuckPond(bot=self.bot) + # Override the constants we'll be needing + constants.STAFF_ROLES = (123,) + constants.DuckPond.custom_emojis = (789,) + constants.DuckPond.threshold = 1 + # Set up some roles - self.admin_role = MockRole(name="Admins", role_id=476190234653229056) - self.contrib_role = MockRole(name="Contributor", role_id=476190302659543061) + self.admin_role = MockRole(name="Admins", role_id=123) + self.contrib_role = MockRole(name="Contributor", role_id=456) # Set up some users - self.admin_member = MockMember(roles=(self.admin_role,)) + self.admin_member_1 = MockMember(roles=(self.admin_role,), id=1) + self.admin_member_2 = MockMember(roles=(self.admin_role,), id=2) self.contrib_member = MockMember(roles=(self.contrib_role,)) self.no_role_member = MockMember() # Set up emojis self.checkmark_emoji = "✅" self.thumbs_up_emoji = "👍" + self.unicode_duck_emoji = "🦆" + self.yellow_ducky_emoji = MockEmoji(id=789) # Set up reactions - self.checkmark_reaction = MockReaction(emoji=self.checkmark_emoji) - self.thumbs_up_reaction = MockReaction(emoji=self.thumbs_up_emoji) + self.checkmark_reaction = MockReaction( + emoji=self.checkmark_emoji, + user_list=[self.admin_member_1] + ) + self.thumbs_up_reaction = MockReaction( + emoji=self.thumbs_up_emoji, + user_list=[self.admin_member_1, self.contrib_member] + ) + self.yellow_ducky_reaction = MockReaction( + emoji=self.yellow_ducky_emoji, + user_list=[self.admin_member_1, self.contrib_member] + ) + self.unicode_duck_reaction_1 = MockReaction( + emoji=self.unicode_duck_emoji, + user_list=[self.admin_member_1] + ) + self.unicode_duck_reaction_2 = MockReaction( + emoji=self.unicode_duck_emoji, + user_list=[self.admin_member_2] + ) # Set up a messages self.checkmark_message = MockMessage(reactions=(self.checkmark_reaction,)) self.thumbs_up_message = MockMessage(reactions=(self.thumbs_up_reaction,)) + self.yellow_ducky_message = MockMessage(reactions=(self.yellow_ducky_reaction,)) + self.unicode_duck_message = MockMessage(reactions=(self.unicode_duck_reaction_1,)) + self.double_duck_message = MockMessage(reactions=(self.unicode_duck_reaction_1, self.unicode_duck_reaction_2)) self.no_reaction_message = MockMessage() def test_is_staff_correctly_identifies_staff(self): """Test that is_staff correctly identifies a staff member.""" with self.subTest(): - self.assertTrue(duck_pond.DuckPond.is_staff(self.admin_member)) - self.assertFalse(duck_pond.DuckPond.is_staff(self.contrib_member)) - self.assertFalse(duck_pond.DuckPond.is_staff(self.no_role_member)) + self.assertTrue(self.cog.is_staff(self.admin_member_1)) + self.assertFalse(self.cog.is_staff(self.contrib_member)) + self.assertFalse(self.cog.is_staff(self.no_role_member)) def test_has_green_checkmark_correctly_identifies_messages(self): """Test that has_green_checkmark recognizes messages with checkmarks.""" with self.subTest(): - self.assertTrue(duck_pond.DuckPond.has_green_checkmark(self.checkmark_message)) - self.assertFalse(duck_pond.DuckPond.has_green_checkmark(self.thumbs_up_message)) - self.assertFalse(duck_pond.DuckPond.has_green_checkmark(self.no_reaction_message)) + self.assertTrue(self.cog.has_green_checkmark(self.checkmark_message)) + self.assertFalse(self.cog.has_green_checkmark(self.thumbs_up_message)) + self.assertFalse(self.cog.has_green_checkmark(self.no_reaction_message)) def test_count_custom_duck_emojis(self): """A string decoding to numeric characters is a valid user ID.""" - pass + count_one_duck = self.cog.count_ducks(self.yellow_ducky_message) + count_no_ducks = self.cog.count_ducks(self.thumbs_up_message) + with self.subTest(): + self.assertEqual(asyncio.run(count_one_duck), 1) + self.assertEqual(asyncio.run(count_no_ducks), 0) def test_count_unicode_duck_emojis(self): """A string decoding to numeric characters is a valid user ID.""" - pass + count_no_ducks = self.cog.count_ducks(self.thumbs_up_message) + count_one_duck = self.cog.count_ducks(self.unicode_duck_message) + count_two_ducks = self.cog.count_ducks(self.double_duck_message) + + with self.subTest(): + self.assertEqual(asyncio.run(count_no_ducks), 0) + self.assertEqual(asyncio.run(count_one_duck), 1) + self.assertEqual(asyncio.run(count_two_ducks), 2) def test_count_mixed_duck_emojis(self): """A string decoding to numeric characters is a valid user ID.""" diff --git a/tests/helpers.py b/tests/helpers.py index 8496ba031..fd79141ec 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -102,8 +102,30 @@ class AsyncMock(CustomMockMixin, unittest.mock.MagicMock): Python 3.8 will introduce an AsyncMock class in the standard library that will have some more features; this stand-in only overwrites the `__call__` method to an async version. """ + async def __call__(self, *args, **kwargs): - return super(AsyncMock, self).__call__(*args, **kwargs) + return super().__call__(*args, **kwargs) + + +class AsyncIteratorMock: + """ + A class to mock asyncronous iterators. + + This allows async for, which is used in certain Discord.py objects. For example, + an async iterator is returned by the Reaction.users() coroutine. + """ + + def __init__(self, sequence): + self.iter = iter(sequence) + + def __aiter__(self): + return self + + async def __anext__(self): + try: + return next(self.iter) + except StopIteration: + raise StopAsyncIteration # Create a guild instance to get a realistic Mock of `discord.Guild` @@ -155,6 +177,7 @@ class MockGuild(CustomMockMixin, unittest.mock.Mock, HashableMixin): For more info, see the `Mocking` section in `tests/README.md`. """ + def __init__( self, guild_id: int = 1, @@ -187,6 +210,7 @@ class MockRole(CustomMockMixin, unittest.mock.Mock, ColourMixin, HashableMixin): Instances of this class will follow the specifications of `discord.Role` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, name: str = "role", role_id: int = 1, position: int = 1, **kwargs) -> None: super().__init__(spec=role_instance, **kwargs) @@ -213,6 +237,7 @@ class MockMember(CustomMockMixin, unittest.mock.Mock, ColourMixin, HashableMixin Instances of this class will follow the specifications of `discord.Member` instances. For more information, see the `MockGuild` docstring. """ + def __init__( self, name: str = "member", @@ -243,6 +268,7 @@ class MockBot(CustomMockMixin, unittest.mock.MagicMock): Instances of this class will follow the specifications of `discord.ext.commands.Bot` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, **kwargs) -> None: super().__init__(spec=bot_instance, **kwargs) @@ -279,6 +305,7 @@ class MockTextChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin): Instances of this class will follow the specifications of `discord.TextChannel` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, name: str = 'channel', channel_id: int = 1, **kwargs) -> None: super().__init__(spec=channel_instance, **kwargs) self.id = channel_id @@ -320,6 +347,7 @@ class MockContext(CustomMockMixin, unittest.mock.MagicMock): Instances of this class will follow the specifications of `discord.ext.commands.Context` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, **kwargs) -> None: super().__init__(spec=context_instance, **kwargs) self.bot = kwargs.get('bot', MockBot()) @@ -336,6 +364,7 @@ class MockMessage(CustomMockMixin, unittest.mock.MagicMock): Instances of this class will follow the specifications of `discord.Message` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, **kwargs) -> None: super().__init__(spec=message_instance, **kwargs) self.author = kwargs.get('author', MockMember()) @@ -353,6 +382,7 @@ class MockEmoji(CustomMockMixin, unittest.mock.MagicMock): Instances of this class will follow the specifications of `discord.Emoji` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, **kwargs) -> None: super().__init__(spec=emoji_instance, **kwargs) self.guild = kwargs.get('guild', MockGuild()) @@ -371,6 +401,7 @@ class MockPartialEmoji(CustomMockMixin, unittest.mock.MagicMock): Instances of this class will follow the specifications of `discord.PartialEmoji` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, **kwargs) -> None: super().__init__(spec=partial_emoji_instance, **kwargs) @@ -385,7 +416,12 @@ class MockReaction(CustomMockMixin, unittest.mock.MagicMock): Instances of this class will follow the specifications of `discord.Reaction` instances. For more information, see the `MockGuild` docstring. """ + def __init__(self, **kwargs) -> None: super().__init__(spec=reaction_instance, **kwargs) self.emoji = kwargs.get('emoji', MockEmoji()) self.message = kwargs.get('message', MockMessage()) + self.user_list = AsyncIteratorMock(kwargs.get('user_list', [])) + + def users(self): + return self.user_list |