Exemple #1
0
    def setUp(self):
        """Adds the cog, a bot, and a message to the instance for usage in tests."""
        self.bot = MockBot()
        self.cog = TokenRemover(bot=self.bot)

        self.msg = MockMessage(id=555, content="hello world")
        self.msg.channel.mention = "#lemonade-stand"
        self.msg.guild.get_member.return_value.bot = False
        self.msg.guild.get_member.return_value.__str__.return_value = "Woody"
        self.msg.author.__str__ = MagicMock(return_value=self.msg.author.name)
        self.msg.author.avatar_url_as.return_value = "picture-lemon.png"
Exemple #2
0
def make_msg(author: str, newline_groups: List[int]) -> MockMessage:
    """Init a MockMessage instance with `author` and content configured by `newline_groups".

    Configure content by passing a list of ints, where each int `n` will generate
    a separate group of `n` newlines.

    Example:
        newline_groups=[3, 1, 2] -> content="\n\n\n \n \n\n"
    """
    content = " ".join("\n" * n for n in newline_groups)
    return MockMessage(author=author, content=content)
Exemple #3
0
    async def test_continue_eval_does_continue(self):
        """Test that the continue_eval function does continue if required conditions are met."""
        ctx = MockContext(message=MockMessage(add_reaction=AsyncMock(),
                                              clear_reactions=AsyncMock()))
        response = MockMessage(delete=AsyncMock())
        new_msg = MockMessage(content='!e NewCode')
        self.bot.wait_for.side_effect = ((None, new_msg), None)

        actual = await self.cog.continue_eval(ctx, response)
        self.assertEqual(actual, 'NewCode')
        self.bot.wait_for.has_calls(
            call('message_edit',
                 partial(snekbox.predicate_eval_message_edit, ctx),
                 timeout=10),
            call('reaction_add',
                 partial(snekbox.predicate_eval_emoji_reaction, ctx),
                 timeout=10))
        ctx.message.add_reaction.assert_called_once_with(snekbox.REEVAL_EMOJI)
        ctx.message.clear_reactions.assert_called_once()
        response.delete.assert_called_once()
Exemple #4
0
    def test_pop_removes_from_the_end(self):
        """Test if a pop removes the right-most message."""
        cache = MessageCache(maxlen=3)
        messages = [MockMessage() for _ in range(3)]

        for msg in messages:
            cache.append(msg)
        msg = cache.pop()

        self.assertEqual(msg, messages[-1])
        self.assertListEqual(messages[:-1], list(cache))
Exemple #5
0
    def test_indexing(self):
        """Test if the cache returns the correct messages by index."""
        cache = MessageCache(maxlen=5)
        messages = [MockMessage() for _ in range(5)]

        for msg in messages:
            cache.append(msg)

        for current_loop in range(-5, 5):
            with self.subTest(current_loop=current_loop):
                self.assertEqual(cache[current_loop], messages[current_loop])
Exemple #6
0
    async def test_process_event_no_archive_on_investigating(self):
        """Message is not archived on `Signal.INVESTIGATING`."""
        with patch("bot.exts.moderation.incidents.Incidents.archive",
                   AsyncMock()) as mocked_archive:
            await self.cog_instance.process_event(
                reaction=incidents.Signal.INVESTIGATING.value,
                incident=MockMessage(),
                member=MockMember(roles=[MockRole(id=1)]),
            )

        mocked_archive.assert_not_called()
Exemple #7
0
    def test_clear(self):
        """Test if a clear makes the cache empty."""
        cache = MessageCache(maxlen=5)
        messages = [MockMessage() for _ in range(3)]

        for msg in messages:
            cache.append(msg)
        cache.clear()

        self.assertListEqual(list(cache), [])
        self.assertEqual(len(cache), 0)
Exemple #8
0
    def test_popleft_removes_from_the_beginning(self):
        """Test if a popleft removes the left-most message."""
        cache = MessageCache(maxlen=3)
        messages = [MockMessage() for _ in range(3)]

        for msg in messages:
            cache.append(msg)
        msg = cache.popleft()

        self.assertEqual(msg, messages[0])
        self.assertListEqual(messages[1:], list(cache))
