aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorGravatar Boris Muratov <[email protected]>2025-08-10 00:12:00 +0300
committerGravatar GitHub <[email protected]>2025-08-10 00:12:00 +0300
commit876adc2ff060127e6496a16c5005dc3a0632eecd (patch)
treec19461757f2900c8b63e15dc0ce4d52b7c8a02b8 /tests
parentUpdate extensions functools.partial to enum.member (diff)
parentMerge branch 'main' into main (diff)
Merge pull request #3331 from b0nes1/main
Implemented optional duration parameter in slowmode command
Diffstat (limited to 'tests')
-rw-r--r--tests/bot/exts/moderation/test_slowmode.py119
1 files changed, 117 insertions, 2 deletions
diff --git a/tests/bot/exts/moderation/test_slowmode.py b/tests/bot/exts/moderation/test_slowmode.py
index cf5101e16..d75fcd2f1 100644
--- a/tests/bot/exts/moderation/test_slowmode.py
+++ b/tests/bot/exts/moderation/test_slowmode.py
@@ -1,14 +1,16 @@
-import unittest
+import asyncio
+import datetime
from unittest import mock
from dateutil.relativedelta import relativedelta
from bot.constants import Emojis
from bot.exts.moderation.slowmode import Slowmode
+from tests.base import RedisTestCase
from tests.helpers import MockBot, MockContext, MockTextChannel
-class SlowmodeTests(unittest.IsolatedAsyncioTestCase):
+class SlowmodeTests(RedisTestCase):
def setUp(self) -> None:
self.bot = MockBot()
@@ -95,6 +97,119 @@ class SlowmodeTests(unittest.IsolatedAsyncioTestCase):
self.ctx, text_channel, relativedelta(seconds=0)
)
+ @mock.patch("bot.exts.moderation.slowmode.datetime")
+ async def test_set_slowmode_with_expiry(self, mock_datetime) -> None:
+ """Set slowmode with an expiry"""
+ fixed_datetime = datetime.datetime(2025, 6, 2, 12, 0, 0, tzinfo=datetime.UTC)
+ mock_datetime.now.return_value = fixed_datetime
+
+ test_cases = (
+ ("python-general", 6, 6000, f"{Emojis.check_mark} The slowmode delay for #python-general is now 6 seconds "
+ "and will revert to 0 seconds <t:1748871600:R>."),
+ ("mod-spam", 5, 600, f"{Emojis.check_mark} The slowmode delay for #mod-spam is now 5 seconds and will "
+ "revert to 0 seconds <t:1748866200:R>."),
+ ("changelog", 12, 7200, f"{Emojis.check_mark} The slowmode delay for #changelog is now 12 seconds and will "
+ "revert to 0 seconds <t:1748872800:R>.")
+ )
+ for channel_name, seconds, expiry, result_msg in test_cases:
+ with self.subTest(
+ channel_mention=channel_name,
+ seconds=seconds,
+ expiry=expiry,
+ result_msg=result_msg
+ ):
+ text_channel = MockTextChannel(name=channel_name, slowmode_delay=0)
+ await self.cog.set_slowmode(
+ self.cog,
+ self.ctx,
+ text_channel,
+ relativedelta(seconds=seconds),
+ fixed_datetime + relativedelta(seconds=expiry)
+ )
+ text_channel.edit.assert_awaited_once_with(slowmode_delay=float(seconds))
+ self.ctx.send.assert_called_once_with(result_msg)
+ self.ctx.reset_mock()
+
+ async def test_callback_scheduled(self):
+ """Schedule slowmode to be reverted"""
+ self.cog.scheduler=mock.MagicMock(wraps=self.cog.scheduler)
+
+ text_channel = MockTextChannel(name="python-general", slowmode_delay=2, id=123)
+ expiry = datetime.datetime.now(tz=datetime.UTC) + relativedelta(seconds=10)
+ await self.cog.set_slowmode(
+ self.cog,
+ self.ctx,
+ text_channel,
+ relativedelta(seconds=4),
+ expiry
+ )
+
+ args = (expiry, text_channel.id, mock.ANY)
+ self.cog.scheduler.schedule_at.assert_called_once_with(*args)
+
+ @mock.patch("bot.exts.moderation.slowmode.get_or_fetch_channel")
+ async def test_revert_slowmode_callback(self, mock_get_or_fetch_channel) -> None:
+ """Check that the slowmode is reverted"""
+ text_channel = MockTextChannel(name="python-general", slowmode_delay=2, id=123, jump_url="#python-general")
+ mod_channel = MockTextChannel(name="mods", id=999, )
+ mock_get_or_fetch_channel.side_effect = [text_channel, mod_channel]
+
+ await self.cog.set_slowmode(
+ self.cog,
+ self.ctx,
+ text_channel,
+ relativedelta(seconds=4),
+ datetime.datetime.now(tz=datetime.UTC) + relativedelta(seconds=10)
+ )
+ await self.cog._revert_slowmode(text_channel.id)
+ text_channel.edit.assert_awaited_with(slowmode_delay=2)
+ mod_channel.send.assert_called_once_with(
+ f"{Emojis.check_mark} A previously applied slowmode in {text_channel.jump_url} ({text_channel.id}) "
+ "has expired and has been reverted to 2 seconds."
+ )
+
+ async def test_reschedule_slowmodes(self) -> None:
+ """Does not reschedule if cache is empty"""
+ self.cog.scheduler.schedule_at = mock.MagicMock()
+ self.cog._reschedule = mock.AsyncMock()
+ await self.cog.cog_unload()
+ await self.cog.cog_load()
+
+ self.cog._reschedule.assert_called()
+ self.cog.scheduler.schedule_at.assert_not_called()
+
+ async def test_reschedule_upon_reload(self) -> None:
+ """ Check that method `_reschedule` is called upon cog reload"""
+ self.cog._reschedule = mock.AsyncMock(wraps=self.cog._reschedule)
+ await self.cog.cog_unload()
+ await self.cog.cog_load()
+
+ self.cog._reschedule.assert_called()
+
+ async def test_reschedules_slowmodes(self) -> None:
+ """Slowmodes are loaded from cache at cog reload and scheduled to be reverted."""
+
+ now = datetime.datetime.now(tz=datetime.UTC)
+ channels = {}
+ slowmodes = (
+ (123, (now - datetime.timedelta(minutes=10)), 2), # expiration in the past
+ (456, (now + datetime.timedelta(minutes=20)), 4), # expiration in the future
+ )
+ for channel_id, expiration_datetime, delay in slowmodes:
+ channel = MockTextChannel(slowmode_delay=delay, id=channel_id)
+ channels[channel_id] = channel
+ await self.cog.slowmode_cache.set(channel_id, f"{delay}, {expiration_datetime}")
+
+ self.bot.get_channel = mock.MagicMock(side_effect=lambda channel_id: channels.get(channel_id))
+ await self.cog.cog_unload()
+ await self.cog.cog_load()
+ for channel_id in channels:
+ self.assertIn(channel_id, self.cog.scheduler)
+
+ await asyncio.sleep(1) # give scheduled task time to execute
+ channels[123].edit.assert_awaited_once_with(slowmode_delay=channels[123].slowmode_delay)
+ channels[456].edit.assert_not_called()
+
@mock.patch("bot.exts.moderation.slowmode.has_any_role")
@mock.patch("bot.exts.moderation.slowmode.MODERATION_ROLES", new=(1, 2, 3))
async def test_cog_check(self, role_check):