From 4890cc5ba43ad73229ce4d2fe240acaf39194edb Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Tue, 14 Apr 2020 08:30:18 +0300 Subject: Created tests for `bot.cogs.logging` connected message. --- tests/bot/cogs/test_logging.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/bot/cogs/test_logging.py (limited to 'tests') diff --git a/tests/bot/cogs/test_logging.py b/tests/bot/cogs/test_logging.py new file mode 100644 index 000000000..ba98a5a56 --- /dev/null +++ b/tests/bot/cogs/test_logging.py @@ -0,0 +1,42 @@ +import unittest +from unittest.mock import patch + +from bot import constants +from bot.cogs.logging import Logging +from tests.helpers import MockBot, MockTextChannel + + +class LoggingTests(unittest.IsolatedAsyncioTestCase): + """Test cases for connected login.""" + + def setUp(self): + self.bot = MockBot() + self.cog = Logging(self.bot) + self.dev_log = MockTextChannel(id=1234, name="dev-log") + + @patch("bot.cogs.logging.DEBUG_MODE", False) + async def test_debug_mode_false(self): + """Should send connected message to dev-log.""" + self.bot.get_channel.return_value = self.dev_log + + await self.cog.startup_greeting() + self.bot.wait_until_guild_available.assert_awaited_once_with() + self.bot.get_channel.assert_called_once_with(constants.Channels.dev_log) + + embed = self.dev_log.send.call_args[1]['embed'] + self.dev_log.send.assert_awaited_once_with(embed=embed) + + self.assertEqual(embed.description, "Connected!") + self.assertEqual(embed.author.name, "Python Bot") + self.assertEqual(embed.author.url, "https://github.com/python-discord/bot") + self.assertEqual( + embed.author.icon_url, + "https://raw.githubusercontent.com/python-discord/branding/master/logos/logo_circle/logo_circle_large.png" + ) + + @patch("bot.cogs.logging.DEBUG_MODE", True) + async def test_debug_mode_true(self): + """Should not send anything to dev-log.""" + await self.cog.startup_greeting() + self.bot.wait_until_guild_available.assert_awaited_once_with() + self.bot.get_channel.assert_not_called() -- cgit v1.2.3 From ededd1879cfb914445342b202d4c66aed23ee94b Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Fri, 22 May 2020 08:43:10 +0300 Subject: Logging Tests: Simplify `DEBUG_MODE` `False` test - Remove embed attributes checks - Replace `self.dev_log.assert_awaited_once_with` with `self.dev_log.assert_awaited_once`. --- tests/bot/cogs/test_logging.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'tests') diff --git a/tests/bot/cogs/test_logging.py b/tests/bot/cogs/test_logging.py index ba98a5a56..8a18fdcd6 100644 --- a/tests/bot/cogs/test_logging.py +++ b/tests/bot/cogs/test_logging.py @@ -22,17 +22,7 @@ class LoggingTests(unittest.IsolatedAsyncioTestCase): await self.cog.startup_greeting() self.bot.wait_until_guild_available.assert_awaited_once_with() self.bot.get_channel.assert_called_once_with(constants.Channels.dev_log) - - embed = self.dev_log.send.call_args[1]['embed'] - self.dev_log.send.assert_awaited_once_with(embed=embed) - - self.assertEqual(embed.description, "Connected!") - self.assertEqual(embed.author.name, "Python Bot") - self.assertEqual(embed.author.url, "https://github.com/python-discord/bot") - self.assertEqual( - embed.author.icon_url, - "https://raw.githubusercontent.com/python-discord/branding/master/logos/logo_circle/logo_circle_large.png" - ) + self.dev_log.send.assert_awaited_once() @patch("bot.cogs.logging.DEBUG_MODE", True) async def test_debug_mode_true(self): -- cgit v1.2.3 From 5f5a51b1715228ac5b401ef6bed8a83491e313de Mon Sep 17 00:00:00 2001 From: Kyle Stanley Date: Thu, 4 Jun 2020 03:17:11 -0400 Subject: Improve LinePaginator to support long lines --- bot/cogs/moderation/management.py | 8 ++--- bot/pagination.py | 66 +++++++++++++++++++++++++++++++++++---- tests/bot/test_pagination.py | 41 +++++++++++++++++++----- 3 files changed, 98 insertions(+), 17 deletions(-) (limited to 'tests') diff --git a/bot/cogs/moderation/management.py b/bot/cogs/moderation/management.py index 250a24247..ad17a90b0 100644 --- a/bot/cogs/moderation/management.py +++ b/bot/cogs/moderation/management.py @@ -83,14 +83,14 @@ class ModManagement(commands.Cog): "actor__id": ctx.author.id, "ordering": "-inserted_at" } - infractions = await self.bot.api_client.get(f"bot/infractions", params=params) + infractions = await self.bot.api_client.get("bot/infractions", params=params) if infractions: old_infraction = infractions[0] infraction_id = old_infraction["id"] else: await ctx.send( - f":x: Couldn't find most recent infraction; you have never given an infraction." + ":x: Couldn't find most recent infraction; you have never given an infraction." ) return else: @@ -224,7 +224,7 @@ class ModManagement(commands.Cog): ) -> None: """Send a paginated embed of infractions for the specified user.""" if not infractions: - await ctx.send(f":warning: No infractions could be found for that query.") + await ctx.send(":warning: No infractions could be found for that query.") return lines = tuple( @@ -268,12 +268,12 @@ class ModManagement(commands.Cog): User: {self.bot.get_user(user_id)} (`{user_id}`) Type: **{infraction["type"]}** Shadow: {hidden} - Reason: {infraction["reason"] or "*None*"} Created: {created} Expires: {expires} Remaining: {remaining} Actor: {actor.mention if actor else actor_id} ID: `{infraction["id"]}` + Reason: {infraction["reason"] or "*None*"} {"**===============**" if active else "==============="} """) diff --git a/bot/pagination.py b/bot/pagination.py index 90c8f849c..5c7be564d 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -37,12 +37,19 @@ class LinePaginator(Paginator): The suffix appended at the end of every page. e.g. three backticks. * max_size: `int` The maximum amount of codepoints allowed in a page. + * scale_to_size: `int` + The maximum amount of characters a single line can scale up to. * max_lines: `int` The maximum amount of lines allowed in a page. """ def __init__( - self, prefix: str = '```', suffix: str = '```', max_size: int = 2000, max_lines: int = None + self, + prefix: str = '```', + suffix: str = '```', + max_size: int = 2000, + scale_to_size: int = 2000, + max_lines: t.Optional[int] = None ) -> None: """ This function overrides the Paginator.__init__ from inside discord.ext.commands. @@ -52,6 +59,10 @@ class LinePaginator(Paginator): self.prefix = prefix self.suffix = suffix self.max_size = max_size - len(suffix) + if scale_to_size < max_size: + raise ValueError("scale_to_size must be >= max_size.") + + self.scale_to_size = scale_to_size self.max_lines = max_lines self._current_page = [prefix] self._linecount = 0 @@ -62,14 +73,26 @@ class LinePaginator(Paginator): """ Adds a line to the current page. - If the line exceeds the `self.max_size` then an exception is raised. + If the line exceeds `self.max_size`, then `self.max_size` will go up to `scale_to_size` for + a single line before creating a new page. If it is still exceeded, the excess characters + are stored and placed on the next pages until there are none remaining (by word boundary). + + Raises a RuntimeError if `self.max_size` is still exceeded after attempting to continue + onto the next page. This function overrides the `Paginator.add_line` from inside `discord.ext.commands`. It overrides in order to allow us to configure the maximum number of lines per page. """ - if len(line) > self.max_size - len(self.prefix) - 2: - raise RuntimeError('Line exceeds maximum page size %s' % (self.max_size - len(self.prefix) - 2)) + remaining_words = None + if len(line) > (max_chars := self.max_size - len(self.prefix) - 2): + if len(line) > self.scale_to_size: + line, remaining_words = self._split_remaining_words(line, max_chars) + # If line still exceeds scale_to_size, we were unable to split into a second + # page without truncating. + if len(line) > self.scale_to_size: + raise RuntimeError(f'Line exceeds maximum scale_to_size {self.scale_to_size}' + ' and could not be split.') if self.max_lines is not None: if self._linecount >= self.max_lines: @@ -87,6 +110,36 @@ class LinePaginator(Paginator): self._current_page.append('') self._count += 1 + if remaining_words: + self.add_line(remaining_words) + + def _split_remaining_words(self, line: str, max_chars: int) -> t.Tuple[str, t.Optional[str]]: + """Internal: split a line into two strings; one that fits within *max_chars* characters + (reduced_words) and another for the remaining (remaining_words), rounding down to the + nearest word. + + Return a tuple in the format (reduced_words, remaining_words). + """ + reduced_words = [] + # "(Continued)" is used on a line by itself to indicate the continuation of last page + remaining_words = ["(Continued)\n", "---------------\n"] + reduced_char_count = 0 + is_full = False + + for word in line.split(" "): + if not is_full: + if len(word) + reduced_char_count <= max_chars: + reduced_words.append(word) + reduced_char_count += len(word) + else: + is_full = True + remaining_words.append(word) + else: + remaining_words.append(word) + + return " ".join(reduced_words), " ".join(remaining_words) if len(remaining_words) > 2 \ + else None + @classmethod async def paginate( cls, @@ -97,6 +150,7 @@ class LinePaginator(Paginator): suffix: str = "", max_lines: t.Optional[int] = None, max_size: int = 500, + scale_to_size: int = 2000, empty: bool = True, restrict_to_user: User = None, timeout: int = 300, @@ -147,7 +201,7 @@ class LinePaginator(Paginator): if not lines: if exception_on_empty_embed: - log.exception(f"Pagination asked for empty lines iterable") + log.exception("Pagination asked for empty lines iterable") raise EmptyPaginatorEmbed("No lines to paginate") log.debug("No lines to add to paginator, adding '(nothing to display)' message") @@ -357,7 +411,7 @@ class ImagePaginator(Paginator): if not pages: if exception_on_empty_embed: - log.exception(f"Pagination asked for empty image list") + log.exception("Pagination asked for empty image list") raise EmptyPaginatorEmbed("No images to paginate") log.debug("No images to add to paginator, adding '(no images to display)' message") diff --git a/tests/bot/test_pagination.py b/tests/bot/test_pagination.py index 0a734b505..f2e2c27ce 100644 --- a/tests/bot/test_pagination.py +++ b/tests/bot/test_pagination.py @@ -8,17 +8,44 @@ class LinePaginatorTests(TestCase): def setUp(self): """Create a paginator for the test method.""" - self.paginator = pagination.LinePaginator(prefix='', suffix='', max_size=30) - - def test_add_line_raises_on_too_long_lines(self): - """`add_line` should raise a `RuntimeError` for too long lines.""" - message = f"Line exceeds maximum page size {self.paginator.max_size - 2}" - with self.assertRaises(RuntimeError, msg=message): - self.paginator.add_line('x' * self.paginator.max_size) + self.paginator = pagination.LinePaginator(prefix='', suffix='', max_size=30, + scale_to_size=50) def test_add_line_works_on_small_lines(self): """`add_line` should allow small lines to be added.""" self.paginator.add_line('x' * (self.paginator.max_size - 3)) + # Note that the page isn't added to _pages until it's full. + self.assertEqual(len(self.paginator._pages), 0) + + def test_add_line_works_on_long_lines(self): + """`add_line` should scale long lines up to `scale_to_size`.""" + self.paginator.add_line('x' * self.paginator.scale_to_size) + self.assertEqual(len(self.paginator._pages), 1) + + # Any additional lines should start a new page after `max_size` is exceeded. + self.paginator.add_line('x') + self.assertEqual(len(self.paginator._pages), 2) + + def test_add_line_continuation(self): + """When `scale_to_size` is exceeded, remaining words should be split onto the next page.""" + self.paginator.add_line('zyz ' * (self.paginator.scale_to_size//4 + 1)) + self.assertEqual(len(self.paginator._pages), 2) + + def test_add_line_no_continuation(self): + """If adding a new line to an existing page would exceed `max_size`, it should start a new + page rather than using continuation. + """ + self.paginator.add_line('z' * (self.paginator.max_size - 3)) + self.paginator.add_line('z') + self.assertEqual(len(self.paginator._pages), 1) + + def test_add_line_raises_on_very_long_words(self): + """`add_line` should raise if a single long word is added that exceeds `scale_to_size`. + + Note: truncation is also a potential option, but this should not occur from normal usage. + """ + with self.assertRaises(RuntimeError): + self.paginator.add_line('x' * (self.paginator.scale_to_size + 1)) class ImagePaginatorTests(TestCase): -- cgit v1.2.3 From 81e50cb2c970fc5c203e135434f897b6a3f7e52a Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sun, 14 Jun 2020 23:34:28 -0700 Subject: Sync tests: test listeners ignore events from other guilds --- tests/bot/cogs/sync/test_cog.py | 64 ++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/bot/cogs/sync/test_cog.py b/tests/bot/cogs/sync/test_cog.py index 14fd909c4..d7d60e961 100644 --- a/tests/bot/cogs/sync/test_cog.py +++ b/tests/bot/cogs/sync/test_cog.py @@ -131,6 +131,12 @@ class SyncCogListenerTests(SyncCogTestCase): super().setUp() self.cog.patch_user = mock.AsyncMock(spec_set=self.cog.patch_user) + self.guild_id_patcher = mock.patch("bot.cogs.sync.cog.constants.Guild.id", 5) + self.guild_id = self.guild_id_patcher.start() + + def tearDown(self): + self.guild_id_patcher.stop() + async def test_sync_cog_on_guild_role_create(self): """A POST request should be sent with the new role's data.""" self.assertTrue(self.cog.on_guild_role_create.__cog_listener__) @@ -142,20 +148,32 @@ class SyncCogListenerTests(SyncCogTestCase): "permissions": 8, "position": 23, } - role = helpers.MockRole(**role_data) + role = helpers.MockRole(**role_data, guild=self.guild_id) await self.cog.on_guild_role_create(role) self.bot.api_client.post.assert_called_once_with("bot/roles", json=role_data) + async def test_sync_cog_on_guild_role_create_ignores_guilds(self): + """Events from other guilds should be ignored.""" + role = helpers.MockRole(guild=0) + await self.cog.on_guild_role_create(role) + self.bot.api_client.post.assert_not_awaited() + async def test_sync_cog_on_guild_role_delete(self): """A DELETE request should be sent.""" self.assertTrue(self.cog.on_guild_role_delete.__cog_listener__) - role = helpers.MockRole(id=99) + role = helpers.MockRole(id=99, guild=self.guild_id) await self.cog.on_guild_role_delete(role) self.bot.api_client.delete.assert_called_once_with("bot/roles/99") + async def test_sync_cog_on_guild_role_delete_ignores_guilds(self): + """Events from other guilds should be ignored.""" + role = helpers.MockRole(guild=0) + await self.cog.on_guild_role_delete(role) + self.bot.api_client.delete.assert_not_awaited() + async def test_sync_cog_on_guild_role_update(self): """A PUT request should be sent if the colour, name, permissions, or position changes.""" self.assertTrue(self.cog.on_guild_role_update.__cog_listener__) @@ -180,8 +198,8 @@ class SyncCogListenerTests(SyncCogTestCase): after_role_data = role_data.copy() after_role_data[attribute] = 876 - before_role = helpers.MockRole(**role_data) - after_role = helpers.MockRole(**after_role_data) + before_role = helpers.MockRole(**role_data, guild=self.guild_id) + after_role = helpers.MockRole(**after_role_data, guild=self.guild_id) await self.cog.on_guild_role_update(before_role, after_role) @@ -193,11 +211,17 @@ class SyncCogListenerTests(SyncCogTestCase): else: self.bot.api_client.put.assert_not_called() + async def test_sync_cog_on_guild_role_update_ignores_guilds(self): + """Events from other guilds should be ignored.""" + role = helpers.MockRole(guild=0) + await self.cog.on_guild_role_update(role, role) + self.bot.api_client.put.assert_not_awaited() + async def test_sync_cog_on_member_remove(self): - """Member should patched to set in_guild as False.""" + """Member should be patched to set in_guild as False.""" self.assertTrue(self.cog.on_member_remove.__cog_listener__) - member = helpers.MockMember() + member = helpers.MockMember(guild=self.guild_id) await self.cog.on_member_remove(member) self.cog.patch_user.assert_called_once_with( @@ -205,14 +229,20 @@ class SyncCogListenerTests(SyncCogTestCase): updated_information={"in_guild": False} ) + async def test_sync_cog_on_member_remove_ignores_guilds(self): + """Events from other guilds should be ignored.""" + member = helpers.MockMember(guild=0) + await self.cog.on_member_remove(member) + self.cog.patch_user.assert_not_awaited() + 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) - after_member = helpers.MockMember(roles=before_roles[1:]) + before_member = helpers.MockMember(roles=before_roles, guild=self.guild_id) + after_member = helpers.MockMember(roles=before_roles[1:], guild=self.guild_id) await self.cog.on_member_update(before_member, after_member) @@ -233,13 +263,19 @@ class SyncCogListenerTests(SyncCogTestCase): with self.subTest(attribute=attribute): self.cog.patch_user.reset_mock() - before_member = helpers.MockMember(**{attribute: old_value}) - after_member = helpers.MockMember(**{attribute: new_value}) + before_member = helpers.MockMember(**{attribute: old_value}, guild=self.guild_id) + after_member = helpers.MockMember(**{attribute: new_value}, guild=self.guild_id) await self.cog.on_member_update(before_member, after_member) self.cog.patch_user.assert_not_called() + async def test_sync_cog_on_member_update_ignores_guilds(self): + """Events from other guilds should be ignored.""" + member = helpers.MockMember(guild=0) + await self.cog.on_member_update(member, member) + self.cog.patch_user.assert_not_awaited() + async def test_sync_cog_on_user_update(self): """A user should be patched only if the name, discriminator, or avatar changes.""" self.assertTrue(self.cog.on_user_update.__cog_listener__) @@ -290,6 +326,7 @@ class SyncCogListenerTests(SyncCogTestCase): member = helpers.MockMember( discriminator="1234", roles=[helpers.MockRole(id=22), helpers.MockRole(id=12)], + guild=self.guild_id, ) data = { @@ -334,6 +371,13 @@ class SyncCogListenerTests(SyncCogTestCase): self.bot.api_client.post.assert_not_called() + async def test_sync_cog_on_member_join_ignores_guilds(self): + """Events from other guilds should be ignored.""" + member = helpers.MockMember(guild=0) + await self.cog.on_member_join(member) + self.bot.api_client.post.assert_not_awaited() + self.bot.api_client.put.assert_not_awaited() + class SyncCogCommandTests(SyncCogTestCase, CommandTestCase): """Tests for the commands in the Sync cog.""" -- cgit v1.2.3 From 4d6acdf32a323de8b88fed464358d70faf35c9d1 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sun, 14 Jun 2020 23:47:40 -0700 Subject: Sync: ignore 404s in on_user_update 404s probably mean the user is from another guild. --- bot/cogs/sync/cog.py | 14 ++++++++------ tests/bot/cogs/sync/test_cog.py | 17 ++++++++++------- 2 files changed, 18 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/bot/cogs/sync/cog.py b/bot/cogs/sync/cog.py index 97ea31ba5..578cccfc9 100644 --- a/bot/cogs/sync/cog.py +++ b/bot/cogs/sync/cog.py @@ -34,14 +34,15 @@ class Sync(Cog): for syncer in (self.role_syncer, self.user_syncer): await syncer.sync(guild) - async def patch_user(self, user_id: int, updated_information: Dict[str, Any]) -> None: + async def patch_user(self, user_id: int, json: Dict[str, Any], ignore_404: bool = False) -> None: """Send a PATCH request to partially update a user in the database.""" try: - await self.bot.api_client.patch(f"bot/users/{user_id}", json=updated_information) + await self.bot.api_client.patch(f"bot/users/{user_id}", json=json) except ResponseCodeError as e: if e.response.status != 404: raise - log.warning("Unable to update user, got 404. Assuming race condition from join event.") + if not ignore_404: + log.warning("Unable to update user, got 404. Assuming race condition from join event.") @Cog.listener() async def on_guild_role_create(self, role: Role) -> None: @@ -137,7 +138,7 @@ class Sync(Cog): if member.guild != constants.Guild.id: return - await self.patch_user(member.id, updated_information={"in_guild": False}) + await self.patch_user(member.id, json={"in_guild": False}) @Cog.listener() async def on_member_update(self, before: Member, after: Member) -> None: @@ -147,7 +148,7 @@ class Sync(Cog): if before.roles != after.roles: updated_information = {"roles": sorted(role.id for role in after.roles)} - await self.patch_user(after.id, updated_information=updated_information) + await self.patch_user(after.id, json=updated_information) @Cog.listener() async def on_user_update(self, before: User, after: User) -> None: @@ -158,7 +159,8 @@ class Sync(Cog): "name": after.name, "discriminator": int(after.discriminator), } - await self.patch_user(after.id, updated_information=updated_information) + # A 404 likely means the user is in another guild. + await self.patch_user(after.id, json=updated_information, ignore_404=True) @commands.group(name='sync') @commands.has_permissions(administrator=True) diff --git a/tests/bot/cogs/sync/test_cog.py b/tests/bot/cogs/sync/test_cog.py index d7d60e961..e5be14391 100644 --- a/tests/bot/cogs/sync/test_cog.py +++ b/tests/bot/cogs/sync/test_cog.py @@ -226,7 +226,7 @@ class SyncCogListenerTests(SyncCogTestCase): self.cog.patch_user.assert_called_once_with( member.id, - updated_information={"in_guild": False} + json={"in_guild": False} ) async def test_sync_cog_on_member_remove_ignores_guilds(self): @@ -247,7 +247,7 @@ class SyncCogListenerTests(SyncCogTestCase): 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, updated_information=data) + self.cog.patch_user.assert_called_once_with(after_member.id, json=data) async def test_sync_cog_on_member_update_other(self): """Members should not be patched if other attributes have changed.""" @@ -308,12 +308,15 @@ class SyncCogListenerTests(SyncCogTestCase): # Don't care if *all* keys are present; only the changed one is required call_args = self.cog.patch_user.call_args - self.assertEqual(call_args[0][0], after_user.id) - self.assertIn("updated_information", call_args[1]) + self.assertEqual(call_args.args[0], after_user.id) + self.assertIn("json", call_args.kwargs) - updated_information = call_args[1]["updated_information"] - self.assertIn(api_field, updated_information) - self.assertEqual(updated_information[api_field], api_value) + self.assertIn("ignore_404", call_args.kwargs) + self.assertTrue(call_args.kwargs["ignore_404"]) + + json = call_args.kwargs["json"] + self.assertIn(api_field, json) + self.assertEqual(json[api_field], api_value) else: self.cog.patch_user.assert_not_called() -- cgit v1.2.3 From ebc0eae42c1da67f61f040a67bc1b70e53a6f97e Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 17 Jun 2020 16:46:43 -0700 Subject: Sync: fix guild ID check Need to compare the IDs against each other rather than the Guild object against the ID. --- bot/cogs/sync/cog.py | 12 ++++++------ tests/bot/cogs/sync/test_cog.py | 35 +++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 22 deletions(-) (limited to 'tests') diff --git a/bot/cogs/sync/cog.py b/bot/cogs/sync/cog.py index 578cccfc9..5ace957e7 100644 --- a/bot/cogs/sync/cog.py +++ b/bot/cogs/sync/cog.py @@ -47,7 +47,7 @@ class Sync(Cog): @Cog.listener() async def on_guild_role_create(self, role: Role) -> None: """Adds newly create role to the database table over the API.""" - if role.guild != constants.Guild.id: + if role.guild.id != constants.Guild.id: return await self.bot.api_client.post( @@ -64,7 +64,7 @@ class Sync(Cog): @Cog.listener() async def on_guild_role_delete(self, role: Role) -> None: """Deletes role from the database when it's deleted from the guild.""" - if role.guild != constants.Guild.id: + if role.guild.id != constants.Guild.id: return await self.bot.api_client.delete(f'bot/roles/{role.id}') @@ -72,7 +72,7 @@ class Sync(Cog): @Cog.listener() async def on_guild_role_update(self, before: Role, after: Role) -> None: """Syncs role with the database if any of the stored attributes were updated.""" - if after.guild != constants.Guild.id: + if after.guild.id != constants.Guild.id: return was_updated = ( @@ -103,7 +103,7 @@ class Sync(Cog): previously left), it will update the user's information. If the user is not yet known by the database, the user is added. """ - if member.guild != constants.Guild.id: + if member.guild.id != constants.Guild.id: return packed = { @@ -135,7 +135,7 @@ class Sync(Cog): @Cog.listener() async def on_member_remove(self, member: Member) -> None: """Set the in_guild field to False when a member leaves the guild.""" - if member.guild != constants.Guild.id: + if member.guild.id != constants.Guild.id: return await self.patch_user(member.id, json={"in_guild": False}) @@ -143,7 +143,7 @@ class Sync(Cog): @Cog.listener() async def on_member_update(self, before: Member, after: Member) -> None: """Update the roles of the member in the database if a change is detected.""" - if after.guild != constants.Guild.id: + if after.guild.id != constants.Guild.id: return if before.roles != after.roles: diff --git a/tests/bot/cogs/sync/test_cog.py b/tests/bot/cogs/sync/test_cog.py index e5be14391..120bc991d 100644 --- a/tests/bot/cogs/sync/test_cog.py +++ b/tests/bot/cogs/sync/test_cog.py @@ -134,6 +134,9 @@ class SyncCogListenerTests(SyncCogTestCase): self.guild_id_patcher = mock.patch("bot.cogs.sync.cog.constants.Guild.id", 5) self.guild_id = self.guild_id_patcher.start() + self.guild = helpers.MockGuild(id=self.guild_id) + self.other_guild = helpers.MockGuild(id=0) + def tearDown(self): self.guild_id_patcher.stop() @@ -148,14 +151,14 @@ class SyncCogListenerTests(SyncCogTestCase): "permissions": 8, "position": 23, } - role = helpers.MockRole(**role_data, guild=self.guild_id) + role = helpers.MockRole(**role_data, guild=self.guild) await self.cog.on_guild_role_create(role) self.bot.api_client.post.assert_called_once_with("bot/roles", json=role_data) async def test_sync_cog_on_guild_role_create_ignores_guilds(self): """Events from other guilds should be ignored.""" - role = helpers.MockRole(guild=0) + role = helpers.MockRole(guild=self.other_guild) await self.cog.on_guild_role_create(role) self.bot.api_client.post.assert_not_awaited() @@ -163,14 +166,14 @@ class SyncCogListenerTests(SyncCogTestCase): """A DELETE request should be sent.""" self.assertTrue(self.cog.on_guild_role_delete.__cog_listener__) - role = helpers.MockRole(id=99, guild=self.guild_id) + role = helpers.MockRole(id=99, guild=self.guild) await self.cog.on_guild_role_delete(role) self.bot.api_client.delete.assert_called_once_with("bot/roles/99") async def test_sync_cog_on_guild_role_delete_ignores_guilds(self): """Events from other guilds should be ignored.""" - role = helpers.MockRole(guild=0) + role = helpers.MockRole(guild=self.other_guild) await self.cog.on_guild_role_delete(role) self.bot.api_client.delete.assert_not_awaited() @@ -198,8 +201,8 @@ class SyncCogListenerTests(SyncCogTestCase): after_role_data = role_data.copy() after_role_data[attribute] = 876 - before_role = helpers.MockRole(**role_data, guild=self.guild_id) - after_role = helpers.MockRole(**after_role_data, guild=self.guild_id) + before_role = helpers.MockRole(**role_data, guild=self.guild) + after_role = helpers.MockRole(**after_role_data, guild=self.guild) await self.cog.on_guild_role_update(before_role, after_role) @@ -213,7 +216,7 @@ class SyncCogListenerTests(SyncCogTestCase): async def test_sync_cog_on_guild_role_update_ignores_guilds(self): """Events from other guilds should be ignored.""" - role = helpers.MockRole(guild=0) + role = helpers.MockRole(guild=self.other_guild) await self.cog.on_guild_role_update(role, role) self.bot.api_client.put.assert_not_awaited() @@ -221,7 +224,7 @@ class SyncCogListenerTests(SyncCogTestCase): """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_id) + member = helpers.MockMember(guild=self.guild) await self.cog.on_member_remove(member) self.cog.patch_user.assert_called_once_with( @@ -231,7 +234,7 @@ class SyncCogListenerTests(SyncCogTestCase): async def test_sync_cog_on_member_remove_ignores_guilds(self): """Events from other guilds should be ignored.""" - member = helpers.MockMember(guild=0) + member = helpers.MockMember(guild=self.other_guild) await self.cog.on_member_remove(member) self.cog.patch_user.assert_not_awaited() @@ -241,8 +244,8 @@ class SyncCogListenerTests(SyncCogTestCase): # 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_id) - after_member = helpers.MockMember(roles=before_roles[1:], guild=self.guild_id) + 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) @@ -263,8 +266,8 @@ class SyncCogListenerTests(SyncCogTestCase): with self.subTest(attribute=attribute): self.cog.patch_user.reset_mock() - before_member = helpers.MockMember(**{attribute: old_value}, guild=self.guild_id) - after_member = helpers.MockMember(**{attribute: new_value}, guild=self.guild_id) + before_member = helpers.MockMember(**{attribute: old_value}, guild=self.guild) + after_member = helpers.MockMember(**{attribute: new_value}, guild=self.guild) await self.cog.on_member_update(before_member, after_member) @@ -272,7 +275,7 @@ class SyncCogListenerTests(SyncCogTestCase): async def test_sync_cog_on_member_update_ignores_guilds(self): """Events from other guilds should be ignored.""" - member = helpers.MockMember(guild=0) + member = helpers.MockMember(guild=self.other_guild) await self.cog.on_member_update(member, member) self.cog.patch_user.assert_not_awaited() @@ -329,7 +332,7 @@ class SyncCogListenerTests(SyncCogTestCase): member = helpers.MockMember( discriminator="1234", roles=[helpers.MockRole(id=22), helpers.MockRole(id=12)], - guild=self.guild_id, + guild=self.guild, ) data = { @@ -376,7 +379,7 @@ class SyncCogListenerTests(SyncCogTestCase): async def test_sync_cog_on_member_join_ignores_guilds(self): """Events from other guilds should be ignored.""" - member = helpers.MockMember(guild=0) + member = helpers.MockMember(guild=self.other_guild) await self.cog.on_member_join(member) self.bot.api_client.post.assert_not_awaited() self.bot.api_client.put.assert_not_awaited() -- cgit v1.2.3 From 195e0f9407d2a8b7ac5b3028b4f10c1b73af0a4f Mon Sep 17 00:00:00 2001 From: Kyle Stanley Date: Fri, 26 Jun 2020 02:08:48 -0400 Subject: Update LinePaginator.add_line() tests --- tests/bot/test_pagination.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/bot/test_pagination.py b/tests/bot/test_pagination.py index f2e2c27ce..74896f010 100644 --- a/tests/bot/test_pagination.py +++ b/tests/bot/test_pagination.py @@ -18,18 +18,18 @@ class LinePaginatorTests(TestCase): self.assertEqual(len(self.paginator._pages), 0) def test_add_line_works_on_long_lines(self): - """`add_line` should scale long lines up to `scale_to_size`.""" - self.paginator.add_line('x' * self.paginator.scale_to_size) - self.assertEqual(len(self.paginator._pages), 1) + """After additional lines after `max_size` is exceeded should go on the next page.""" + self.paginator.add_line('x' * self.paginator.max_size) + self.assertEqual(len(self.paginator._pages), 0) # Any additional lines should start a new page after `max_size` is exceeded. self.paginator.add_line('x') - self.assertEqual(len(self.paginator._pages), 2) + self.assertEqual(len(self.paginator._pages), 1) def test_add_line_continuation(self): """When `scale_to_size` is exceeded, remaining words should be split onto the next page.""" self.paginator.add_line('zyz ' * (self.paginator.scale_to_size//4 + 1)) - self.assertEqual(len(self.paginator._pages), 2) + self.assertEqual(len(self.paginator._pages), 1) def test_add_line_no_continuation(self): """If adding a new line to an existing page would exceed `max_size`, it should start a new -- cgit v1.2.3 From 77ce4c88695ca748059a7076de88d5b42b37d5f5 Mon Sep 17 00:00:00 2001 From: Kyle Stanley Date: Fri, 26 Jun 2020 03:22:30 -0400 Subject: In LinePaginator, truncate words that exceed scale_to_size --- bot/pagination.py | 11 ++++++----- tests/bot/test_pagination.py | 12 +++++------- 2 files changed, 11 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/bot/pagination.py b/bot/pagination.py index 746ec3696..cd602c715 100644 --- a/bot/pagination.py +++ b/bot/pagination.py @@ -88,11 +88,9 @@ class LinePaginator(Paginator): if len(line) > (max_chars := self.max_size - len(self.prefix) - 2): if len(line) > self.scale_to_size: line, remaining_words = self._split_remaining_words(line, max_chars) - # If line still exceeds scale_to_size, we were unable to split into a second - # page without truncating. if len(line) > self.scale_to_size: - raise RuntimeError(f'Line exceeds maximum scale_to_size {self.scale_to_size}' - ' and could not be split.') + log.debug("Could not continue to next page, truncating line.") + line = line[:self.scale_to_size] if self.max_lines is not None and self._linecount >= self.max_lines: log.debug("max_lines exceeded, creating new page.") @@ -144,11 +142,14 @@ class LinePaginator(Paginator): reduced_words.append(word) reduced_char_count += len(word) + 1 else: + # If reduced_words is empty, we were unable to split the words across pages + if not reduced_words: + return line, None is_full = True remaining_words.append(word) else: remaining_words.append(word) - + return ( " ".join(reduced_words), continuation_header + " ".join(remaining_words) if remaining_words else None diff --git a/tests/bot/test_pagination.py b/tests/bot/test_pagination.py index 74896f010..ce880d457 100644 --- a/tests/bot/test_pagination.py +++ b/tests/bot/test_pagination.py @@ -39,13 +39,11 @@ class LinePaginatorTests(TestCase): self.paginator.add_line('z') self.assertEqual(len(self.paginator._pages), 1) - def test_add_line_raises_on_very_long_words(self): - """`add_line` should raise if a single long word is added that exceeds `scale_to_size`. - - Note: truncation is also a potential option, but this should not occur from normal usage. - """ - with self.assertRaises(RuntimeError): - self.paginator.add_line('x' * (self.paginator.scale_to_size + 1)) + def test_add_line_truncates_very_long_words(self): + """`add_line` should truncate if a single long word exceeds `scale_to_size`.""" + self.paginator.add_line('x' * (self.paginator.scale_to_size + 1)) + # Note: item at index 1 is the truncated line, index 0 is prefix + self.assertEqual(self.paginator._current_page[1], 'x' * self.paginator.scale_to_size) class ImagePaginatorTests(TestCase): -- cgit v1.2.3 From 40719793f9c0d8a2c5761d3730b5920a146709c3 Mon Sep 17 00:00:00 2001 From: Den4200 Date: Mon, 6 Jul 2020 04:08:27 +0000 Subject: Add tests for cog_check and get_slowmode --- tests/bot/cogs/test_slowmode.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/bot/cogs/test_slowmode.py (limited to 'tests') diff --git a/tests/bot/cogs/test_slowmode.py b/tests/bot/cogs/test_slowmode.py new file mode 100644 index 000000000..fb9f3c9ad --- /dev/null +++ b/tests/bot/cogs/test_slowmode.py @@ -0,0 +1,37 @@ +import unittest +from unittest import mock + +from bot.cogs.slowmode import Slowmode +from tests.helpers import MockBot, MockContext, MockTextChannel + + +class SlowmodeTests(unittest.IsolatedAsyncioTestCase): + + def setUp(self) -> None: + self.bot = MockBot() + self.cog = Slowmode(self.bot) + self.text_channel = MockTextChannel() + self.ctx = MockContext(channel=self.text_channel) + + async def test_get_slowmode_no_channel(self) -> None: + """Get slowmode without a given channel""" + self.text_channel.mention = '#python-general' + self.text_channel.slowmode_delay = 5 + + await self.cog.get_slowmode(self.cog, self.ctx, None) + self.ctx.send.assert_called_once_with("The slowmode delay for #python-general is 5 seconds.") + + async def test_get_slowmode_with_channel(self) -> None: + """Get slowmode without a given channel""" + self.text_channel.mention = '#python-language' + self.text_channel.slowmode_delay = 2 + + await self.cog.get_slowmode(self.cog, self.ctx, self.text_channel) + self.ctx.send.assert_called_once_with("The slowmode delay for #python-language is 2 seconds.") + + @mock.patch("bot.cogs.slowmode.with_role_check") + @mock.patch("bot.cogs.slowmode.MODERATION_ROLES", new=(1, 2, 3)) + def test_cog_check(self, role_check): + """Role check is called with `MODERATION_ROLES`""" + self.cog.cog_check(self.ctx) + role_check.assert_called_once_with(self.ctx, *(1, 2, 3)) -- cgit v1.2.3 From e760b4312a5264fe9442cb1d53c9e357dbeb2b81 Mon Sep 17 00:00:00 2001 From: Den4200 Date: Mon, 6 Jul 2020 04:55:42 +0000 Subject: Add tests for reset_slowmode --- tests/bot/cogs/test_slowmode.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/bot/cogs/test_slowmode.py b/tests/bot/cogs/test_slowmode.py index fb9f3c9ad..a2e5ad346 100644 --- a/tests/bot/cogs/test_slowmode.py +++ b/tests/bot/cogs/test_slowmode.py @@ -2,6 +2,7 @@ import unittest from unittest import mock from bot.cogs.slowmode import Slowmode +from bot.constants import Emojis from tests.helpers import MockBot, MockContext, MockTextChannel @@ -14,7 +15,7 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): self.ctx = MockContext(channel=self.text_channel) async def test_get_slowmode_no_channel(self) -> None: - """Get slowmode without a given channel""" + """Get slowmode without a given channel.""" self.text_channel.mention = '#python-general' self.text_channel.slowmode_delay = 5 @@ -22,12 +23,30 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): self.ctx.send.assert_called_once_with("The slowmode delay for #python-general is 5 seconds.") async def test_get_slowmode_with_channel(self) -> None: - """Get slowmode without a given channel""" + """Get slowmode with a given channel.""" self.text_channel.mention = '#python-language' self.text_channel.slowmode_delay = 2 await self.cog.get_slowmode(self.cog, self.ctx, self.text_channel) - self.ctx.send.assert_called_once_with("The slowmode delay for #python-language is 2 seconds.") + self.ctx.send.assert_called_once_with('The slowmode delay for #python-language is 2 seconds.') + + async def test_reset_slowmode_no_channel(self) -> None: + """Reset slowmode without a given channel.""" + self.text_channel.mention = '#careers' + + await self.cog.reset_slowmode(self.cog, self.ctx, None) + self.ctx.send.assert_called_once_with( + f'{Emojis.check_mark} The slowmode delay for #careers has been reset to 0 seconds.' + ) + + async def test_reset_slowmode_with_channel(self) -> None: + """Reset slowmode with a given channel.""" + self.text_channel.mention = '#meta' + + await self.cog.reset_slowmode(self.cog, self.ctx, self.text_channel) + self.ctx.send.assert_called_once_with( + f'{Emojis.check_mark} The slowmode delay for #meta has been reset to 0 seconds.' + ) @mock.patch("bot.cogs.slowmode.with_role_check") @mock.patch("bot.cogs.slowmode.MODERATION_ROLES", new=(1, 2, 3)) -- cgit v1.2.3 From 8613659cb191bedca925dc798c89623b49c9a90a Mon Sep 17 00:00:00 2001 From: Den4200 Date: Mon, 6 Jul 2020 05:45:04 +0000 Subject: Add tests for set_slowmode --- tests/bot/cogs/test_slowmode.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'tests') diff --git a/tests/bot/cogs/test_slowmode.py b/tests/bot/cogs/test_slowmode.py index a2e5ad346..5262ce34a 100644 --- a/tests/bot/cogs/test_slowmode.py +++ b/tests/bot/cogs/test_slowmode.py @@ -1,6 +1,8 @@ import unittest from unittest import mock +from dateutil.relativedelta import relativedelta + from bot.cogs.slowmode import Slowmode from bot.constants import Emojis from tests.helpers import MockBot, MockContext, MockTextChannel @@ -30,6 +32,24 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): await self.cog.get_slowmode(self.cog, self.ctx, self.text_channel) self.ctx.send.assert_called_once_with('The slowmode delay for #python-language is 2 seconds.') + async def test_set_slowmode_no_channel(self) -> None: + """Set slowmode without a given channel.""" + self.text_channel.mention = '#careers' + + await self.cog.set_slowmode(self.cog, self.ctx, None, relativedelta(seconds=3)) + self.ctx.send.assert_called_once_with( + f'{Emojis.check_mark} The slowmode delay for #careers is now 3 seconds.' + ) + + async def test_set_slowmode_with_channel(self) -> None: + """Set slowmode with a given channel.""" + self.text_channel.mention = '#meta' + + await self.cog.set_slowmode(self.cog, self.ctx, self.text_channel, relativedelta(seconds=4)) + self.ctx.send.assert_called_once_with( + f'{Emojis.check_mark} The slowmode delay for #meta is now 4 seconds.' + ) + async def test_reset_slowmode_no_channel(self) -> None: """Reset slowmode without a given channel.""" self.text_channel.mention = '#careers' -- cgit v1.2.3 From 4935ed5ae632f5887bcff23ac67c781eab8527e9 Mon Sep 17 00:00:00 2001 From: Den4200 Date: Mon, 6 Jul 2020 06:05:32 +0000 Subject: Use local text_channel instead of instance attribute --- tests/bot/cogs/test_slowmode.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/tests/bot/cogs/test_slowmode.py b/tests/bot/cogs/test_slowmode.py index 5262ce34a..663c9fd43 100644 --- a/tests/bot/cogs/test_slowmode.py +++ b/tests/bot/cogs/test_slowmode.py @@ -13,28 +13,25 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): def setUp(self) -> None: self.bot = MockBot() self.cog = Slowmode(self.bot) - self.text_channel = MockTextChannel() - self.ctx = MockContext(channel=self.text_channel) + self.ctx = MockContext() async def test_get_slowmode_no_channel(self) -> None: """Get slowmode without a given channel.""" - self.text_channel.mention = '#python-general' - self.text_channel.slowmode_delay = 5 + self.ctx.channel = MockTextChannel(name='python-general', slowmode_delay=5) await self.cog.get_slowmode(self.cog, self.ctx, None) self.ctx.send.assert_called_once_with("The slowmode delay for #python-general is 5 seconds.") async def test_get_slowmode_with_channel(self) -> None: """Get slowmode with a given channel.""" - self.text_channel.mention = '#python-language' - self.text_channel.slowmode_delay = 2 + text_channel = MockTextChannel(name='python-language', slowmode_delay=2) - await self.cog.get_slowmode(self.cog, self.ctx, self.text_channel) + await self.cog.get_slowmode(self.cog, self.ctx, text_channel) self.ctx.send.assert_called_once_with('The slowmode delay for #python-language is 2 seconds.') async def test_set_slowmode_no_channel(self) -> None: """Set slowmode without a given channel.""" - self.text_channel.mention = '#careers' + self.ctx.channel = MockTextChannel(name='careers') await self.cog.set_slowmode(self.cog, self.ctx, None, relativedelta(seconds=3)) self.ctx.send.assert_called_once_with( @@ -43,16 +40,16 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): async def test_set_slowmode_with_channel(self) -> None: """Set slowmode with a given channel.""" - self.text_channel.mention = '#meta' + text_channel = MockTextChannel(name='meta') - await self.cog.set_slowmode(self.cog, self.ctx, self.text_channel, relativedelta(seconds=4)) + await self.cog.set_slowmode(self.cog, self.ctx, text_channel, relativedelta(seconds=4)) self.ctx.send.assert_called_once_with( f'{Emojis.check_mark} The slowmode delay for #meta is now 4 seconds.' ) async def test_reset_slowmode_no_channel(self) -> None: """Reset slowmode without a given channel.""" - self.text_channel.mention = '#careers' + self.ctx.channel = MockTextChannel(name='careers', slowmode_delay=6) await self.cog.reset_slowmode(self.cog, self.ctx, None) self.ctx.send.assert_called_once_with( @@ -61,9 +58,9 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): async def test_reset_slowmode_with_channel(self) -> None: """Reset slowmode with a given channel.""" - self.text_channel.mention = '#meta' + text_channel = MockTextChannel(name='meta', slowmode_delay=1) - await self.cog.reset_slowmode(self.cog, self.ctx, self.text_channel) + await self.cog.reset_slowmode(self.cog, self.ctx, text_channel) self.ctx.send.assert_called_once_with( f'{Emojis.check_mark} The slowmode delay for #meta has been reset to 0 seconds.' ) -- cgit v1.2.3 From 77a2e514dd2e200e23ccf45760677c2e7c40b9ff Mon Sep 17 00:00:00 2001 From: Den4200 Date: Mon, 6 Jul 2020 06:11:00 +0000 Subject: Add multiple test cases for set_slowmode tests --- tests/bot/cogs/test_slowmode.py | 44 +++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/bot/cogs/test_slowmode.py b/tests/bot/cogs/test_slowmode.py index 663c9fd43..e9835b8bd 100644 --- a/tests/bot/cogs/test_slowmode.py +++ b/tests/bot/cogs/test_slowmode.py @@ -31,22 +31,46 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): async def test_set_slowmode_no_channel(self) -> None: """Set slowmode without a given channel.""" - self.ctx.channel = MockTextChannel(name='careers') - - await self.cog.set_slowmode(self.cog, self.ctx, None, relativedelta(seconds=3)) - self.ctx.send.assert_called_once_with( - f'{Emojis.check_mark} The slowmode delay for #careers is now 3 seconds.' + test_cases = ( + ('helpers', 23, f'{Emojis.check_mark} The slowmode delay for #helpers is now 23 seconds.'), + ('mods', 76526, f'{Emojis.cross_mark} The slowmode delay must be between 0 and 6 hours.'), + ('admins', 97, f'{Emojis.check_mark} The slowmode delay for #admins is now 1 minute and 37 seconds.') ) + for channel_name, seconds, result_msg in test_cases: + with self.subTest( + channel_mention=channel_name, + seconds=seconds, + result_msg=result_msg + ): + self.ctx.channel = MockTextChannel(name=channel_name) + + await self.cog.set_slowmode(self.cog, self.ctx, None, relativedelta(seconds=seconds)) + self.ctx.send.assert_called_once_with(result_msg) + + self.ctx.reset_mock() + async def test_set_slowmode_with_channel(self) -> None: """Set slowmode with a given channel.""" - text_channel = MockTextChannel(name='meta') - - await self.cog.set_slowmode(self.cog, self.ctx, text_channel, relativedelta(seconds=4)) - self.ctx.send.assert_called_once_with( - f'{Emojis.check_mark} The slowmode delay for #meta is now 4 seconds.' + test_cases = ( + ('bot-commands', 12, f'{Emojis.check_mark} The slowmode delay for #bot-commands is now 12 seconds.'), + ('mod-spam', 21, f'{Emojis.check_mark} The slowmode delay for #mod-spam is now 21 seconds.'), + ('admin-spam', 4323598, f'{Emojis.cross_mark} The slowmode delay must be between 0 and 6 hours.') ) + for channel_name, seconds, result_msg in test_cases: + with self.subTest( + channel_mention=channel_name, + seconds=seconds, + result_msg=result_msg + ): + text_channel = MockTextChannel(name=channel_name) + + await self.cog.set_slowmode(self.cog, self.ctx, text_channel, relativedelta(seconds=seconds)) + self.ctx.send.assert_called_once_with(result_msg) + + self.ctx.reset_mock() + async def test_reset_slowmode_no_channel(self) -> None: """Reset slowmode without a given channel.""" self.ctx.channel = MockTextChannel(name='careers', slowmode_delay=6) -- cgit v1.2.3 From 2d170b8af92c77bedea4d77fbdeedc515d3f2c59 Mon Sep 17 00:00:00 2001 From: Den4200 Date: Mon, 6 Jul 2020 17:08:24 +0000 Subject: Improve set_slowmode tests by checking whether the channel was edited --- tests/bot/cogs/test_slowmode.py | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/bot/cogs/test_slowmode.py b/tests/bot/cogs/test_slowmode.py index e9835b8bd..65b1534cb 100644 --- a/tests/bot/cogs/test_slowmode.py +++ b/tests/bot/cogs/test_slowmode.py @@ -32,20 +32,27 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): async def test_set_slowmode_no_channel(self) -> None: """Set slowmode without a given channel.""" test_cases = ( - ('helpers', 23, f'{Emojis.check_mark} The slowmode delay for #helpers is now 23 seconds.'), - ('mods', 76526, f'{Emojis.cross_mark} The slowmode delay must be between 0 and 6 hours.'), - ('admins', 97, f'{Emojis.check_mark} The slowmode delay for #admins is now 1 minute and 37 seconds.') + ('helpers', 23, True, f'{Emojis.check_mark} The slowmode delay for #helpers is now 23 seconds.'), + ('mods', 76526, False, f'{Emojis.cross_mark} The slowmode delay must be between 0 and 6 hours.'), + ('admins', 97, True, f'{Emojis.check_mark} The slowmode delay for #admins is now 1 minute and 37 seconds.') ) - for channel_name, seconds, result_msg in test_cases: + for channel_name, seconds, edited, result_msg in test_cases: with self.subTest( channel_mention=channel_name, seconds=seconds, + edited=edited, result_msg=result_msg ): self.ctx.channel = MockTextChannel(name=channel_name) await self.cog.set_slowmode(self.cog, self.ctx, None, relativedelta(seconds=seconds)) + + if edited: + self.ctx.channel.edit.assert_awaited_once_with(slowmode_delay=float(seconds)) + else: + self.ctx.channel.edit.assert_not_called() + self.ctx.send.assert_called_once_with(result_msg) self.ctx.reset_mock() @@ -53,20 +60,27 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): async def test_set_slowmode_with_channel(self) -> None: """Set slowmode with a given channel.""" test_cases = ( - ('bot-commands', 12, f'{Emojis.check_mark} The slowmode delay for #bot-commands is now 12 seconds.'), - ('mod-spam', 21, f'{Emojis.check_mark} The slowmode delay for #mod-spam is now 21 seconds.'), - ('admin-spam', 4323598, f'{Emojis.cross_mark} The slowmode delay must be between 0 and 6 hours.') + ('bot-commands', 12, True, f'{Emojis.check_mark} The slowmode delay for #bot-commands is now 12 seconds.'), + ('mod-spam', 21, True, f'{Emojis.check_mark} The slowmode delay for #mod-spam is now 21 seconds.'), + ('admin-spam', 4323598, False, f'{Emojis.cross_mark} The slowmode delay must be between 0 and 6 hours.') ) - for channel_name, seconds, result_msg in test_cases: + for channel_name, seconds, edited, result_msg in test_cases: with self.subTest( channel_mention=channel_name, seconds=seconds, + edited=edited, result_msg=result_msg ): text_channel = MockTextChannel(name=channel_name) await self.cog.set_slowmode(self.cog, self.ctx, text_channel, relativedelta(seconds=seconds)) + + if edited: + text_channel.edit.assert_awaited_once_with(slowmode_delay=float(seconds)) + else: + text_channel.edit.assert_not_called() + self.ctx.send.assert_called_once_with(result_msg) self.ctx.reset_mock() -- cgit v1.2.3 From cdeb41bfd283cb6cb1285993737e8e3abd5aea9f Mon Sep 17 00:00:00 2001 From: Den4200 Date: Mon, 6 Jul 2020 17:30:44 +0000 Subject: Fix imports in slowmode tests --- tests/bot/cogs/test_slowmode.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/bot/cogs/test_slowmode.py b/tests/bot/cogs/test_slowmode.py index 65b1534cb..f442814c8 100644 --- a/tests/bot/cogs/test_slowmode.py +++ b/tests/bot/cogs/test_slowmode.py @@ -3,7 +3,7 @@ from unittest import mock from dateutil.relativedelta import relativedelta -from bot.cogs.slowmode import Slowmode +from bot.cogs.moderation.slowmode import Slowmode from bot.constants import Emojis from tests.helpers import MockBot, MockContext, MockTextChannel @@ -103,8 +103,8 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase): f'{Emojis.check_mark} The slowmode delay for #meta has been reset to 0 seconds.' ) - @mock.patch("bot.cogs.slowmode.with_role_check") - @mock.patch("bot.cogs.slowmode.MODERATION_ROLES", new=(1, 2, 3)) + @mock.patch("bot.cogs.moderation.slowmode.with_role_check") + @mock.patch("bot.cogs.moderation.slowmode.MODERATION_ROLES", new=(1, 2, 3)) def test_cog_check(self, role_check): """Role check is called with `MODERATION_ROLES`""" self.cog.cog_check(self.ctx) -- cgit v1.2.3