diff options
| author | 2021-12-08 12:28:54 +0100 | |
|---|---|---|
| committer | 2021-12-08 12:28:54 +0100 | |
| commit | 6f275cf3299c2c423e2c6cd38a21dcc4416e35c6 (patch) | |
| tree | 006d64cf28d405c44b89d83057b65d7dbc68698e /tests | |
| parent | Correct typo in logline (diff) | |
| parent | Merge pull request #1994 from onerandomusername/fix-pep-whitespace (diff) | |
Merge branch 'main' into modpings-schedule
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/bot/exts/backend/test_error_handler.py | 32 | ||||
| -rw-r--r-- | tests/bot/exts/filters/test_filtering.py | 40 | ||||
| -rw-r--r-- | tests/bot/exts/info/test_information.py | 14 | ||||
| -rw-r--r-- | tests/bot/exts/moderation/test_incidents.py | 94 | ||||
| -rw-r--r-- | tests/bot/test_converters.py | 50 | ||||
| -rw-r--r-- | tests/bot/utils/test_time.py | 27 | 
6 files changed, 180 insertions, 77 deletions
| diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py index 462f718e6..d12329b1f 100644 --- a/tests/bot/exts/backend/test_error_handler.py +++ b/tests/bot/exts/backend/test_error_handler.py @@ -572,38 +572,6 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase):                  push_scope_mock.set_extra.has_calls(set_extra_calls) -class OtherErrorHandlerTests(unittest.IsolatedAsyncioTestCase): -    """Other `ErrorHandler` tests.""" - -    def setUp(self): -        self.bot = MockBot() -        self.ctx = MockContext() - -    async def test_get_help_command_command_specified(self): -        """Should return coroutine of help command of specified command.""" -        self.ctx.command = "foo" -        result = ErrorHandler.get_help_command(self.ctx) -        expected = self.ctx.send_help("foo") -        self.assertEqual(result.__qualname__, expected.__qualname__) -        self.assertEqual(result.cr_frame.f_locals, expected.cr_frame.f_locals) - -        # Await coroutines to avoid warnings -        await result -        await expected - -    async def test_get_help_command_no_command_specified(self): -        """Should return coroutine of help command.""" -        self.ctx.command = None -        result = ErrorHandler.get_help_command(self.ctx) -        expected = self.ctx.send_help() -        self.assertEqual(result.__qualname__, expected.__qualname__) -        self.assertEqual(result.cr_frame.f_locals, expected.cr_frame.f_locals) - -        # Await coroutines to avoid warnings -        await result -        await expected - -  class ErrorHandlerSetupTests(unittest.TestCase):      """Tests for `ErrorHandler` `setup` function.""" diff --git a/tests/bot/exts/filters/test_filtering.py b/tests/bot/exts/filters/test_filtering.py new file mode 100644 index 000000000..8ae59c1f1 --- /dev/null +++ b/tests/bot/exts/filters/test_filtering.py @@ -0,0 +1,40 @@ +import unittest +from unittest.mock import patch + +from bot.exts.filters import filtering +from tests.helpers import MockBot, autospec + + +class FilteringCogTests(unittest.IsolatedAsyncioTestCase): +    """Tests the `Filtering` cog.""" + +    def setUp(self): +        """Instantiate the bot and cog.""" +        self.bot = MockBot() +        with patch("bot.utils.scheduling.create_task", new=lambda task, **_: task.close()): +            self.cog = filtering.Filtering(self.bot) + +    @autospec(filtering.Filtering, "_get_filterlist_items", pass_mocks=False, return_value=["TOKEN"]) +    async def test_token_filter(self): +        """Ensure that a filter token is correctly detected in a message.""" +        messages = { +            "": False, +            "no matches": False, +            "TOKEN": True, + +            # See advisory https://github.com/python-discord/bot/security/advisories/GHSA-j8c3-8x46-8pp6 +            "https://google.com TOKEN": True, +            "https://google.com something else": False, +        } + +        for message, match in messages.items(): +            with self.subTest(input=message, match=match): +                result, _ = await self.cog._has_watch_regex_match(message) + +                self.assertEqual( +                    match, +                    bool(result), +                    msg=f"Hit was {'expected' if match else 'not expected'} for this input." +                ) +                if result: +                    self.assertEqual("TOKEN", result.group()) diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index 4b50c3fd9..632287322 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -42,7 +42,7 @@ class InformationCogTests(unittest.IsolatedAsyncioTestCase):          embed = kwargs.pop('embed')          self.assertEqual(embed.title, "Role information (Total 1 role)") -        self.assertEqual(embed.colour, discord.Colour.blurple()) +        self.assertEqual(embed.colour, discord.Colour.og_blurple())          self.assertEqual(embed.description, f"\n`{self.moderator_role.id}` - {self.moderator_role.mention}\n")      async def test_role_info_command(self): @@ -50,7 +50,7 @@ class InformationCogTests(unittest.IsolatedAsyncioTestCase):          dummy_role = helpers.MockRole(              name="Dummy",              id=112233445566778899, -            colour=discord.Colour.blurple(), +            colour=discord.Colour.og_blurple(),              position=10,              members=[self.ctx.author],              permissions=discord.Permissions(0) @@ -80,11 +80,11 @@ class InformationCogTests(unittest.IsolatedAsyncioTestCase):          admin_embed = admin_kwargs["embed"]          self.assertEqual(dummy_embed.title, "Dummy info") -        self.assertEqual(dummy_embed.colour, discord.Colour.blurple()) +        self.assertEqual(dummy_embed.colour, discord.Colour.og_blurple())          self.assertEqual(dummy_embed.fields[0].value, str(dummy_role.id))          self.assertEqual(dummy_embed.fields[1].value, f"#{dummy_role.colour.value:0>6x}") -        self.assertEqual(dummy_embed.fields[2].value, "0.65 0.64 242") +        self.assertEqual(dummy_embed.fields[2].value, "0.63 0.48 218")          self.assertEqual(dummy_embed.fields[3].value, "1")          self.assertEqual(dummy_embed.fields[4].value, "10")          self.assertEqual(dummy_embed.fields[5].value, "0") @@ -417,14 +417,14 @@ class UserEmbedTests(unittest.IsolatedAsyncioTestCase):          f"{COG_PATH}.basic_user_infraction_counts",          new=unittest.mock.AsyncMock(return_value=("Infractions", "basic infractions"))      ) -    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.""" +    async def test_create_user_embed_uses_og_blurple_colour_when_user_has_no_roles(self): +        """The embed should be created with the og blurple colour if the user has no assigned roles."""          ctx = helpers.MockContext()          user = helpers.MockMember(id=217, colour=discord.Colour.default())          embed = await self.cog.create_user_embed(ctx, user) -        self.assertEqual(embed.colour, discord.Colour.blurple()) +        self.assertEqual(embed.colour, discord.Colour.og_blurple())      @unittest.mock.patch(          f"{COG_PATH}.basic_user_infraction_counts", diff --git a/tests/bot/exts/moderation/test_incidents.py b/tests/bot/exts/moderation/test_incidents.py index ccc842050..cfe0c4b03 100644 --- a/tests/bot/exts/moderation/test_incidents.py +++ b/tests/bot/exts/moderation/test_incidents.py @@ -3,13 +3,16 @@ import enum  import logging  import typing as t  import unittest +from unittest import mock  from unittest.mock import AsyncMock, MagicMock, Mock, call, patch  import aiohttp  import discord +from async_rediscache import RedisSession  from bot.constants import Colours  from bot.exts.moderation import incidents +from bot.utils.messages import format_user  from tests.helpers import (      MockAsyncWebhook, MockAttachment, MockBot, MockMember, MockMessage, MockReaction, MockRole, MockTextChannel,      MockUser @@ -276,6 +279,22 @@ class TestIncidents(unittest.IsolatedAsyncioTestCase):      the instance as they wish.      """ +    session = None + +    async def flush(self): +        """Flush everything from the database to prevent carry-overs between tests.""" +        with await self.session.pool as connection: +            await connection.flushall() + +    async def asyncSetUp(self):  # noqa: N802 +        self.session = RedisSession(use_fakeredis=True) +        await self.session.connect() +        await self.flush() + +    async def asyncTearDown(self):  # noqa: N802 +        if self.session: +            await self.session.close() +      def setUp(self):          """          Prepare a fresh `Incidents` instance for each test. @@ -506,7 +525,7 @@ class TestProcessEvent(TestIncidents):          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(), +                incident=MockMessage(id=123),                  member=MockMember(roles=[MockRole(id=1)])              ) @@ -526,7 +545,7 @@ class TestProcessEvent(TestIncidents):              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(), +                    incident=MockMessage(id=123),                      member=MockMember(roles=[MockRole(id=1)])                  )          except asyncio.TimeoutError: @@ -761,3 +780,74 @@ class TestOnMessage(TestIncidents):              await self.cog_instance.on_message(MockMessage())          mock_add_signals.assert_not_called() + + +class TestMessageLinkEmbeds(TestIncidents): +    """Tests for `extract_message_links` coroutine.""" + +    async def test_shorten_text(self): +        """Test all cases of text shortening by mocking messages.""" +        tests = { +            "thisisasingleword"*10: "thisisasinglewordthisisasinglewordthisisasinglewor...", + +            "\n".join("Lets make a new line test".split()): "Lets\nmake\na...", + +            'Hello, World!' * 300: ( +                "Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!" +                "Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!" +                "Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!Hello, World!" +                "Hello, World!Hello, World!H..." +            ) +        } + +        for content, expected_conversion in tests.items(): +            with self.subTest(content=content, expected_conversion=expected_conversion): +                conversion = incidents.shorten_text(content) +                self.assertEqual(conversion, expected_conversion) + +    async def extract_and_form_message_link_embeds(self): +        """ +        Extract message links from a mocked message and form the message link embed. + +        Considers all types of message links, discord supports. +        """ +        self.guild_id_patcher = mock.patch("bot.exts.backend.sync._cog.constants.Guild.id", 5) +        self.guild_id = self.guild_id_patcher.start() + +        msg = MockMessage(id=555, content="Hello, World!" * 3000) +        msg.channel.mention = "#lemonade-stand" + +        msg_links = [ +            # Valid Message links +            f"https://discord.com/channels/{self.guild_id}/{msg.channel.discord_id}/{msg.discord_id}", +            f"http://canary.discord.com/channels/{self.guild_id}/{msg.channel.discord_id}/{msg.discord_id}", + +            # Invalid Message links +            f"https://discord.com/channels/{msg.channel.discord_id}/{msg.discord_id}", +            f"https://discord.com/channels/{self.guild_id}/{msg.channel.discord_id}000/{msg.discord_id}", +        ] + +        incident_msg = MockMessage( +            id=777, +            content=( +                f"I would like to report the following messages, " +                f"as they break our rules: \n{', '.join(msg_links)}" +            ) +        ) + +        with patch( +                "bot.exts.moderation.incidents.Incidents.extract_message_links", AsyncMock() +        ) as mock_extract_message_links: +            embeds = mock_extract_message_links(incident_msg) +            description = ( +                f"**Author:** {format_user(msg.author)}\n" +                f"**Channel:** {msg.channel.mention} ({msg.channel.category}/#{msg.channel.name})\n" +                f"**Content:** {('Hello, World!' * 3000)[:300] + '...'}\n" +            ) + +            # Check number of embeds returned with number of valid links +            self.assertEqual(len(embeds), 2) + +            # Check for the embed descriptions +            for embed in embeds: +                self.assertEqual(embed.description, description) diff --git a/tests/bot/test_converters.py b/tests/bot/test_converters.py index ef6c8e19e..988b3857b 100644 --- a/tests/bot/test_converters.py +++ b/tests/bot/test_converters.py @@ -1,6 +1,6 @@ -import datetime  import re  import unittest +from datetime import MAXYEAR, datetime, timezone  from unittest.mock import MagicMock, patch  from dateutil.relativedelta import relativedelta @@ -17,7 +17,7 @@ class ConverterTests(unittest.IsolatedAsyncioTestCase):          cls.context = MagicMock          cls.context.author = 'bob' -        cls.fixed_utc_now = datetime.datetime.fromisoformat('2019-01-01T00:00:00') +        cls.fixed_utc_now = datetime.fromisoformat('2019-01-01T00:00:00+00:00')      async def test_tag_name_converter_for_invalid(self):          """TagNameConverter should raise the correct exception for invalid tag names.""" @@ -111,7 +111,7 @@ class ConverterTests(unittest.IsolatedAsyncioTestCase):              expected_datetime = self.fixed_utc_now + relativedelta(**duration_dict)              with patch('bot.converters.datetime') as mock_datetime: -                mock_datetime.utcnow.return_value = self.fixed_utc_now +                mock_datetime.now.return_value = self.fixed_utc_now                  with self.subTest(duration=duration, duration_dict=duration_dict):                      converted_datetime = await converter.convert(self.context, duration) @@ -157,52 +157,53 @@ class ConverterTests(unittest.IsolatedAsyncioTestCase):      async def test_duration_converter_out_of_range(self, mock_datetime):          """Duration converter should raise BadArgument if datetime raises a ValueError."""          mock_datetime.__add__.side_effect = ValueError -        mock_datetime.utcnow.return_value = mock_datetime +        mock_datetime.now.return_value = mock_datetime -        duration = f"{datetime.MAXYEAR}y" +        duration = f"{MAXYEAR}y"          exception_message = f"`{duration}` results in a datetime outside the supported range."          with self.assertRaisesRegex(BadArgument, re.escape(exception_message)):              await Duration().convert(self.context, duration)      async def test_isodatetime_converter_for_valid(self):          """ISODateTime converter returns correct datetime for valid datetime string.""" +        utc = timezone.utc          test_values = (              # `YYYY-mm-ddTHH:MM:SSZ` | `YYYY-mm-dd HH:MM:SSZ` -            ('2019-09-02T02:03:05Z', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02 02:03:05Z', datetime.datetime(2019, 9, 2, 2, 3, 5)), +            ('2019-09-02T02:03:05Z', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02 02:03:05Z', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)),              # `YYYY-mm-ddTHH:MM:SS±HH:MM` | `YYYY-mm-dd HH:MM:SS±HH:MM` -            ('2019-09-02T03:18:05+01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02 03:18:05+01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02T00:48:05-01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02 00:48:05-01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), +            ('2019-09-02T03:18:05+01:15', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02 03:18:05+01:15', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02T00:48:05-01:15', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02 00:48:05-01:15', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)),              # `YYYY-mm-ddTHH:MM:SS±HHMM` | `YYYY-mm-dd HH:MM:SS±HHMM` -            ('2019-09-02T03:18:05+0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02 03:18:05+0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02T00:48:05-0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02 00:48:05-0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), +            ('2019-09-02T03:18:05+0115', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02 03:18:05+0115', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02T00:48:05-0115', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02 00:48:05-0115', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)),              # `YYYY-mm-ddTHH:MM:SS±HH` | `YYYY-mm-dd HH:MM:SS±HH` -            ('2019-09-02 03:03:05+01', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02T01:03:05-01', datetime.datetime(2019, 9, 2, 2, 3, 5)), +            ('2019-09-02 03:03:05+01', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02T01:03:05-01', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)),              # `YYYY-mm-ddTHH:MM:SS` | `YYYY-mm-dd HH:MM:SS` -            ('2019-09-02T02:03:05', datetime.datetime(2019, 9, 2, 2, 3, 5)), -            ('2019-09-02 02:03:05', datetime.datetime(2019, 9, 2, 2, 3, 5)), +            ('2019-09-02T02:03:05', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)), +            ('2019-09-02 02:03:05', datetime(2019, 9, 2, 2, 3, 5, tzinfo=utc)),              # `YYYY-mm-ddTHH:MM` | `YYYY-mm-dd HH:MM` -            ('2019-11-12T09:15', datetime.datetime(2019, 11, 12, 9, 15)), -            ('2019-11-12 09:15', datetime.datetime(2019, 11, 12, 9, 15)), +            ('2019-11-12T09:15', datetime(2019, 11, 12, 9, 15, tzinfo=utc)), +            ('2019-11-12 09:15', datetime(2019, 11, 12, 9, 15, tzinfo=utc)),              # `YYYY-mm-dd` -            ('2019-04-01', datetime.datetime(2019, 4, 1)), +            ('2019-04-01', datetime(2019, 4, 1, tzinfo=utc)),              # `YYYY-mm` -            ('2019-02-01', datetime.datetime(2019, 2, 1)), +            ('2019-02-01', datetime(2019, 2, 1, tzinfo=utc)),              # `YYYY` -            ('2025', datetime.datetime(2025, 1, 1)), +            ('2025', datetime(2025, 1, 1, tzinfo=utc)),          )          converter = ISODateTime() @@ -210,7 +211,6 @@ class ConverterTests(unittest.IsolatedAsyncioTestCase):          for datetime_string, expected_dt in test_values:              with self.subTest(datetime_string=datetime_string, expected_dt=expected_dt):                  converted_dt = await converter.convert(self.context, datetime_string) -                self.assertIsNone(converted_dt.tzinfo)                  self.assertEqual(converted_dt, expected_dt)      async def test_isodatetime_converter_for_invalid(self): diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 8edffd1c9..a3dcbfc0a 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -72,9 +72,9 @@ class TimeTests(unittest.TestCase):      def test_format_infraction_with_duration_custom_units(self):          """format_infraction_with_duration should work for custom max_units."""          test_cases = ( -            ('3000-12-12T00:01:00Z', datetime(3000, 12, 11, 12, 5, 5), 6, +            ('3000-12-12T00:01:00Z', datetime(3000, 12, 11, 12, 5, 5, tzinfo=timezone.utc), 6,               '<t:32533488060:f> (11 hours, 55 minutes and 55 seconds)'), -            ('3000-11-23T20:09:00Z', datetime(3000, 4, 25, 20, 15), 20, +            ('3000-11-23T20:09:00Z', datetime(3000, 4, 25, 20, 15, tzinfo=timezone.utc), 20,               '<t:32531918940:f> (6 months, 28 days, 23 hours and 54 minutes)')          ) @@ -84,16 +84,21 @@ class TimeTests(unittest.TestCase):      def test_format_infraction_with_duration_normal_usage(self):          """format_infraction_with_duration should work for normal usage, across various durations.""" +        utc = timezone.utc          test_cases = ( -            ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 2, '<t:1576108860:f> (12 hours and 55 seconds)'), -            ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 1, '<t:1576108860:f> (12 hours)'), -            ('2019-12-12T00:00:00Z', datetime(2019, 12, 11, 23, 59), 2, '<t:1576108800:f> (1 minute)'), -            ('2019-11-23T20:09:00Z', datetime(2019, 11, 15, 20, 15), 2, '<t:1574539740:f> (7 days and 23 hours)'), -            ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 2, '<t:1574539740:f> (6 months and 28 days)'), -            ('2019-11-23T20:58:00Z', datetime(2019, 11, 23, 20, 53), 2, '<t:1574542680:f> (5 minutes)'), -            ('2019-11-24T00:00:00Z', datetime(2019, 11, 23, 23, 59, 0), 2, '<t:1574553600:f> (1 minute)'), -            ('2019-11-23T23:59:00Z', datetime(2017, 7, 21, 23, 0), 2, '<t:1574553540:f> (2 years and 4 months)'), -            ('2019-11-23T23:59:00Z', datetime(2019, 11, 23, 23, 49, 5), 2, +            ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5, tzinfo=utc), 2, +                '<t:1576108860:f> (12 hours and 55 seconds)'), +            ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5, tzinfo=utc), 1, '<t:1576108860:f> (12 hours)'), +            ('2019-12-12T00:00:00Z', datetime(2019, 12, 11, 23, 59, tzinfo=utc), 2, '<t:1576108800:f> (1 minute)'), +            ('2019-11-23T20:09:00Z', datetime(2019, 11, 15, 20, 15, tzinfo=utc), 2, +                '<t:1574539740:f> (7 days and 23 hours)'), +            ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15, tzinfo=utc), 2, +                '<t:1574539740:f> (6 months and 28 days)'), +            ('2019-11-23T20:58:00Z', datetime(2019, 11, 23, 20, 53, tzinfo=utc), 2, '<t:1574542680:f> (5 minutes)'), +            ('2019-11-24T00:00:00Z', datetime(2019, 11, 23, 23, 59, 0, tzinfo=utc), 2, '<t:1574553600:f> (1 minute)'), +            ('2019-11-23T23:59:00Z', datetime(2017, 7, 21, 23, 0, tzinfo=utc), 2, +             '<t:1574553540:f> (2 years and 4 months)'), +            ('2019-11-23T23:59:00Z', datetime(2019, 11, 23, 23, 49, 5, tzinfo=utc), 2,               '<t:1574553540:f> (9 minutes and 55 seconds)'),              (None, datetime(2019, 11, 23, 23, 49, 5), 2, None),          ) | 
