def test_on_raw_reaction_add_returns_on_message_with_green_checkmark_placed_by_bot( self, count_ducks, is_staff): """The `on_raw_reaction_add` event should return when the message has a green check mark placed by the bot.""" channel_id = 31415926535 message_id = 27182818284 user_id = 16180339887 channel, message, member, payload = self._raw_reaction_mocks( channel_id, message_id, user_id) payload.emoji = helpers.MockPartialEmoji(name=self.unicode_duck_emoji) payload.emoji.is_custom_emoji.return_value = False message.reactions = [ helpers.MockReaction(emoji=self.checkmark_emoji, users=[self.bot.user]) ] is_staff.return_value = True count_ducks.side_effect = AssertionError( "Expected method to return before calling `self.count_ducks`") self.assertIsNone(asyncio.run(self.cog.on_raw_reaction_add(payload))) # Assert that we've made it past `self.is_staff` is_staff.assert_called_once()
def _get_reaction(self, emoji: typing.Union[str, helpers.MockEmoji], staff: int = 0, nonstaff: int = 0) -> helpers.MockReaction: staffers = [ helpers.MockMember(roles=[self.staff_role]) for _ in range(staff) ] nonstaffers = [helpers.MockMember() for _ in range(nonstaff)] return helpers.MockReaction(emoji=emoji, users=staffers + nonstaffers)
async def test_has_green_checkmark_correctly_detects_presence_of_green_checkmark_emoji( self): """The `has_green_checkmark` method should only return `True` if one is present.""" test_cases = ( ("No reactions", helpers.MockMessage(), False), ("No green check mark reactions", helpers.MockMessage(reactions=[ helpers.MockReaction(emoji=self.unicode_duck_emoji, users=[self.bot.user]), helpers.MockReaction(emoji=self.thumbs_up_emoji, users=[self.bot.user]) ]), False), ("Green check mark reaction, but not from our bot", helpers.MockMessage(reactions=[ helpers.MockReaction(emoji=self.unicode_duck_emoji, users=[self.bot.user]), helpers.MockReaction(emoji=self.checkmark_emoji, users=[self.staff_member]) ]), False), ("Green check mark reaction, with one from the bot", helpers.MockMessage(reactions=[ helpers.MockReaction(emoji=self.unicode_duck_emoji, users=[self.bot.user]), helpers.MockReaction(emoji=self.checkmark_emoji, users=[self.staff_member, self.bot.user]) ]), True)) for description, message, expected_return in test_cases: actual_return = await self.cog.has_green_checkmark(message) with self.subTest(test_case=description, expected_return=expected_return, actual_return=actual_return): self.assertEqual(expected_return, actual_return)
async def test_wait_for_confirmation(self): """The message should always be edited and only return True if the emoji is a check mark.""" subtests = ( (constants.Emojis.check_mark, True, None), ("InVaLiD", False, None), (None, False, asyncio.TimeoutError), ) for emoji, ret_val, side_effect in subtests: for bot in (True, False): with self.subTest(emoji=emoji, ret_val=ret_val, side_effect=side_effect, bot=bot): # Set up mocks message = helpers.MockMessage() member = helpers.MockMember(bot=bot) self.bot.wait_for.reset_mock() self.bot.wait_for.return_value = (helpers.MockReaction( emoji=emoji), None) self.bot.wait_for.side_effect = side_effect # Call the function actual_return = await self.syncer._wait_for_confirmation( member, message) # Perform assertions self.bot.wait_for.assert_called_once() self.assertIn("reaction_add", self.bot.wait_for.call_args[0]) message.edit.assert_called_once() kwargs = message.edit.call_args[1] self.assertIn("content", kwargs) # Core devs should only be mentioned if the author is a bot. if bot: self.assertIn(self.syncer._CORE_DEV_MENTION, kwargs["content"]) else: self.assertNotIn(self.syncer._CORE_DEV_MENTION, kwargs["content"]) self.assertIs(actual_return, ret_val)
def test_reaction_check_for_invalid_reactions(self): """Should return False for invalid reaction events.""" valid_emoji = self.syncer._REACTION_EMOJIS[0] subtests = ( ( helpers.MockMember(id=77), *self.get_message_reaction(valid_emoji), helpers.MockMember(id=43, roles=[self.core_dev_role]), "users are not identical", ), ( helpers.MockMember(id=77, bot=True), *self.get_message_reaction(valid_emoji), helpers.MockMember(id=43), "reactor lacks the core-dev role", ), ( helpers.MockMember(id=77, bot=True, roles=[self.core_dev_role]), *self.get_message_reaction(valid_emoji), helpers.MockMember(id=77, bot=True, roles=[self.core_dev_role]), "reactor is a bot", ), ( helpers.MockMember(id=77), helpers.MockMessage(id=95), helpers.MockReaction(emoji=valid_emoji, message=helpers.MockMessage(id=26)), helpers.MockMember(id=77), "messages are not identical", ), ( helpers.MockMember(id=77), *self.get_message_reaction("InVaLiD"), helpers.MockMember(id=77), "emoji is invalid", ), ) for *args, msg in subtests: kwargs = dict(zip(("author", "message", "reaction", "user"), args)) with self.subTest(**kwargs, msg=msg): ret_val = self.syncer._reaction_check(*args) self.assertFalse(ret_val)
def get_message_reaction(emoji): """Fixture to return a mock message an reaction from the given `emoji`.""" message = helpers.MockMessage() reaction = helpers.MockReaction(emoji=emoji, message=message) return message, reaction
async def test_count_ducks_correctly_counts_the_number_of_eligible_duck_emojis( self): """The `count_ducks` method should return the number of unique staffers who gave a duck.""" test_cases = ( # Simple test cases # A message without reactions should return 0 ("No reactions", helpers.MockMessage(), 0), # A message with a non-duck reaction from a non-staffer should return 0 ("Non-duck reaction from non-staffer", helpers.MockMessage(reactions=[ self._get_reaction(emoji=self.thumbs_up_emoji, nonstaff=1) ]), 0), # A message with a non-duck reaction from a staffer should return 0 ("Non-duck reaction from staffer", helpers.MockMessage(reactions=[ self._get_reaction(emoji=self.non_duck_custom_emoji, staff=1) ]), 0), # A message with a non-duck reaction from a non-staffer and staffer should return 0 ("Non-duck reaction from staffer + non-staffer", helpers.MockMessage(reactions=[ self._get_reaction( emoji=self.thumbs_up_emoji, staff=1, nonstaff=1) ]), 0), # A message with a unicode duck reaction from a non-staffer should return 0 ("Unicode Duck Reaction from non-staffer", helpers.MockMessage(reactions=[ self._get_reaction(emoji=self.unicode_duck_emoji, nonstaff=1) ]), 0), # A message with a unicode duck reaction from a staffer should return 1 ("Unicode Duck Reaction from staffer", helpers.MockMessage(reactions=[ self._get_reaction(emoji=self.unicode_duck_emoji, staff=1) ]), 1), # A message with a unicode duck reaction from a non-staffer and staffer should return 1 ("Unicode Duck Reaction from staffer + non-staffer", helpers.MockMessage(reactions=[ self._get_reaction( emoji=self.unicode_duck_emoji, staff=1, nonstaff=1) ]), 1), # A message with a duckpond duck reaction from a non-staffer should return 0 ("Duckpond Duck Reaction from non-staffer", helpers.MockMessage(reactions=[ self._get_reaction(emoji=self.duck_pond_emoji, nonstaff=1) ]), 0), # A message with a duckpond duck reaction from a staffer should return 1 ("Duckpond Duck Reaction from staffer", helpers.MockMessage(reactions=[ self._get_reaction(emoji=self.duck_pond_emoji, staff=1) ]), 1), # A message with a duckpond duck reaction from a non-staffer and staffer should return 1 ("Duckpond Duck Reaction from staffer + non-staffer", helpers.MockMessage(reactions=[ self._get_reaction( emoji=self.duck_pond_emoji, staff=1, nonstaff=1) ]), 1), # Complex test cases # A message with duckpond duck reactions from 3 staffers and 2 non-staffers returns 3 ("Duckpond Duck Reaction from 3 staffers + 2 non-staffers", helpers.MockMessage(reactions=[ self._get_reaction( emoji=self.duck_pond_emoji, staff=3, nonstaff=2) ]), 3), # A staffer with multiple duck reactions only counts once ("Two different duck reactions from the same staffer", helpers.MockMessage(reactions=[ helpers.MockReaction(emoji=self.duck_pond_emoji, users=[self.staff_member]), helpers.MockReaction(emoji=self.unicode_duck_emoji, users=[self.staff_member]), ]), 1), # A non-string emoji does not count (to test the `isinstance(reaction.emoji, str)` elif) ("Reaction with non-Emoji/str emoij from 3 staffers + 2 non-staffers", helpers.MockMessage(reactions=[ self._get_reaction(emoji=100, staff=3, nonstaff=2) ]), 0), # We correctly sum when multiple reactions are provided. ("Duckpond Duck Reaction from 3 staffers + 2 non-staffers", helpers.MockMessage(reactions=[ self._get_reaction( emoji=self.duck_pond_emoji, staff=3, nonstaff=2), self._get_reaction( emoji=self.unicode_duck_emoji, staff=4, nonstaff=9), ]), 3 + 4), ) for description, message, expected_count in test_cases: actual_count = await self.cog.count_ducks(message) with self.subTest(test_case=description, expected_count=expected_count, actual_count=actual_count): self.assertEqual(expected_count, actual_count)