Exemple #9
0
    async def test_eval_command_evaluate_once(self):
        """Test the eval command procedure."""
        ctx = MockContext()
        response = MockMessage()
        self.cog.prepare_input = MagicMock(return_value='MyAwesomeFormattedCode')
        self.cog.send_eval = AsyncMock(return_value=response)
        self.cog.continue_eval = AsyncMock(return_value=None)

        await self.cog.eval_command(self.cog, ctx=ctx, code='MyAwesomeCode')
        self.cog.prepare_input.assert_called_once_with('MyAwesomeCode')
        self.cog.send_eval.assert_called_once_with(ctx, 'MyAwesomeFormattedCode')
        self.cog.continue_eval.assert_called_once_with(ctx, response)
Exemple #10
0
    async def test_archive_webhook_not_found(self):
        """
        Method recovers and returns False when the webhook is not found.

        Implicitly, this also tests that the error is handled internally and doesn't
        propagate out of the method, which is just as important.
        """
        self.cog_instance.bot.fetch_webhook = AsyncMock(side_effect=mock_404)
        self.assertFalse(await
                         self.cog_instance.archive(incident=MockMessage(),
                                                   outcome=MagicMock(),
                                                   actioned_by=MockMember()))
Exemple #11
0
    async def test_eval_command_evaluate_twice(self):
        """Test the eval and re-eval command procedure."""
        ctx = MockContext()
        response = MockMessage()
        self.cog.prepare_input = MagicMock(return_value='MyAwesomeFormattedCode')
        self.cog.send_eval = AsyncMock(return_value=response)
        self.cog.continue_eval = AsyncMock()
        self.cog.continue_eval.side_effect = ('MyAwesomeCode-2', None)

        await self.cog.eval_command(self.cog, ctx=ctx, code='MyAwesomeCode')
        self.cog.prepare_input.has_calls(call('MyAwesomeCode'), call('MyAwesomeCode-2'))
        self.cog.send_eval.assert_called_with(ctx, 'MyAwesomeFormattedCode')
        self.cog.continue_eval.assert_called_with(ctx, response)
Exemple #12
0
    async def test_process_event_confirmation_task_is_awaited(self):
        """Task given by `Incidents.make_confirmation_task` is awaited before method exits."""
        mock_task = AsyncMock()

        with patch(
                "bot.exts.moderation.incidents.Incidents.make_confirmation_task",
                mock_task):
            await self.cog_instance.process_event(
                reaction=incidents.Signal.ACTIONED.value,
                incident=MockMessage(id=123),
                member=MockMember(roles=[MockRole(id=1)]))

        mock_task.assert_awaited()
Exemple #13
0
 def setUp(self):
     """Sets up fresh objects for each test."""
     self.bot = MockBot()
     self.bot.filter_list_cache = {
         "FILE_FORMAT.True": {
             ".first": {},
             ".second": {},
             ".third": {},
         }
     }
     self.cog = antimalware.AntiMalware(self.bot)
     self.message = MockMessage()
     self.whitelist = [".first", ".second", ".third"]
Exemple #14
0
    def test_bad_index_raises_index_error(self):
        """Test if the cache raises IndexError for invalid indices."""
        cache = MessageCache(maxlen=5)
        messages = [MockMessage() for _ in range(3)]
        test_cases = (-10, -4, 3, 4, 5)

        for msg in messages:
            cache.append(msg)

        for current_loop in test_cases:
            with self.subTest(current_loop=current_loop):
                with self.assertRaises(IndexError):
                    cache[current_loop]
Exemple #15
0
    async def test_process_event_bad_emoji(self):
        """
        The reaction is removed when an invalid emoji is used.

        This requires that we pass in a `member` with valid roles, as we need the role check
        to succeed.
        """
        incident = MockMessage()
        member = MockMember(roles=[MockRole(id=1)])  # Member has allowed role

        await self.cog_instance.process_event("invalid_signal", incident,
                                              member)
        incident.remove_reaction.assert_called_once_with(
            "invalid_signal", member)
