diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/bot/exts/backend/sync/test_users.py | 8 | ||||
-rw-r--r-- | tests/bot/exts/backend/test_error_handler.py | 10 | ||||
-rw-r--r-- | tests/bot/exts/moderation/test_silence.py | 136 | ||||
-rw-r--r-- | tests/bot/exts/recruitment/talentpool/test_review.py | 40 | ||||
-rw-r--r-- | tests/bot/exts/utils/snekbox/test_snekbox.py | 17 | ||||
-rw-r--r-- | tests/helpers.py | 34 |
6 files changed, 144 insertions, 101 deletions
diff --git a/tests/bot/exts/backend/sync/test_users.py b/tests/bot/exts/backend/sync/test_users.py index 2fc000446..26bf0d9a0 100644 --- a/tests/bot/exts/backend/sync/test_users.py +++ b/tests/bot/exts/backend/sync/test_users.py @@ -210,8 +210,8 @@ class UserSyncerSyncTests(unittest.IsolatedAsyncioTestCase): diff = _Diff(self.users, [], None) await UserSyncer._sync(diff) - self.bot.api_client.post.assert_any_call("bot/users", json=diff.created[:self.chunk_size]) - self.bot.api_client.post.assert_any_call("bot/users", json=diff.created[self.chunk_size:]) + self.bot.api_client.post.assert_any_call("bot/users", json=tuple(diff.created[:self.chunk_size])) + self.bot.api_client.post.assert_any_call("bot/users", json=tuple(diff.created[self.chunk_size:])) self.assertEqual(self.bot.api_client.post.call_count, self.chunk_count) self.bot.api_client.put.assert_not_called() @@ -222,8 +222,8 @@ class UserSyncerSyncTests(unittest.IsolatedAsyncioTestCase): diff = _Diff([], self.users, None) await UserSyncer._sync(diff) - self.bot.api_client.patch.assert_any_call("bot/users/bulk_patch", json=diff.updated[:self.chunk_size]) - self.bot.api_client.patch.assert_any_call("bot/users/bulk_patch", json=diff.updated[self.chunk_size:]) + self.bot.api_client.patch.assert_any_call("bot/users/bulk_patch", json=tuple(diff.updated[:self.chunk_size])) + self.bot.api_client.patch.assert_any_call("bot/users/bulk_patch", json=tuple(diff.updated[self.chunk_size:])) self.assertEqual(self.bot.api_client.patch.call_count, self.chunk_count) self.bot.api_client.post.assert_not_called() diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py index dbc62270b..85dc33999 100644 --- a/tests/bot/exts/backend/test_error_handler.py +++ b/tests/bot/exts/backend/test_error_handler.py @@ -495,26 +495,26 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase): else: log_mock.debug.assert_called_once() - @patch("bot.exts.backend.error_handler.push_scope") + @patch("bot.exts.backend.error_handler.new_scope") @patch("bot.exts.backend.error_handler.log") - async def test_handle_unexpected_error(self, log_mock, push_scope_mock): + async def test_handle_unexpected_error(self, log_mock, new_scope_mock): """Should `ctx.send` this error, error log this and sent to Sentry.""" for case in (None, MockGuild()): with self.subTest(guild=case): self.ctx.reset_mock() log_mock.reset_mock() - push_scope_mock.reset_mock() + new_scope_mock.reset_mock() scope_mock = Mock() # Mock `with push_scope_mock() as scope:` - push_scope_mock.return_value.__enter__.return_value = scope_mock + new_scope_mock.return_value.__enter__.return_value = scope_mock self.ctx.guild = case await self.cog.handle_unexpected_error(self.ctx, errors.CommandError()) self.ctx.send.assert_awaited_once() log_mock.error.assert_called_once() - push_scope_mock.assert_called_once() + new_scope_mock.assert_called_once() set_tag_calls = [ call("command", self.ctx.command.qualified_name), diff --git a/tests/bot/exts/moderation/test_silence.py b/tests/bot/exts/moderation/test_silence.py index a7f239d7f..86d396afd 100644 --- a/tests/bot/exts/moderation/test_silence.py +++ b/tests/bot/exts/moderation/test_silence.py @@ -37,15 +37,13 @@ class SilenceTest(RedisTestCase): self.bot = MockBot(get_channel=lambda _id: MockTextChannel(id=_id)) self.cog = silence.Silence(self.bot) - @autospec(silence, "SilenceNotifier", pass_mocks=False) async def asyncSetUp(self) -> None: await super().asyncSetUp() await self.cog.cog_load() # Populate instance attributes. -class SilenceNotifierTests(SilenceTest): +class SilenceNotifierTests(unittest.IsolatedAsyncioTestCase): def setUp(self) -> None: - super().setUp() self.alert_channel = MockTextChannel() self.notifier = silence.SilenceNotifier(self.alert_channel) self.notifier.stop = self.notifier_stop_mock = Mock() @@ -54,32 +52,36 @@ class SilenceNotifierTests(SilenceTest): def test_add_channel_adds_channel(self): """Channel is added to `_silenced_channels` with the current loop.""" channel = Mock() - with mock.patch.object(self.notifier, "_silenced_channels") as silenced_channels: - self.notifier.add_channel(channel) - silenced_channels.__setitem__.assert_called_with(channel, self.notifier._current_loop) + self.notifier.add_channel(channel) + self.assertDictEqual(self.notifier._silenced_channels, {channel: self.notifier._current_loop}) + + def test_add_channel_loop_called_correctly(self): + """Loop is called only in correct scenarios.""" - def test_add_channel_starts_loop(self): - """Loop is started if `_silenced_channels` was empty.""" + # Loop is started if `_silenced_channels` was empty. self.notifier.add_channel(Mock()) self.notifier_start_mock.assert_called_once() - def test_add_channel_skips_start_with_channels(self): - """Loop start is not called when `_silenced_channels` is not empty.""" - with mock.patch.object(self.notifier, "_silenced_channels"): - self.notifier.add_channel(Mock()) + self.notifier_start_mock.reset_mock() + + # Loop start is not called when `_silenced_channels` is not empty. + self.notifier.add_channel(Mock()) self.notifier_start_mock.assert_not_called() def test_remove_channel_removes_channel(self): """Channel is removed from `_silenced_channels`.""" channel = Mock() - with mock.patch.object(self.notifier, "_silenced_channels") as silenced_channels: - self.notifier.remove_channel(channel) - silenced_channels.__delitem__.assert_called_with(channel) + self.notifier.add_channel(channel) + self.notifier.remove_channel(channel) + self.assertDictEqual(self.notifier._silenced_channels, {}) def test_remove_channel_stops_loop(self): """Notifier loop is stopped if `_silenced_channels` is empty after remove.""" - with mock.patch.object(self.notifier, "_silenced_channels", __bool__=lambda _: False): - self.notifier.remove_channel(Mock()) + channel = Mock() + self.notifier.add_channel(channel) + self.notifier_stop_mock.assert_not_called() + + self.notifier.remove_channel(channel) self.notifier_stop_mock.assert_called_once() def test_remove_channel_skips_stop_with_channels(self): @@ -111,33 +113,28 @@ class SilenceNotifierTests(SilenceTest): self.alert_channel.send.assert_not_called() -@autospec(silence.Silence, "previous_overwrites", "unsilence_timestamps", pass_mocks=False) class SilenceCogTests(SilenceTest): """Tests for the general functionality of the Silence cog.""" - @autospec(silence, "SilenceNotifier", pass_mocks=False) async def test_cog_load_got_guild(self): """Bot got guild after it became available.""" self.bot.wait_until_guild_available.assert_awaited_once() self.bot.get_guild.assert_called_once_with(Guild.id) - @autospec(silence, "SilenceNotifier", pass_mocks=False) async def test_cog_load_got_channels(self): """Got channels from bot.""" - await self.cog.cog_load() self.assertEqual(self.cog._mod_alerts_channel.id, Channels.mod_alerts) - @autospec(silence, "SilenceNotifier") - async def test_cog_load_got_notifier(self, notifier): + async def test_cog_load_got_notifier(self): """Notifier was started with channel.""" - await self.cog.cog_load() + with mock.patch.object(silence, "SilenceNotifier") as notifier: + await self.cog.cog_load() notifier.assert_called_once_with(MockTextChannel(id=Channels.mod_log)) self.assertEqual(self.cog.notifier, notifier.return_value) - @autospec(silence, "SilenceNotifier", pass_mocks=False) async def testcog_load_rescheduled(self): """`_reschedule_` coroutine was awaited.""" - self.cog._reschedule = mock.create_autospec(self.cog._reschedule) + self.cog._reschedule = AsyncMock() await self.cog.cog_load() self.cog._reschedule.assert_awaited_once_with() @@ -242,7 +239,7 @@ class SilenceCogTests(SilenceTest): self.assertEqual(member.move_to.call_count, 1 if member == failing_member else 2) -class SilenceArgumentParserTests(SilenceTest): +class SilenceArgumentParserTests(unittest.IsolatedAsyncioTestCase): """Tests for the silence argument parser utility function.""" @autospec(silence.Silence, "send_message", pass_mocks=False) @@ -250,6 +247,9 @@ class SilenceArgumentParserTests(SilenceTest): @autospec(silence.Silence, "parse_silence_args") async def test_command(self, parser_mock): """Test that the command passes in the correct arguments for different calls.""" + bot = MockBot() + cog = silence.Silence(bot) + test_cases = ( (), (15, ), @@ -262,7 +262,7 @@ class SilenceArgumentParserTests(SilenceTest): for case in test_cases: with self.subTest("Test command converters", args=case): - await self.cog.silence.callback(self.cog, ctx, *case) + await cog.silence.callback(cog, ctx, *case) try: first_arg = case[0] @@ -281,7 +281,7 @@ class SilenceArgumentParserTests(SilenceTest): async def test_no_arguments(self): """Test the parser when no arguments are passed to the command.""" ctx = MockContext() - channel, duration = self.cog.parse_silence_args(ctx, None, 10) + channel, duration = silence.Silence.parse_silence_args(ctx, None, 10) self.assertEqual(ctx.channel, channel) self.assertEqual(10, duration) @@ -289,7 +289,7 @@ class SilenceArgumentParserTests(SilenceTest): async def test_channel_only(self): """Test the parser when just the channel argument is passed.""" expected_channel = MockTextChannel() - actual_channel, duration = self.cog.parse_silence_args(MockContext(), expected_channel, 10) + actual_channel, duration = silence.Silence.parse_silence_args(MockContext(), expected_channel, 10) self.assertEqual(expected_channel, actual_channel) self.assertEqual(10, duration) @@ -297,7 +297,7 @@ class SilenceArgumentParserTests(SilenceTest): async def test_duration_only(self): """Test the parser when just the duration argument is passed.""" ctx = MockContext() - channel, duration = self.cog.parse_silence_args(ctx, 15, 10) + channel, duration = silence.Silence.parse_silence_args(ctx, 15, 10) self.assertEqual(ctx.channel, channel) self.assertEqual(15, duration) @@ -305,13 +305,12 @@ class SilenceArgumentParserTests(SilenceTest): async def test_all_args(self): """Test the parser when both channel and duration are passed.""" expected_channel = MockTextChannel() - actual_channel, duration = self.cog.parse_silence_args(MockContext(), expected_channel, 15) + actual_channel, duration = silence.Silence.parse_silence_args(MockContext(), expected_channel, 15) self.assertEqual(expected_channel, actual_channel) self.assertEqual(15, duration) -@autospec(silence.Silence, "previous_overwrites", "unsilence_timestamps", pass_mocks=False) class RescheduleTests(RedisTestCase): """Tests for the rescheduling of cached unsilences.""" @@ -328,7 +327,7 @@ class RescheduleTests(RedisTestCase): async def test_skipped_missing_channel(self): """Did nothing because the channel couldn't be retrieved.""" - self.cog.unsilence_timestamps.items.return_value = [(123, -1), (123, 1), (123, 10000000000)] + await self.cog.unsilence_timestamps.set(123, -1) self.bot.get_channel.return_value = None await self.cog._reschedule() @@ -341,8 +340,8 @@ class RescheduleTests(RedisTestCase): """Permanently silenced channels were added to the notifier.""" channels = [MockTextChannel(id=123), MockTextChannel(id=456)] self.bot.get_channel.side_effect = channels - self.cog.unsilence_timestamps.items.return_value = [(123, -1), (456, -1)] - + await self.cog.unsilence_timestamps.set(123, -1) + await self.cog.unsilence_timestamps.set(456, -1) await self.cog._reschedule() self.cog.notifier.add_channel.assert_any_call(channels[0]) @@ -355,7 +354,8 @@ class RescheduleTests(RedisTestCase): """Unsilenced expired silences.""" channels = [MockTextChannel(id=123), MockTextChannel(id=456)] self.bot.get_channel.side_effect = channels - self.cog.unsilence_timestamps.items.return_value = [(123, 100), (456, 200)] + await self.cog.unsilence_timestamps.set(123, 100) + await self.cog.unsilence_timestamps.set(456, 200) await self.cog._reschedule() @@ -370,7 +370,8 @@ class RescheduleTests(RedisTestCase): """Rescheduled active silences.""" channels = [MockTextChannel(id=123), MockTextChannel(id=456)] self.bot.get_channel.side_effect = channels - self.cog.unsilence_timestamps.items.return_value = [(123, 2000), (456, 3000)] + await self.cog.unsilence_timestamps.set(123, 2000) + await self.cog.unsilence_timestamps.set(456, 3000) silence.datetime.now.return_value = datetime.fromtimestamp(1000, tz=UTC) self.cog._unsilence_wrapper = mock.MagicMock() @@ -398,7 +399,6 @@ def voice_sync_helper(function): return inner -@autospec(silence.Silence, "previous_overwrites", "unsilence_timestamps", pass_mocks=False) class SilenceTests(SilenceTest): """Tests for the silence command and its related helper methods.""" @@ -596,19 +596,28 @@ class SilenceTests(SilenceTest): async def test_temp_not_added_to_notifier(self): """Channel was not added to notifier if a duration was set for the silence.""" - with mock.patch.object(self.cog, "_set_silence_overwrites", return_value=True): + with ( + mock.patch.object(self.cog, "_set_silence_overwrites", return_value=True), + mock.patch.object(self.cog.notifier, "add_channel") + ): await self.cog.silence.callback(self.cog, MockContext(), 15) self.cog.notifier.add_channel.assert_not_called() async def test_indefinite_added_to_notifier(self): """Channel was added to notifier if a duration was not set for the silence.""" - with mock.patch.object(self.cog, "_set_silence_overwrites", return_value=True): + with ( + mock.patch.object(self.cog, "_set_silence_overwrites", return_value=True), + mock.patch.object(self.cog.notifier, "add_channel") + ): await self.cog.silence.callback(self.cog, MockContext(), None, None) self.cog.notifier.add_channel.assert_called_once() async def test_silenced_not_added_to_notifier(self): """Channel was not added to the notifier if it was already silenced.""" - with mock.patch.object(self.cog, "_set_silence_overwrites", return_value=False): + with ( + mock.patch.object(self.cog, "_set_silence_overwrites", return_value=False), + mock.patch.object(self.cog.notifier, "add_channel") + ): await self.cog.silence.callback(self.cog, MockContext(), 15) self.cog.notifier.add_channel.assert_not_called() @@ -619,7 +628,7 @@ class SilenceTests(SilenceTest): '"create_public_threads": false, "send_messages_in_threads": true}' ) await self.cog._set_silence_overwrites(self.text_channel) - self.cog.previous_overwrites.set.assert_awaited_once_with(self.text_channel.id, overwrite_json) + self.assertEqual(await self.cog.previous_overwrites.get(self.text_channel.id), overwrite_json) @autospec(silence, "datetime") async def test_cached_unsilence_time(self, datetime_mock): @@ -632,14 +641,14 @@ class SilenceTests(SilenceTest): ctx = MockContext(channel=self.text_channel) await self.cog.silence.callback(self.cog, ctx, duration) - self.cog.unsilence_timestamps.set.assert_awaited_once_with(ctx.channel.id, timestamp) + self.assertEqual(await self.cog.unsilence_timestamps.get(ctx.channel.id), timestamp) datetime_mock.now.assert_called_once_with(tz=UTC) # Ensure it's using an aware dt. async def test_cached_indefinite_time(self): """A value of -1 was cached for a permanent silence.""" ctx = MockContext(channel=self.text_channel) await self.cog.silence.callback(self.cog, ctx, None, None) - self.cog.unsilence_timestamps.set.assert_awaited_once_with(ctx.channel.id, -1) + self.assertEqual(await self.cog.unsilence_timestamps.get(ctx.channel.id), -1) async def test_scheduled_task(self): """An unsilence task was scheduled.""" @@ -665,7 +674,6 @@ class SilenceTests(SilenceTest): unsilence.assert_awaited_once_with(ctx, ctx.channel, None) -@autospec(silence.Silence, "unsilence_timestamps", pass_mocks=False) class UnsilenceTests(SilenceTest): """Tests for the unsilence command and its related helper methods.""" @@ -681,13 +689,6 @@ class UnsilenceTests(SilenceTest): self.voice_overwrite = PermissionOverwrite(connect=True, speak=True) self.voice_channel.overwrites_for.return_value = self.voice_overwrite - async def asyncSetUp(self) -> None: - await super().asyncSetUp() - overwrites_cache = mock.create_autospec(self.cog.previous_overwrites, spec_set=True) - self.cog.previous_overwrites = overwrites_cache - - overwrites_cache.get.return_value = '{"send_messages": true, "add_reactions": false}' - async def test_sent_correct_message(self): """Appropriate failure/success message was sent by the command.""" unsilenced_overwrite = PermissionOverwrite(send_messages=True, add_reactions=True) @@ -720,7 +721,6 @@ class UnsilenceTests(SilenceTest): async def test_skipped_already_unsilenced(self): """Permissions were not set and `False` was returned for an already unsilenced channel.""" self.cog.scheduler.__contains__.return_value = False - self.cog.previous_overwrites.get.return_value = None for channel in (MockVoiceChannel(), MockTextChannel()): with self.subTest(channel=channel): @@ -729,6 +729,7 @@ class UnsilenceTests(SilenceTest): async def test_restored_overwrites_text(self): """Text channel's `send_message` and `add_reactions` overwrites were restored.""" + await self.cog.previous_overwrites.set(self.text_channel.id, '{"send_messages": true, "add_reactions": false}') await self.cog._unsilence(self.text_channel) self.text_channel.set_permissions.assert_awaited_once_with( self.cog._everyone_role, @@ -741,19 +742,18 @@ class UnsilenceTests(SilenceTest): async def test_restored_overwrites_voice(self): """Voice channel's `connect` and `speak` overwrites were restored.""" + await self.cog.previous_overwrites.set(self.voice_channel.id, '{"connect": true, "speak": true}') await self.cog._unsilence(self.voice_channel) self.voice_channel.set_permissions.assert_awaited_once_with( self.cog._verified_voice_role, overwrite=self.voice_overwrite, ) - # Recall that these values are determined by the fixture. self.assertTrue(self.voice_overwrite.connect) self.assertTrue(self.voice_overwrite.speak) async def test_cache_miss_used_default_overwrites_text(self): """Text overwrites were set to None due previous values not being found in the cache.""" - self.cog.previous_overwrites.get.return_value = None await self.cog._unsilence(self.text_channel) self.text_channel.set_permissions.assert_awaited_once_with( @@ -766,7 +766,6 @@ class UnsilenceTests(SilenceTest): async def test_cache_miss_used_default_overwrites_voice(self): """Voice overwrites were set to None due previous values not being found in the cache.""" - self.cog.previous_overwrites.get.return_value = None await self.cog._unsilence(self.voice_channel) self.voice_channel.set_permissions.assert_awaited_once_with( @@ -779,30 +778,31 @@ class UnsilenceTests(SilenceTest): async def test_cache_miss_sent_mod_alert_text(self): """A message was sent to the mod alerts channel upon muting a text channel.""" - self.cog.previous_overwrites.get.return_value = None await self.cog._unsilence(self.text_channel) self.cog._mod_alerts_channel.send.assert_awaited_once() async def test_cache_miss_sent_mod_alert_voice(self): """A message was sent to the mod alerts channel upon muting a voice channel.""" - self.cog.previous_overwrites.get.return_value = None await self.cog._unsilence(MockVoiceChannel()) self.cog._mod_alerts_channel.send.assert_awaited_once() async def test_removed_notifier(self): """Channel was removed from `notifier`.""" - await self.cog._unsilence(self.text_channel) - self.cog.notifier.remove_channel.assert_called_once_with(self.text_channel) + with mock.patch.object(silence.SilenceNotifier, "remove_channel"): + await self.cog._unsilence(self.text_channel) + self.cog.notifier.remove_channel.assert_called_once_with(self.text_channel) async def test_deleted_cached_overwrite(self): """Channel was deleted from the overwrites cache.""" + await self.cog.previous_overwrites.set(self.text_channel.id, '{"send_messages": true, "add_reactions": false}') await self.cog._unsilence(self.text_channel) - self.cog.previous_overwrites.delete.assert_awaited_once_with(self.text_channel.id) + self.assertEqual(await self.cog.previous_overwrites.get(self.text_channel.id), None) async def test_deleted_cached_time(self): """Channel was deleted from the timestamp cache.""" + await self.cog.unsilence_timestamps.set(self.text_channel.id, 100) await self.cog._unsilence(self.text_channel) - self.cog.unsilence_timestamps.delete.assert_awaited_once_with(self.text_channel.id) + self.assertEqual(await self.cog.unsilence_timestamps.get(self.text_channel.id), None) async def test_cancelled_task(self): """The scheduled unsilence task should be cancelled.""" @@ -813,7 +813,10 @@ class UnsilenceTests(SilenceTest): """Text channel's other unrelated overwrites were not changed, including cache misses.""" for overwrite_json in ('{"send_messages": true, "add_reactions": null}', None): with self.subTest(overwrite_json=overwrite_json): - self.cog.previous_overwrites.get.return_value = overwrite_json + if overwrite_json is None: + await self.cog.previous_overwrites.delete(self.text_channel.id) + else: + await self.cog.previous_overwrites.set(self.text_channel.id, overwrite_json) prev_overwrite_dict = dict(self.text_overwrite) await self.cog._unsilence(self.text_channel) @@ -831,7 +834,10 @@ class UnsilenceTests(SilenceTest): """Voice channel's other unrelated overwrites were not changed, including cache misses.""" for overwrite_json in ('{"connect": true, "speak": true}', None): with self.subTest(overwrite_json=overwrite_json): - self.cog.previous_overwrites.get.return_value = overwrite_json + if overwrite_json is None: + await self.cog.previous_overwrites.delete(self.voice_channel.id) + else: + await self.cog.previous_overwrites.set(self.voice_channel.id, overwrite_json) prev_overwrite_dict = dict(self.voice_overwrite) await self.cog._unsilence(self.voice_channel) diff --git a/tests/bot/exts/recruitment/talentpool/test_review.py b/tests/bot/exts/recruitment/talentpool/test_review.py index 25622e91f..8ec384bb2 100644 --- a/tests/bot/exts/recruitment/talentpool/test_review.py +++ b/tests/bot/exts/recruitment/talentpool/test_review.py @@ -3,7 +3,7 @@ from datetime import UTC, datetime, timedelta from unittest.mock import AsyncMock, Mock, patch from bot.exts.recruitment.talentpool import _review -from tests.helpers import MockBot, MockMember, MockMessage, MockTextChannel +from tests.helpers import MockBot, MockMember, MockMessage, MockReaction, MockTextChannel class AsyncIterator: @@ -63,8 +63,10 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase): """Tests for the `is_ready_for_review` function.""" too_recent = datetime.now(UTC) - timedelta(hours=1) not_too_recent = datetime.now(UTC) - timedelta(days=7) + ticket_reaction = MockReaction(users=[self.bot_user], emoji="\N{TICKET}") + cases = ( - # Only one review, and not too recent, so ready. + # Only one active review, and not too recent, so ready. ( [ MockMessage(author=self.bot_user, content="wookie for Helper!", created_at=not_too_recent), @@ -75,7 +77,7 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase): True, ), - # Three reviews, so not ready. + # Three active reviews, so not ready. ( [ MockMessage(author=self.bot_user, content="Chrisjl for Helper!", created_at=not_too_recent), @@ -86,7 +88,7 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase): False, ), - # Only one review, but too recent, so not ready. + # Only one active review, but too recent, so not ready. ( [ MockMessage(author=self.bot_user, content="Chrisjl for Helper!", created_at=too_recent), @@ -95,7 +97,7 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase): False, ), - # Only two reviews, and not too recent, so ready. + # Only two active reviews, and not too recent, so ready. ( [ MockMessage(author=self.bot_user, content="Not a review", created_at=too_recent), @@ -107,6 +109,34 @@ class ReviewerTests(unittest.IsolatedAsyncioTestCase): True, ), + # Over the active threshold, but below the total threshold + ( + [ + MockMessage( + author=self.bot_user, + content="joe for Helper!", + created_at=not_too_recent, + reactions=[ticket_reaction] + ) + ] * 6, + not_too_recent.timestamp(), + True + ), + + # Over the total threshold + ( + [ + MockMessage( + author=self.bot_user, + content="joe for Helper!", + created_at=not_too_recent, + reactions=[ticket_reaction] + ) + ] * 11, + not_too_recent.timestamp(), + False + ), + # No messages, so ready. ([], None, True), ) diff --git a/tests/bot/exts/utils/snekbox/test_snekbox.py b/tests/bot/exts/utils/snekbox/test_snekbox.py index d057b284d..3595d9a67 100644 --- a/tests/bot/exts/utils/snekbox/test_snekbox.py +++ b/tests/bot/exts/utils/snekbox/test_snekbox.py @@ -199,7 +199,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): too_many_lines = ( "001 | v\n002 | e\n003 | r\n004 | y\n005 | l\n006 | o\n" - "007 | n\n008 | g\n009 | b\n010 | e\n011 | a\n... (truncated - too many lines)" + "007 | n\n008 | g\n009 | b\n010 | e\n... (truncated - too many lines)" ) too_long_too_many_lines = ( "\n".join( @@ -292,7 +292,6 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): async def test_send_job(self): """Test the send_job function.""" ctx = MockContext() - ctx.message = MockMessage() ctx.send = AsyncMock() ctx.author = MockUser(mention="@LemonLemonishBeard#0042") @@ -311,7 +310,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): ctx.send.assert_called_once() self.assertEqual( ctx.send.call_args.args[0], - "@LemonLemonishBeard#0042 :warning: Your 3.12 eval job has completed " + ":warning: Your 3.12 eval job has completed " "with return code 0.\n\n```\n[No output]\n```" ) allowed_mentions = ctx.send.call_args.kwargs["allowed_mentions"] @@ -325,9 +324,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): async def test_send_job_with_paste_link(self): """Test the send_job function with a too long output that generate a paste link.""" ctx = MockContext() - ctx.message = MockMessage() ctx.send = AsyncMock() - ctx.author.mention = "@LemonLemonishBeard#0042" eval_result = EvalResult("Way too long beard", 0) self.cog.post_job = AsyncMock(return_value=eval_result) @@ -343,7 +340,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): ctx.send.assert_called_once() self.assertEqual( ctx.send.call_args.args[0], - "@LemonLemonishBeard#0042 :white_check_mark: Your 3.12 eval job " + ":white_check_mark: Your 3.12 eval job " "has completed with return code 0." "\n\n```\nWay too long beard\n```\nFull output: lookatmybeard.com" ) @@ -354,9 +351,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): async def test_send_job_with_non_zero_eval(self): """Test the send_job function with a code returning a non-zero code.""" ctx = MockContext() - ctx.message = MockMessage() ctx.send = AsyncMock() - ctx.author.mention = "@LemonLemonishBeard#0042" eval_result = EvalResult("ERROR", 127) self.cog.post_job = AsyncMock(return_value=eval_result) @@ -372,7 +367,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): ctx.send.assert_called_once() self.assertEqual( ctx.send.call_args.args[0], - "@LemonLemonishBeard#0042 :x: Your 3.12 eval job has completed with return code 127." + ":x: Your 3.12 eval job has completed with return code 127." "\n\n```\nERROR\n```" ) @@ -382,9 +377,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): async def test_send_job_with_disallowed_file_ext(self): """Test send_job with disallowed file extensions.""" ctx = MockContext() - ctx.message = MockMessage() ctx.send = AsyncMock() - ctx.author.mention = "@user#7700" files = [ FileAttachment("test.disallowed2", b"test"), @@ -407,7 +400,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase): ctx.send.assert_called_once() res = ctx.send.call_args.args[0] self.assertTrue( - res.startswith("@user#7700 :white_check_mark: Your 3.12 eval job has completed with return code 0.") + res.startswith(":white_check_mark: Your 3.12 eval job has completed with return code 0.") ) self.assertIn("Files with disallowed extensions can't be uploaded: **.disallowed, .disallowed2, ...**", res) diff --git a/tests/helpers.py b/tests/helpers.py index 580848c25..1164828d6 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -388,12 +388,17 @@ class MockTextChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin): spec_set = text_channel_instance def __init__(self, **kwargs) -> None: - default_kwargs = {"id": next(self.discord_id), "name": "channel", "guild": MockGuild()} + default_kwargs = {"id": next(self.discord_id), "name": "channel"} super().__init__(**collections.ChainMap(kwargs, default_kwargs)) if "mention" not in kwargs: self.mention = f"#{self.name}" + @cached_property + def guild(self) -> MockGuild: + """Cached guild property.""" + return MockGuild() + class MockVoiceChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin): """ @@ -405,12 +410,17 @@ class MockVoiceChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin): spec_set = voice_channel_instance def __init__(self, **kwargs) -> None: - default_kwargs = {"id": next(self.discord_id), "name": "channel", "guild": MockGuild()} + default_kwargs = {"id": next(self.discord_id), "name": "channel"} super().__init__(**collections.ChainMap(kwargs, default_kwargs)) if "mention" not in kwargs: self.mention = f"#{self.name}" + @cached_property + def guild(self) -> MockGuild: + """Cached guild property.""" + return MockGuild() + # Create data for the DMChannel instance state = unittest.mock.MagicMock() @@ -500,10 +510,12 @@ class MockContext(CustomMockMixin, unittest.mock.MagicMock): super().__init__(**kwargs) self.me = kwargs.get("me", MockMember()) self.bot = kwargs.get("bot", MockBot()) - self.guild = kwargs.get("guild", MockGuild()) - self.author = kwargs.get("author", MockMember()) - self.channel = kwargs.get("channel", MockTextChannel()) - self.message = kwargs.get("message", MockMessage()) + + self.message = kwargs.get("message", MockMessage(guild=self.guild)) + self.author = kwargs.get("author", self.message.author) + self.channel = kwargs.get("channel", self.message.channel) + self.guild = kwargs.get("guild", self.channel.guild) + self.invoked_from_error_handler = kwargs.get("invoked_from_error_handler", False) @@ -519,10 +531,12 @@ class MockInteraction(CustomMockMixin, unittest.mock.MagicMock): super().__init__(**kwargs) self.me = kwargs.get("me", MockMember()) self.client = kwargs.get("client", MockBot()) - self.guild = kwargs.get("guild", MockGuild()) - self.user = kwargs.get("user", MockMember()) - self.channel = kwargs.get("channel", MockTextChannel()) - self.message = kwargs.get("message", MockMessage()) + + self.message = kwargs.get("message", MockMessage(guild=self.guild)) + self.user = kwargs.get("user", self.message.author) + self.channel = kwargs.get("channel", self.message.channel) + self.guild = kwargs.get("guild", self.channel.guild) + self.invoked_from_error_handler = kwargs.get("invoked_from_error_handler", False) |