async def test_on_raw_reaction_add_returns_for_bot_and_non_staff_members( self): """The `on_raw_reaction_add` event handler should return for bot users or non-staff members.""" channel_id = 1234 message_id = 2345 user_id = 3456 channel, message, _, payload = self._raw_reaction_mocks( channel_id, message_id, user_id) test_cases = ( ("non-staff member", helpers.MockMember(id=user_id)), ("bot staff member", helpers.MockMember(id=user_id, roles=[self.staff_role], bot=True)), ) payload.emoji = self.duck_pond_emoji for description, member in test_cases: message.guild.members = (member, ) with self.subTest(test_case=description), patch( f"{MODULE_PATH}.DuckPond.has_green_checkmark" ) as checkmark: checkmark.side_effect = AssertionError( "Expected method to return before calling `self.has_green_checkmark`." ) self.assertIsNone(await self.cog.on_raw_reaction_add(payload)) # Check that we did make it past the payload checks channel.fetch_message.assert_called_once() channel.fetch_message.reset_mock()
def test_reaction_check_for_valid_emoji_and_authors(self): """Should return True if authors are identical or are a bot and a core dev, respectively.""" user_subtests = ( ( helpers.MockMember(id=77), helpers.MockMember(id=77), "identical users", ), ( helpers.MockMember(id=77, bot=True), helpers.MockMember(id=43, roles=[self.core_dev_role]), "bot author and core-dev reactor", ), ) for emoji in self.syncer._REACTION_EMOJIS: for author, user, msg in user_subtests: with self.subTest(author=author, user=user, emoji=emoji, msg=msg): message, reaction = self.get_message_reaction(emoji) ret_val = self.syncer._reaction_check( author, message, reaction, user) self.assertTrue(ret_val)
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_ship_command(self): context = helpers.MockContext( guild=helpers.MockGuild( members=[helpers.MockMember(), helpers.MockMember()] ) ) await self.cog.ship(self.cog, context) self.assertEqual(context.send.call_args.kwargs.get("embed"), None)
def test_mock_guild_alternative_arguments(self): """Test if MockGuild initializes with the arguments provided.""" core_developer = helpers.MockRole(name="Core Developer", position=2) guild = helpers.MockGuild( roles=[core_developer], members=[helpers.MockMember(id=54321)], ) self.assertListEqual(guild.roles, [helpers.MockRole(name="@everyone", position=1, id=0), core_developer]) self.assertListEqual(guild.members, [helpers.MockMember(id=54321)])
def setUp(self): """Set up steps executed before each test is run.""" self.bot = helpers.MockBot() self.cog = information.Information(self.bot) self.moderator_role = helpers.MockRole(name="Moderators", id=2, position=10) self.flautist_role = helpers.MockRole(name="Flautists", id=3, position=2) self.bassist_role = helpers.MockRole(name="Bassists", id=4, position=3) self.author = helpers.MockMember(id=1, name="syntaxaire") self.moderator = helpers.MockMember(id=2, name="riffautae", roles=[self.moderator_role]) self.target = helpers.MockMember(id=3, name="__fluzz__")
def test_is_staff_returns_correct_values_based_on_instance_passed(self): """The `is_staff` method should return correct values based on the instance passed.""" test_cases = ( (helpers.MockUser(name="User instance"), False), (helpers.MockMember(name="Member instance without staff role"), False), (helpers.MockMember(name="Member instance with staff role", roles=[self.staff_role]), True) ) for user, expected_return in test_cases: actual_return = self.cog.is_staff(user) with self.subTest(user_type=user.name, expected_return=expected_return, actual_return=actual_return): self.assertEqual(expected_return, actual_return)
def setUpClass(cls): """Sets up the objects that only have to be initialized once.""" cls.nonstaff_member = helpers.MockMember(name="Non-staffer") cls.staff_role = helpers.MockRole(name="Staff role", id=constants.STAFF_ROLES[0]) cls.staff_member = helpers.MockMember(name="staffer", roles=[cls.staff_role]) cls.checkmark_emoji = "\N{White Heavy Check Mark}" cls.thumbs_up_emoji = "\N{Thumbs Up Sign}" cls.unicode_duck_emoji = "\N{Duck}" cls.duck_pond_emoji = helpers.MockPartialEmoji(id=constants.DuckPond.custom_emojis[0]) cls.non_duck_custom_emoji = helpers.MockPartialEmoji(id=123)
async def test_sync_cog_on_member_update_roles(self): """Members should be patched if their roles have changed.""" self.assertTrue(self.cog.on_member_update.__cog_listener__) # Roles are intentionally unsorted. before_roles = [helpers.MockRole(id=12), helpers.MockRole(id=30), helpers.MockRole(id=20)] before_member = helpers.MockMember(roles=before_roles, guild=self.guild) after_member = helpers.MockMember(roles=before_roles[1:], guild=self.guild) await self.cog.on_member_update(before_member, after_member) data = {"roles": sorted(role.id for role in after_member.roles)} self.cog.patch_user.assert_called_once_with(after_member.id, json=data)
def test_create_user_embed_basic_information_outside_of_moderation_channels( self, infraction_counts): """The embed should contain only basic infraction data outside of mod channels.""" ctx = helpers.MockContext(channel=helpers.MockTextChannel( channel_id=100)) moderators_role = helpers.MockRole('Moderators') moderators_role.colour = 100 infraction_counts.return_value = "basic infractions info" user = helpers.MockMember(user_id=314, roles=[moderators_role], top_role=moderators_role) embed = asyncio.run(self.cog.create_user_embed(ctx, user)) infraction_counts.assert_called_once_with(user) self.assertEqual( textwrap.dedent(f""" **User Information** Created: {"1 year ago"} Profile: {user.mention} ID: {user.id} **Member Information** Joined: {"1 year ago"} Roles: &Moderators basic infractions info """).strip(), embed.description)
def setUp(self): self.bot = helpers.MockBot(user=helpers.MockMember(bot=True)) self.syncer = TestSyncer(self.bot) self.guild = helpers.MockGuild() # Make sure `_get_diff` returns a MagicMock, not an AsyncMock self.syncer._get_diff.return_value = mock.MagicMock()
async def test_sync_confirmation_context_redirect(self): """If ctx is given, a new message should be sent and author should be ctx's author.""" mock_member = helpers.MockMember() subtests = ( (None, self.bot.user, None), (helpers.MockContext(author=mock_member), mock_member, helpers.MockMessage()), ) for ctx, author, message in subtests: with self.subTest(ctx=ctx, author=author, message=message): if ctx is not None: ctx.send.return_value = message # Make sure `_get_diff` returns a MagicMock, not an AsyncMock self.syncer._get_diff.return_value = mock.MagicMock() self.syncer._get_confirmation_result = mock.AsyncMock( return_value=(False, None)) guild = helpers.MockGuild() await self.syncer.sync(guild, ctx) if ctx is not None: ctx.send.assert_called_once() self.syncer._get_confirmation_result.assert_called_once() self.assertEqual( self.syncer._get_confirmation_result.call_args[0][1], author) self.assertEqual( self.syncer._get_confirmation_result.call_args[0][2], message)
def setUp(self): """Set up steps executed before each test is run.""" self.bot = helpers.MockBot() self.cog = information.Information(self.bot) self.moderator_role = helpers.MockRole(name="Moderators", id=2, position=10) self.flautist_role = helpers.MockRole(name="Flautists", id=3, position=2) self.bassist_role = helpers.MockRole(name="Bassists", id=4, position=3) self.author = helpers.MockMember(id=1, name="syntaxaire") self.moderator = helpers.MockMember(id=2, name="riffautae", roles=[self.moderator_role]) self.target = helpers.MockMember(id=3, name="__fluzz__") # There's no way to mock the channel constant without deferring imports. The constant is # used as a default value for a parameter, which gets defined upon import. self.bot_command_channel = helpers.MockTextChannel(id=constants.Channels.bot_commands)
async def on_member_join_helper(self, side_effect: Exception) -> dict: """ Helper to set `side_effect` for on_member_join and assert a PUT request was sent. The request data for the mock member is returned. All exceptions will be re-raised. """ member = helpers.MockMember( discriminator="1234", roles=[helpers.MockRole(id=22), helpers.MockRole(id=12)], guild=self.guild, ) data = { "discriminator": int(member.discriminator), "id": member.id, "in_guild": True, "name": member.name, "roles": sorted(role.id for role in member.roles) } self.bot.api_client.put.reset_mock(side_effect=True) self.bot.api_client.put.side_effect = side_effect try: await self.cog.on_member_join(member) except Exception: raise finally: self.bot.api_client.put.assert_called_once_with( f"bot/users/{member.id}", json=data) return data
def setUpClass(cls): """Set up helpers that only need to be defined once.""" cls.bot_commands = helpers.MockTextChannel(id=123456789, category_id=123456) cls.help_channel = helpers.MockTextChannel(id=987654321, category_id=987654) cls.non_whitelisted_channel = helpers.MockTextChannel(id=666666) cls.dm_channel = helpers.MockDMChannel() cls.non_staff_member = helpers.MockMember() cls.staff_role = helpers.MockRole(id=121212) cls.staff_member = helpers.MockMember(roles=(cls.staff_role, )) cls.channels = (cls.bot_commands.id, ) cls.categories = (cls.help_channel.category_id, ) cls.roles = (cls.staff_role.id, )
async def test_create_user_embed_basic_information_outside_of_moderation_channels(self, infraction_counts): """The embed should contain only basic infraction data outside of mod channels.""" ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=100)) moderators_role = helpers.MockRole(name='Moderators') moderators_role.colour = 100 infraction_counts.return_value = ("Infractions", "basic infractions info") user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role) embed = await self.cog.create_user_embed(ctx, user) infraction_counts.assert_called_once_with(user) self.assertEqual( textwrap.dedent(f""" Created: {"1 year ago"} Profile: {user.mention} ID: {user.id} """).strip(), embed.fields[0].value ) self.assertEqual( textwrap.dedent(f""" Joined: {"1 year ago"} Roles: &Moderators """).strip(), embed.fields[1].value ) self.assertEqual( "basic infractions info", embed.fields[3].value )
async def test_create_user_embed_expanded_information_in_moderation_channels( self, nomination_counts, infraction_counts): """The embed should contain expanded infractions and nomination info in mod channels.""" ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=50)) moderators_role = helpers.MockRole(name='Moderators') infraction_counts.return_value = ("Infractions", "expanded infractions info") nomination_counts.return_value = ("Nominations", "nomination info") user = helpers.MockMember(id=314, roles=[moderators_role], colour=100) embed = await self.cog.create_user_embed(ctx, user) infraction_counts.assert_called_once_with(user) nomination_counts.assert_called_once_with(user) self.assertEqual( textwrap.dedent(f""" Created: {"1 year ago"} Profile: {user.mention} ID: {user.id} """).strip(), embed.fields[0].value) self.assertEqual( textwrap.dedent(f""" Joined: {"1 year ago"} Verified: {"True"} Roles: &Moderators """).strip(), embed.fields[1].value)
def test_create_user_embed_expanded_information_in_moderation_channels( self, nomination_counts, infraction_counts): """The embed should contain expanded infractions and nomination info in mod channels.""" ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=50)) moderators_role = helpers.MockRole(name='Moderators') moderators_role.colour = 100 infraction_counts.return_value = "expanded infractions info" nomination_counts.return_value = "nomination info" user = helpers.MockMember(id=314, roles=[moderators_role], top_role=moderators_role) embed = asyncio.run(self.cog.create_user_embed(ctx, user)) infraction_counts.assert_called_once_with(user) nomination_counts.assert_called_once_with(user) self.assertEqual( textwrap.dedent(f""" **User Information** Created: {"1 year ago"} Profile: {user.mention} ID: {user.id} **Member Information** Joined: {"1 year ago"} Roles: &Moderators expanded infractions info nomination info """).strip(), embed.description)
async def test_confirmation_result_large_diff(self): """Should return True if confirmed and False if _send_prompt fails or aborted.""" author = helpers.MockMember() mock_message = helpers.MockMessage() subtests = ( (True, mock_message, True, "confirmed"), (False, None, False, "_send_prompt failed"), (False, mock_message, False, "aborted"), ) for expected_result, expected_message, confirmed, msg in subtests: # pragma: no cover with self.subTest(msg=msg): self.syncer._send_prompt = mock.AsyncMock( return_value=expected_message) self.syncer._wait_for_confirmation = mock.AsyncMock( return_value=confirmed) coro = self.syncer._get_confirmation_result(4, author) actual_result, actual_message = await coro self.syncer._send_prompt.assert_called_once_with( None) # message defaults to None self.assertIs(actual_result, expected_result) self.assertEqual(actual_message, expected_message) if expected_message: self.syncer._wait_for_confirmation.assert_called_once_with( author, expected_message)
def get_mock_member(member: dict): member = member.copy() del member["in_guild"] mock_member = helpers.MockMember(**member) mock_member.roles = [ helpers.MockRole(id=role_id) for role_id in member["roles"] ] return mock_member
async def test_create_user_embed_uses_blurple_colour_when_user_has_no_roles(self): """The embed should be created with a blurple colour if the user has no assigned roles.""" ctx = helpers.MockContext() user = helpers.MockMember(id=217) embed = await self.cog.create_user_embed(ctx, user) self.assertEqual(embed.colour, discord.Colour.blurple())
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 test_mock_member_accepts_dynamic_arguments(self): """Test if MockMember accepts and sets abitrary keyword arguments.""" member = helpers.MockMember( nick="Dino Man", colour=discord.Colour.default(), ) self.assertEqual(member.nick, "Dino Man") self.assertEqual(member.colour, discord.Colour.default())
async def test_sync_cog_on_member_remove(self): """Member should be patched to set in_guild as False.""" self.assertTrue(self.cog.on_member_remove.__cog_listener__) member = helpers.MockMember(guild=self.guild) await self.cog.on_member_remove(member) self.cog.patch_user.assert_called_once_with(member.id, json={"in_guild": False})
async def test_create_user_embed_uses_png_format_of_user_avatar_as_thumbnail(self): """The embed thumbnail should be set to the user's avatar in `png` format.""" ctx = helpers.MockContext() user = helpers.MockMember(id=217) user.avatar_url_as.return_value = "avatar url" embed = await self.cog.create_user_embed(ctx, user) user.avatar_url_as.assert_called_once_with(static_format="png") self.assertEqual(embed.thumbnail.url, "avatar url")
async def test_create_user_embed_uses_nick_in_title_if_available(self): """The embed should use the nick if it's available.""" ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1)) user = helpers.MockMember() user.nick = "Cat lover" user.__str__ = unittest.mock.Mock(return_value="Mr. Hemlock") embed = await self.cog.create_user_embed(ctx, user) self.assertEqual(embed.title, "Cat lover (Mr. Hemlock)")
async def test_create_user_embed_uses_string_representation_of_user_in_title_if_nick_is_not_available(self): """The embed should use the string representation of the user if they don't have a nick.""" ctx = helpers.MockContext(channel=helpers.MockTextChannel(id=1)) user = helpers.MockMember() user.nick = None user.__str__ = unittest.mock.Mock(return_value="Mr. Hemlock") embed = await self.cog.create_user_embed(ctx, user) self.assertEqual(embed.title, "Mr. Hemlock")
def test_mock_member_default_initialization(self): """Test if the default initialization of Mockmember results in the correct object.""" member = helpers.MockMember() # The `spec` argument makes sure `isistance` checks with `discord.Member` pass self.assertIsInstance(member, discord.Member) self.assertEqual(member.name, "member") self.assertListEqual(member.roles, [helpers.MockRole(name="@everyone", position=1, id=0)]) self.assertEqual(member.mention, "@member")
async def test_match_command(self): context = helpers.MockContext() await self.cog.match( self.cog, context, user1=helpers.MockMember(name="Snake Bot", id=744747000293228684), ) self.assertEqual(context.send.call_args.kwargs.get("embed"), None)
async def test_create_user_embed_uses_top_role_colour_when_user_has_roles(self): """The embed should be created with the colour of the top role, if a top role is available.""" ctx = helpers.MockContext() moderators_role = helpers.MockRole(name='Moderators') user = helpers.MockMember(id=314, roles=[moderators_role], colour=100) embed = await self.cog.create_user_embed(ctx, user) self.assertEqual(embed.colour, discord.Colour(100))