Exemple #16
0
    def test_predicate_eval_emoji_reaction(self):
        """Test the predicate_eval_emoji_reaction function."""
        valid_reaction = MockReaction(message=MockMessage(id=1))
        valid_reaction.__str__.return_value = snekbox.REEVAL_EMOJI
        valid_ctx = MockContext(message=MockMessage(id=1), author=MockUser(id=2))
        valid_user = MockUser(id=2)

        invalid_reaction_id = MockReaction(message=MockMessage(id=42))
        invalid_reaction_id.__str__.return_value = snekbox.REEVAL_EMOJI
        invalid_user_id = MockUser(id=42)
        invalid_reaction_str = MockReaction(message=MockMessage(id=1))
        invalid_reaction_str.__str__.return_value = ':longbeard:'

        cases = (
            (invalid_reaction_id, valid_user, False, 'invalid reaction ID'),
            (valid_reaction, invalid_user_id, False, 'invalid user ID'),
            (invalid_reaction_str, valid_user, False, 'invalid reaction __str__'),
            (valid_reaction, valid_user, True, 'matching attributes')
        )
        for reaction, user, expected, testname in cases:
            with self.subTest(msg=f'Test with {testname} and expected return {expected}'):
                actual = snekbox.predicate_eval_emoji_reaction(valid_ctx, reaction, user)
                self.assertEqual(actual, expected)
Exemple #17
0
    def test_format_userid_log_message_unknown(self, unknown_user_log_message):
        """Should correctly format the user ID portion when the actual user it belongs to is unknown."""
        token = Token("NDcyMjY1OTQzMDYyNDEzMzMy", "XsySD_",
                      "s45jqDV_Iisn-symw0yDRrk_jf4")
        unknown_user_log_message.format.return_value = " Partner"
        msg = MockMessage(id=555, content="hello world")
        msg.guild.get_member.return_value = None

        return_value = TokenRemover.format_userid_log_message(msg, token)

        self.assertEqual(return_value,
                         (unknown_user_log_message.format.return_value, False))
        unknown_user_log_message.format.assert_called_once_with(
            user_id=472265943062413332)
Exemple #18
0
    async def test_make_embed_with_attachment_succeeds(self):
        """Incident's attachment is downloaded and displayed in the embed's image field."""
        file = MagicMock(discord.File, filename="bigbadjoe.jpg")
        attachment = MockAttachment(filename="bigbadjoe.jpg")
        incident = MockMessage(content="this is an incident",
                               attachments=[attachment])

        # Patch `download_file` to return our `file`
        with patch("bot.exts.moderation.incidents.download_file",
                   AsyncMock(return_value=file)):
            embed, returned_file = await incidents.make_embed(
                incident, incidents.Signal.ACTIONED, MockMember())

        self.assertIs(file, returned_file)
        self.assertEqual("attachment://bigbadjoe.jpg", embed.image.url)
Exemple #19
0
    def setUp(self):
        """Adds the cog, a bot, and a message to the instance for usage in tests."""
        self.bot = MockBot()
        self.bot.get_cog.return_value = MagicMock()
        self.bot.get_cog.return_value.send_log_message = AsyncMock()
        self.cog = TokenRemover(bot=self.bot)

        self.msg = MockMessage(id=555, content='')
        self.msg.author.__str__ = MagicMock()
        self.msg.author.__str__.return_value = 'lemon'
        self.msg.author.bot = False
        self.msg.author.avatar_url_as.return_value = 'picture-lemon.png'
        self.msg.author.id = 42
        self.msg.author.mention = '@lemon'
        self.msg.channel.mention = "#lemonade-stand"
Exemple #20
0
    async def test_resolve_message_in_cache(self):
        """
        No API call is made if the queried message exists in the cache.

        We mock the `_get_message` return value regardless of input. Whether it finds the message
        internally is considered d.py's responsibility, not ours.
        """
        cached_message = MockMessage(id=123)
        self.cog_instance.bot._connection._get_message = MagicMock(
            return_value=cached_message)

        return_value = await self.cog_instance.resolve_message(123)

        self.assertIs(return_value, cached_message)
        self.cog_instance.bot.get_channel.assert_not_called(
        )  # The `fetch_message` line was never hit
Exemple #21
0
    async def test_process_event_no_delete_if_archive_fails(self):
        """
        Original message is not deleted when `Incidents.archive` returns False.

        This is the way of signaling that the relay failed, and we should not remove the original,
        as that would result in losing the incident record.
        """
        incident = MockMessage()

        with patch("bot.exts.moderation.incidents.Incidents.archive",
                   AsyncMock(return_value=False)):
            await self.cog_instance.process_event(
                reaction=incidents.Signal.ACTIONED.value,
                incident=incident,
                member=MockMember(roles=[MockRole(id=1)]))

        incident.delete.assert_not_called()
