diff options
| author | 2022-01-02 20:13:53 +0000 | |
|---|---|---|
| committer | 2022-02-16 22:12:05 +0000 | |
| commit | 993529aa945a1f9ec8d769c770399dbe2cd8bd25 (patch) | |
| tree | db9edb687e47a6c9f2f6001d29b7e7cd74616301 | |
| parent | Don't append clean log if no clean was done from purge ban (diff) | |
Add tests for new CleanBan and Clean functionality
| -rw-r--r-- | tests/bot/exts/moderation/infraction/test_infractions.py | 90 | ||||
| -rw-r--r-- | tests/bot/exts/moderation/test_clean.py | 104 | 
2 files changed, 193 insertions, 1 deletions
| diff --git a/tests/bot/exts/moderation/infraction/test_infractions.py b/tests/bot/exts/moderation/infraction/test_infractions.py index f89465f84..57235ec6d 100644 --- a/tests/bot/exts/moderation/infraction/test_infractions.py +++ b/tests/bot/exts/moderation/infraction/test_infractions.py @@ -1,13 +1,15 @@  import inspect  import textwrap  import unittest -from unittest.mock import ANY, AsyncMock, MagicMock, Mock, patch +from unittest.mock import ANY, AsyncMock, DEFAULT, MagicMock, Mock, patch  from discord.errors import NotFound  from bot.constants import Event +from bot.exts.moderation.clean import Clean  from bot.exts.moderation.infraction import _utils  from bot.exts.moderation.infraction.infractions import Infractions +from bot.exts.moderation.infraction.management import ModManagement  from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockRole, MockUser, autospec @@ -231,3 +233,89 @@ class VoiceMuteTests(unittest.IsolatedAsyncioTestCase):              "DM": "**Failed**"          })          notify_pardon_mock.assert_awaited_once() + + +class CleanBanTests(unittest.IsolatedAsyncioTestCase): +    """Tests for cleanban functionality.""" + +    def setUp(self): +        self.bot = MockBot() +        self.mod = MockMember(roles=[MockRole(id=7890123, position=10)]) +        self.user = MockMember(roles=[MockRole(id=123456, position=1)]) +        self.guild = MockGuild() +        self.ctx = MockContext(bot=self.bot, author=self.mod) +        self.cog = Infractions(self.bot) +        self.clean_cog = Clean(self.bot) +        self.management_cog = ModManagement(self.bot) + +        self.cog.apply_ban = AsyncMock(return_value={"id": 42}) +        self.log_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" +        self.clean_cog._clean_messages = AsyncMock(return_value=self.log_url) + +    def mock_get_cog(self, enable_clean, enable_manage): +        def inner(name): +            if name == "ModManagement": +                return self.management_cog if enable_manage else None +            elif name == "Clean": +                return self.clean_cog if enable_clean else None +            else: +                return DEFAULT +        return inner + +    async def test_cleanban_falls_back_to_native_purge_without_clean_cog(self): +        """Should fallback to native purge if the Clean cog is not available.""" +        self.bot.get_cog.side_effect = self.mock_get_cog(False, False) + +        self.assertIsNone(await self.cog.cleanban(self.cog, self.ctx, self.user, None, reason="FooBar")) +        self.cog.apply_ban.assert_awaited_once_with( +            self.ctx, +            self.user, +            "FooBar", +            1, +            expires_at=None, +        ) + +    async def test_cleanban_doesnt_purge_messages_if_clean_cog_available(self): +        """Cleanban command should use the native purge messages if the clean cog is available.""" +        self.bot.get_cog.side_effect = self.mock_get_cog(True, False) + +        self.assertIsNone(await self.cog.cleanban(self.cog, self.ctx, self.user, None, reason="FooBar")) +        self.cog.apply_ban.assert_awaited_once_with( +            self.ctx, +            self.user, +            "FooBar", +            expires_at=None, +        ) + +    @patch("bot.exts.moderation.infraction.infractions.Age") +    async def test_cleanban_uses_clean_cog_when_available(self, mocked_age_converter): +        """Test cleanban uses the clean cog to clean messages if it's available.""" +        self.bot.api_client.patch = AsyncMock() +        self.bot.get_cog.side_effect = self.mock_get_cog(True, False) + +        mocked_age_converter.return_value.convert = AsyncMock(return_value="81M") +        self.assertIsNone(await self.cog.cleanban(self.cog, self.ctx, self.user, None, reason="FooBar")) + +        self.clean_cog._clean_messages.assert_awaited_once_with( +            self.ctx, +            users=[self.user], +            channels="*", +            first_limit="81M", +            attempt_delete_invocation=False, +        ) + +    @patch("bot.exts.moderation.infraction.infractions.Infraction") +    async def test_cleanban_edits_infraction_reason(self, mocked_infraction_converter): +        """Ensure cleanban edits the ban reason with a link to the clean log.""" +        self.bot.get_cog.side_effect = self.mock_get_cog(True, True) + +        self.management_cog.infraction_append = AsyncMock() +        mocked_infraction_converter.return_value.convert = AsyncMock(return_value=42) +        self.assertIsNone(await self.cog.cleanban(self.cog, self.ctx, self.user, None, reason="FooBar")) + +        self.management_cog.infraction_append.assert_awaited_once_with( +            self.ctx, +            42, +            None, +            reason=f"[Clean log]({self.log_url})" +        ) diff --git a/tests/bot/exts/moderation/test_clean.py b/tests/bot/exts/moderation/test_clean.py new file mode 100644 index 000000000..83489ea00 --- /dev/null +++ b/tests/bot/exts/moderation/test_clean.py @@ -0,0 +1,104 @@ +import unittest +from unittest.mock import AsyncMock, MagicMock, patch + +from bot.exts.moderation.clean import Clean +from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockMessage, MockRole, MockTextChannel + + +class CleanTests(unittest.IsolatedAsyncioTestCase): +    """Tests for clean cog functionality.""" + +    def setUp(self): +        self.bot = MockBot() +        self.mod = MockMember(roles=[MockRole(id=7890123, position=10)]) +        self.user = MockMember(roles=[MockRole(id=123456, position=1)]) +        self.guild = MockGuild() +        self.ctx = MockContext(bot=self.bot, author=self.mod) +        self.cog = Clean(self.bot) + +        self.log_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" +        self.cog._modlog_cleaned_messages = AsyncMock(return_value=self.log_url) + +        self.cog._use_cache = MagicMock(return_value=True) +        self.cog._delete_found = AsyncMock(return_value=[42, 84]) + +    @patch("bot.exts.moderation.clean.is_mod_channel") +    async def test_clean_deletes_invocation_in_non_mod_channel(self, mod_channel_check): +        """Clean command should delete the invocation message if ran in a non mod channel.""" +        mod_channel_check.return_value = False +        self.ctx.message.delete = AsyncMock() + +        self.assertIsNone(await self.cog._delete_invocation(self.ctx)) + +        self.ctx.message.delete.assert_awaited_once() + +    @patch("bot.exts.moderation.clean.is_mod_channel") +    async def test_clean_doesnt_delete_invocation_in_mod_channel(self, mod_channel_check): +        """Clean command should not delete the invocation message if ran in a mod channel.""" +        mod_channel_check.return_value = True +        self.ctx.message.delete = AsyncMock() + +        self.assertIsNone(await self.cog._delete_invocation(self.ctx)) + +        self.ctx.message.delete.assert_not_awaited() + +    async def test_clean_doesnt_attempt_deletion_when_attempt_delete_invocation_is_false(self): +        """Clean command should not attempt to delete the invocation message if attempt_delete_invocation is false.""" +        self.cog._delete_invocation = AsyncMock() +        self.bot.get_channel = MagicMock(return_value=False) + +        self.assertEqual( +            await self.cog._clean_messages( +                self.ctx, +                None, +                first_limit=MockMessage(), +                attempt_delete_invocation=False, +            ), +            self.log_url, +        ) + +        self.cog._delete_invocation.assert_not_awaited() + +    @patch("bot.exts.moderation.clean.is_mod_channel") +    async def test_clean_replies_with_success_message_when_ran_in_mod_channel(self, mod_channel_check): +        """Clean command should reply to the message with a confirmation message if invoked in a mod channel.""" +        mod_channel_check.return_value = True +        self.ctx.reply = AsyncMock() + +        self.assertEqual( +            await self.cog._clean_messages( +                self.ctx, +                None, +                first_limit=MockMessage(), +                attempt_delete_invocation=False, +            ), +            self.log_url, +        ) + +        self.ctx.reply.assert_awaited_once() +        sent_message = self.ctx.reply.await_args[0][0] +        self.assertIn(self.log_url, sent_message) +        self.assertIn("2 messages", sent_message) + +    @patch("bot.exts.moderation.clean.is_mod_channel") +    async def test_clean_send_success_message__to_mods_when_ran_in_non_mod_channel(self, mod_channel_check): +        """Clean command should send a confirmation message to #mods if invoked in a non-mod channel.""" +        mod_channel_check.return_value = False +        mocked_mods = MockTextChannel(id=1234567) +        mocked_mods.send = AsyncMock() +        self.bot.get_channel = MagicMock(return_value=mocked_mods) + +        self.assertEqual( +            await self.cog._clean_messages( +                self.ctx, +                None, +                first_limit=MockMessage(), +                attempt_delete_invocation=False, +            ), +            self.log_url, +        ) + +        mocked_mods.send.assert_awaited_once() +        sent_message = mocked_mods.send.await_args[0][0] +        self.assertIn(self.log_url, sent_message) +        self.assertIn("2 messages", sent_message) | 