Exemple #22
0
    async def test_resolve_message_not_in_cache(self):
        """
        The message is retrieved from the API if it isn't cached.

        This is desired behaviour for messages which exist, but were sent before the bot's
        current session.
        """
        self.cog_instance.bot._connection._get_message = MagicMock(
            return_value=None)  # Cache returns None

        # API returns our message
        uncached_message = MockMessage()
        fetch_message = AsyncMock(return_value=uncached_message)
        self.cog_instance.bot.get_channel = MagicMock(
            return_value=MockTextChannel(fetch_message=fetch_message))

        retrieved_message = await self.cog_instance.resolve_message(123)
        self.assertIs(retrieved_message, uncached_message)
Exemple #23
0
    async def test_on_raw_reaction_add_message_is_not_an_incident(self):
        """
        The event won't be processed if the related message is not an incident.

        This is an edge-case that can happen if someone manually leaves a reaction
        on a pinned message, or a comment.

        We check this by asserting that `process_event` was never called.
        """
        self.cog_instance.process_event = AsyncMock()
        self.cog_instance.resolve_message = AsyncMock(
            return_value=MockMessage())

        with patch("bot.exts.moderation.incidents.is_incident",
                   MagicMock(return_value=False)):
            await self.cog_instance.on_raw_reaction_add(self.payload)

        self.cog_instance.process_event.assert_not_called()
Exemple #24
0
    async def test_make_embed_with_attachment_fails(self):
        """Incident's attachment fails to download, proxy url is linked instead."""
        attachment = MockAttachment(proxy_url="discord.com/bigbadjoe.jpg")
        incident = MockMessage(content="this is an incident",
                               attachments=[attachment])

        # Patch `download_file` to return None as if the download failed
        with patch("bot.exts.moderation.incidents.download_file",
                   AsyncMock(return_value=None)):
            embed, returned_file = await incidents.make_embed(
                incident, incidents.Signal.ACTIONED, MockMember())

        self.assertIsNone(returned_file)

        # The author name field is simply expected to have something in it, we do not assert the message
        self.assertGreater(len(embed.author.name), 0)
        self.assertEqual(embed.author.url, "discord.com/bigbadjoe.jpg"
                         )  # However, it should link the exact url
Exemple #25
0
    def test_format_userid_log_message_bot(self, known_user_log_message):
        """Should correctly format the user ID portion when the ID belongs to a known bot."""
        token = Token("NDcyMjY1OTQzMDYyNDEzMzMy", "XsySD_",
                      "s45jqDV_Iisn-symw0yDRrk_jf4")
        known_user_log_message.format.return_value = " Partner"
        msg = MockMessage(id=555, content="hello world")
        msg.guild.get_member.return_value.__str__.return_value = "Sam"
        msg.guild.get_member.return_value.bot = True

        return_value = TokenRemover.format_userid_log_message(msg, token)

        self.assertEqual(return_value,
                         (known_user_log_message.format.return_value, False))

        known_user_log_message.format.assert_called_once_with(
            user_id=472265943062413332,
            user_name="Sam",
            kind="BOT",
        )
Exemple #26
0
    async def test_send_eval_with_non_zero_eval(self):
        """Test the send_eval function with a code returning a non-zero code."""
        ctx = MockContext()
        ctx.message = MockMessage()
        ctx.send = AsyncMock()
        ctx.author.mention = '@LemonLemonishBeard#0042'
        self.cog.post_eval = AsyncMock(return_value={'stdout': 'ERROR', 'returncode': 127})
        self.cog.get_results_message = MagicMock(return_value=('Return code 127', 'Beard got stuck in the eval'))
        self.cog.get_status_emoji = MagicMock(return_value=':nope!:')
        self.cog.format_output = AsyncMock()  # This function isn't called

        await self.cog.send_eval(ctx, 'MyAwesomeCode')
        ctx.send.assert_called_once_with(
            '@LemonLemonishBeard#0042 :nope!: Return code 127.\n\n```py\nBeard got stuck in the eval\n```'
        )
        self.cog.post_eval.assert_called_once_with('MyAwesomeCode')
        self.cog.get_status_emoji.assert_called_once_with({'stdout': 'ERROR', 'returncode': 127})
        self.cog.get_results_message.assert_called_once_with({'stdout': 'ERROR', 'returncode': 127})
        self.cog.format_output.assert_not_called()
Exemple #27
0
    async def test_send_eval(self):
        """Test the send_eval function."""
        ctx = MockContext()
        ctx.message = MockMessage()
        ctx.send = AsyncMock()
        ctx.author.mention = '@LemonLemonishBeard#0042'

        self.cog.post_eval = AsyncMock(return_value={'stdout': '', 'returncode': 0})
        self.cog.get_results_message = MagicMock(return_value=('Return code 0', ''))
        self.cog.get_status_emoji = MagicMock(return_value=':yay!:')
        self.cog.format_output = AsyncMock(return_value=('[No output]', None))

        await self.cog.send_eval(ctx, 'MyAwesomeCode')
        ctx.send.assert_called_once_with(
            '@LemonLemonishBeard#0042 :yay!: Return code 0.\n\n```py\n[No output]\n```'
        )
        self.cog.post_eval.assert_called_once_with('MyAwesomeCode')
        self.cog.get_status_emoji.assert_called_once_with({'stdout': '', 'returncode': 0})
        self.cog.get_results_message.assert_called_once_with({'stdout': '', 'returncode': 0})
        self.cog.format_output.assert_called_once_with('')
Exemple #28
0
    def test_slicing_with_unfilled_cache(self):
        """Test if slicing returns the correct messages if the cache is not yet fully filled."""
        sizes = (5, 10, 55, 101)

        slices = (slice(None), slice(2, None), slice(None, 2),
                  slice(None, None, 2), slice(None, None, 3), slice(-1, 2),
                  slice(-1, 3000), slice(-3, -1), slice(-10, 3),
                  slice(-10, 4, 2), slice(None, None, -1), slice(None, 3, -2),
                  slice(None, None, -3), slice(-1, -10, -2), slice(-3, -7, -1))

        for size in sizes:
            cache = MessageCache(maxlen=size)
            messages = [MockMessage() for _ in range(size // 3 * 2)]

            for msg in messages:
                cache.append(msg)

            for slice_ in slices:
                with self.subTest(current_loop=(size, slice_)):
                    self.assertListEqual(cache[slice_], messages[slice_])
Exemple #29
0
    async def test_send_eval_with_paste_link(self):
        """Test the send_eval function with a too long output that generate a paste link."""
        ctx = MockContext()
        ctx.message = MockMessage()
        ctx.send = AsyncMock()
        ctx.author.mention = '@LemonLemonishBeard#0042'

        self.cog.post_eval = AsyncMock(return_value={'stdout': 'Way too long beard', 'returncode': 0})
        self.cog.get_results_message = MagicMock(return_value=('Return code 0', ''))
        self.cog.get_status_emoji = MagicMock(return_value=':yay!:')
        self.cog.format_output = AsyncMock(return_value=('Way too long beard', 'lookatmybeard.com'))

        await self.cog.send_eval(ctx, 'MyAwesomeCode')
        ctx.send.assert_called_once_with(
            '@LemonLemonishBeard#0042 :yay!: Return code 0.'
            '\n\n```py\nWay too long beard\n```\nFull output: lookatmybeard.com'
        )
        self.cog.post_eval.assert_called_once_with('MyAwesomeCode')
        self.cog.get_status_emoji.assert_called_once_with({'stdout': 'Way too long beard', 'returncode': 0})
        self.cog.get_results_message.assert_called_once_with({'stdout': 'Way too long beard', 'returncode': 0})
        self.cog.format_output.assert_called_once_with('Way too long beard')
Exemple #30
0
    def test_slicing_with_overfilled_cache(self):
        """Test if slicing returns the correct messages if the cache was appended with more messages it can contain."""
        sizes = (5, 10, 55, 101)

        slices = (slice(None), slice(2, None), slice(None, 2),
                  slice(None, None, 2), slice(None, None, 3), slice(-1, 2),
                  slice(-1, 3000), slice(-3, -1), slice(-10, 3),
                  slice(-10, 4, 2), slice(None, None, -1), slice(None, 3, -2),
                  slice(None, None, -3), slice(-1, -10, -2), slice(-3, -7, -1))

        for size in sizes:
            cache = MessageCache(maxlen=size)
            messages = [MockMessage() for _ in range(size * 3 // 2)]

            for msg in messages:
                cache.append(msg)
            messages = messages[size // 2:]

            for slice_ in slices:
                with self.subTest(current_loop=(size, slice_)):
                    self.assertListEqual(cache[slice_], messages[slice_])