aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/bot/exts/moderation/test_silence.py394
-rw-r--r--tests/helpers.py25
2 files changed, 390 insertions, 29 deletions
diff --git a/tests/bot/exts/moderation/test_silence.py b/tests/bot/exts/moderation/test_silence.py
index 104293d8e..635e017e3 100644
--- a/tests/bot/exts/moderation/test_silence.py
+++ b/tests/bot/exts/moderation/test_silence.py
@@ -2,14 +2,14 @@ import asyncio
import unittest
from datetime import datetime, timezone
from unittest import mock
-from unittest.mock import Mock
+from unittest.mock import AsyncMock, Mock
from async_rediscache import RedisSession
from discord import PermissionOverwrite
from bot.constants import Channels, Guild, Roles
from bot.exts.moderation import silence
-from tests.helpers import MockBot, MockContext, MockTextChannel, autospec
+from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockTextChannel, MockVoiceChannel, autospec
redis_session = None
redis_loop = asyncio.get_event_loop()
@@ -123,7 +123,7 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):
guild.get_role.side_effect = lambda id_: Mock(id=id_)
await self.cog._async_init()
- self.assertEqual(self.cog._verified_role.id, Roles.verified)
+ self.assertEqual(self.cog._verified_msg_role.id, Roles.verified)
@autospec(silence, "SilenceNotifier", pass_mocks=False)
async def test_async_init_got_channels(self):
@@ -158,7 +158,7 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):
self.assertTrue(self.cog._init_task.cancelled())
@autospec("discord.ext.commands", "has_any_role")
- @mock.patch.object(silence, "MODERATION_ROLES", new=(1, 2, 3))
+ @mock.patch.object(silence.constants, "MODERATION_ROLES", new=(1, 2, 3))
async def test_cog_check(self, role_check):
"""Role check was called with `MODERATION_ROLES`"""
ctx = MockContext()
@@ -168,6 +168,157 @@ class SilenceCogTests(unittest.IsolatedAsyncioTestCase):
role_check.assert_called_once_with(*(1, 2, 3))
role_check.return_value.predicate.assert_awaited_once_with(ctx)
+ @mock.patch.object(silence.Silence, "_get_related_text_channel")
+ async def test_send_message(self, mock_get_related_text_channel):
+ """Test the send function reports to the correct channels."""
+ text_channel_1 = MockTextChannel()
+ text_channel_2 = MockTextChannel()
+
+ voice_channel = MockVoiceChannel()
+ voice_channel.name = "General/Offtopic"
+ voice_channel.mention = f"#{voice_channel.name}"
+
+ mock_get_related_text_channel.return_value = text_channel_2
+
+ def reset():
+ text_channel_1.reset_mock()
+ text_channel_2.reset_mock()
+ voice_channel.reset_mock()
+
+ with self.subTest("Basic One Channel Test"):
+ await self.cog.send_message("Text basic message.", text_channel_1, text_channel_2, False)
+ text_channel_1.send.assert_called_once_with("Text basic message.")
+ text_channel_2.send.assert_not_called()
+
+ reset()
+ with self.subTest("Basic Two Channel Test"):
+ await self.cog.send_message("Text basic message.", text_channel_1, text_channel_2, True)
+ text_channel_1.send.assert_called_once_with("Text basic message.")
+ text_channel_2.send.assert_called_once_with("Text basic message.")
+
+ reset()
+ with self.subTest("Replacement One Channel Test"):
+ message = "Current. The following should be replaced: current channel."
+ await self.cog.send_message(message, text_channel_1, text_channel_2, False)
+
+ text_channel_1.send.assert_called_once_with(message.replace("current channel", text_channel_1.mention))
+ text_channel_2.send.assert_not_called()
+
+ reset()
+ with self.subTest("Replacement Two Channel Test"):
+ message = "Current. The following should be replaced: current channel."
+ await self.cog.send_message(message, text_channel_1, text_channel_2, True)
+
+ text_channel_1.send.assert_called_once_with(message.replace("current channel", text_channel_1.mention))
+ text_channel_2.send.assert_called_once_with(message)
+
+ reset()
+ with self.subTest("Text and Voice"):
+ await self.cog.send_message("This should show up just here", text_channel_1, voice_channel, False)
+ text_channel_1.send.assert_called_once_with("This should show up just here")
+
+ reset()
+ with self.subTest("Text and Voice"):
+ message = "This should show up as current channel"
+ await self.cog.send_message(message, text_channel_1, voice_channel, True)
+ text_channel_1.send.assert_called_once_with(message.replace("current channel", voice_channel.mention))
+ text_channel_2.send.assert_called_once_with(message.replace("current channel", voice_channel.mention))
+
+ reset()
+ with self.subTest("Text and Voice Same Invocation"):
+ message = "This should show up as current channel"
+ await self.cog.send_message(message, text_channel_2, voice_channel, True)
+ text_channel_2.send.assert_called_once_with(message.replace("current channel", voice_channel.mention))
+
+ async def test_get_related_text_channel(self):
+ """Tests the helper function that connects voice to text channels."""
+ voice_channel = MockVoiceChannel()
+
+ tests = (
+ ("Off-Topic/General", Channels.voice_chat),
+ ("code/help 1", Channels.code_help_voice),
+ ("Staff", Channels.staff_voice),
+ ("ADMIN", Channels.admins_voice),
+ ("not in the channel list", None)
+ )
+
+ with mock.patch.object(self.cog.bot, "get_channel", lambda x: x):
+ for (name, channel_id) in tests:
+ voice_channel.name = name
+ voice_channel.id = channel_id
+
+ result_id = await self.cog._get_related_text_channel(voice_channel)
+ self.assertEqual(result_id, channel_id)
+
+ async def test_force_voice_sync(self):
+ """Tests the _force_voice_sync helper function."""
+ await self.cog._async_init()
+
+ members = []
+ for _ in range(10):
+ members.append(MockMember())
+
+ afk_channel = MockVoiceChannel()
+ channel = MockVoiceChannel(guild=MockGuild(afk_channel=afk_channel), members=members)
+
+ await self.cog._force_voice_sync(channel)
+ for member in members:
+ self.assertEqual(member.move_to.call_count, 2)
+ calls = [
+ mock.call(afk_channel, reason="Muting VC member."),
+ mock.call(channel, reason="Muting VC member.")
+ ]
+ member.move_to.assert_has_calls(calls, any_order=False)
+
+ async def test_force_voice_sync_staff(self):
+ """Tests to ensure _force_voice_sync does not kick staff members."""
+ await self.cog._async_init()
+ member = MockMember(roles=[self.cog._helper_role])
+
+ await self.cog._force_voice_sync(MockVoiceChannel(members=[member]))
+ member.move_to.assert_not_called()
+
+ async def test_force_voice_sync_no_channel(self):
+ """Test to ensure _force_voice_sync can create its own voice channel if one is not available."""
+ await self.cog._async_init()
+
+ channel = MockVoiceChannel(guild=MockGuild(afk_channel=None))
+ new_channel = MockVoiceChannel(delete=AsyncMock())
+ channel.guild.create_voice_channel.return_value = new_channel
+
+ await self.cog._force_voice_sync(channel)
+
+ # Check channel creation
+ overwrites = {
+ channel.guild.default_role: PermissionOverwrite(speak=False, connect=False, view_channel=False)
+ }
+ channel.guild.create_voice_channel.assert_called_once_with("mute-temp", overwrites=overwrites)
+
+ # Check bot deleted channel
+ new_channel.delete.assert_called_once_with(reason="Deleting temp mute channel.")
+
+ async def test_voice_kick(self):
+ """Test to ensure kick function can remove all members from a voice channel."""
+ await self.cog._async_init()
+
+ members = []
+ for _ in range(10):
+ members.append(MockMember())
+
+ channel = MockVoiceChannel(members=members)
+ await self.cog._kick_voice_members(channel)
+
+ for member in members:
+ member.move_to.assert_called_once_with(None, reason="Kicking member from voice channel.")
+
+ async def test_voice_kick_staff(self):
+ """Test to ensure voice kick skips staff members."""
+ await self.cog._async_init()
+ member = MockMember(roles=[self.cog._helper_role])
+
+ await self.cog._kick_voice_members(MockVoiceChannel(members=[member]))
+ member.move_to.assert_not_called()
+
@autospec(silence.Silence, "previous_overwrites", "unsilence_timestamps", pass_mocks=False)
class RescheduleTests(unittest.IsolatedAsyncioTestCase):
@@ -261,9 +412,13 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
asyncio.run(self.cog._async_init()) # Populate instance attributes.
- self.channel = MockTextChannel()
- self.overwrite = PermissionOverwrite(stream=True, send_messages=True, add_reactions=False)
- self.channel.overwrites_for.return_value = self.overwrite
+ self.text_channel = MockTextChannel()
+ self.text_overwrite = PermissionOverwrite(stream=True, send_messages=True, add_reactions=False)
+ self.text_channel.overwrites_for.return_value = self.text_overwrite
+
+ self.voice_channel = MockVoiceChannel()
+ self.voice_overwrite = PermissionOverwrite(speak=True)
+ self.voice_channel.overwrites_for.return_value = self.voice_overwrite
async def test_sent_correct_message(self):
"""Appropriate failure/success message was sent by the command."""
@@ -277,7 +432,60 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
with mock.patch.object(self.cog, "_set_silence_overwrites", return_value=was_silenced):
with self.subTest(was_silenced=was_silenced, message=message, duration=duration):
await self.cog.silence.callback(self.cog, ctx, duration)
- ctx.send.assert_called_once_with(message)
+ ctx.channel.send.assert_called_once_with(message)
+
+ @mock.patch.object(silence.Silence, "_set_silence_overwrites", return_value=True)
+ @mock.patch.object(silence.Silence, "_force_voice_sync")
+ async def test_sent_to_correct_channel(self, voice_sync, _):
+ """Test function sends messages to the correct channels."""
+ text_channel = MockTextChannel()
+ voice_channel = MockVoiceChannel()
+ ctx = MockContext()
+
+ test_cases = (
+ (None, silence.MSG_SILENCE_SUCCESS.format(duration=10)),
+ (text_channel, silence.MSG_SILENCE_SUCCESS.format(duration=10)),
+ (voice_channel, silence.MSG_SILENCE_SUCCESS.format(duration=10)),
+ (ctx.channel, silence.MSG_SILENCE_SUCCESS.format(duration=10)),
+ )
+
+ for target, message in test_cases:
+ with self.subTest(target_channel=target, message=message):
+ await self.cog.silence.callback(self.cog, ctx, 10, False, channel=target)
+ if ctx.channel == target or target is None:
+ ctx.channel.send.assert_called_once_with(message)
+
+ else:
+ ctx.channel.send.assert_called_once_with(message.replace("current channel", target.mention))
+ if isinstance(target, MockTextChannel):
+ target.send.assert_called_once_with(message)
+ else:
+ voice_sync.assert_called_once_with(target)
+
+ ctx.channel.send.reset_mock()
+ if target is not None and isinstance(target, MockTextChannel):
+ target.send.reset_mock()
+
+ @mock.patch.object(silence.Silence, "_kick_voice_members")
+ @mock.patch.object(silence.Silence, "_force_voice_sync")
+ async def test_sync_or_kick_called(self, sync, kick):
+ """Tests if silence command calls kick or sync on voice channels when appropriate."""
+ channel = MockVoiceChannel()
+ ctx = MockContext()
+
+ with mock.patch.object(self.cog, "_set_silence_overwrites", return_value=True):
+ with self.subTest("Test calls kick"):
+ await self.cog.silence.callback(self.cog, ctx, 10, kick=True, channel=channel)
+ kick.assert_called_once_with(channel)
+ sync.assert_not_called()
+
+ kick.reset_mock()
+ sync.reset_mock()
+
+ with self.subTest("Test calls sync"):
+ await self.cog.silence.callback(self.cog, ctx, 10, kick=False, channel=channel)
+ sync.assert_called_once_with(channel)
+ kick.assert_not_called()
async def test_skipped_already_silenced(self):
"""Permissions were not set and `False` was returned for an already silenced channel."""
@@ -298,19 +506,19 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
async def test_silenced_channel(self):
"""Channel had `send_message` and `add_reactions` permissions revoked for verified role."""
- self.assertTrue(await self.cog._set_silence_overwrites(self.channel))
- self.assertFalse(self.overwrite.send_messages)
- self.assertFalse(self.overwrite.add_reactions)
- self.channel.set_permissions.assert_awaited_once_with(
- self.cog._verified_role,
- overwrite=self.overwrite
+ self.assertTrue(await self.cog._set_silence_overwrites(self.text_channel))
+ self.assertFalse(self.text_overwrite.send_messages)
+ self.assertFalse(self.text_overwrite.add_reactions)
+ self.text_channel.set_permissions.assert_awaited_once_with(
+ self.cog._verified_msg_role,
+ overwrite=self.text_overwrite
)
async def test_preserved_other_overwrites(self):
"""Channel's other unrelated overwrites were not changed."""
- prev_overwrite_dict = dict(self.overwrite)
- await self.cog._set_silence_overwrites(self.channel)
- new_overwrite_dict = dict(self.overwrite)
+ prev_overwrite_dict = dict(self.text_overwrite)
+ await self.cog._set_silence_overwrites(self.text_channel)
+ new_overwrite_dict = dict(self.text_overwrite)
# Remove 'send_messages' & 'add_reactions' keys because they were changed by the method.
del prev_overwrite_dict['send_messages']
@@ -341,8 +549,8 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
async def test_cached_previous_overwrites(self):
"""Channel's previous overwrites were cached."""
overwrite_json = '{"send_messages": true, "add_reactions": false}'
- await self.cog._set_silence_overwrites(self.channel)
- self.cog.previous_overwrites.set.assert_called_once_with(self.channel.id, overwrite_json)
+ await self.cog._set_silence_overwrites(self.text_channel)
+ self.cog.previous_overwrites.set.assert_called_once_with(self.text_channel.id, overwrite_json)
@autospec(silence, "datetime")
async def test_cached_unsilence_time(self, datetime_mock):
@@ -352,7 +560,7 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
timestamp = now_timestamp + duration * 60
datetime_mock.now.return_value = datetime.fromtimestamp(now_timestamp, tz=timezone.utc)
- ctx = MockContext(channel=self.channel)
+ 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)
@@ -360,26 +568,37 @@ class SilenceTests(unittest.IsolatedAsyncioTestCase):
async def test_cached_indefinite_time(self):
"""A value of -1 was cached for a permanent silence."""
- ctx = MockContext(channel=self.channel)
+ ctx = MockContext(channel=self.text_channel)
await self.cog.silence.callback(self.cog, ctx, None)
self.cog.unsilence_timestamps.set.assert_awaited_once_with(ctx.channel.id, -1)
async def test_scheduled_task(self):
"""An unsilence task was scheduled."""
- ctx = MockContext(channel=self.channel, invoke=mock.MagicMock())
+ ctx = MockContext(channel=self.text_channel, invoke=mock.MagicMock())
await self.cog.silence.callback(self.cog, ctx, 5)
args = (300, ctx.channel.id, ctx.invoke.return_value)
self.cog.scheduler.schedule_later.assert_called_once_with(*args)
- ctx.invoke.assert_called_once_with(self.cog.unsilence)
+ ctx.invoke.assert_called_once_with(self.cog.unsilence, channel=ctx.channel)
async def test_permanent_not_scheduled(self):
"""A task was not scheduled for a permanent silence."""
- ctx = MockContext(channel=self.channel)
+ ctx = MockContext(channel=self.text_channel)
await self.cog.silence.callback(self.cog, ctx, None)
self.cog.scheduler.schedule_later.assert_not_called()
+ async def test_correct_permission_updates(self):
+ """Tests if _set_silence_overwrites can correctly get and update permissions."""
+ self.assertTrue(await self.cog._set_silence_overwrites(self.text_channel))
+ self.assertFalse(self.text_overwrite.send_messages or self.text_overwrite.add_reactions)
+
+ self.assertTrue(await self.cog._set_silence_overwrites(self.voice_channel))
+ self.assertFalse(self.voice_overwrite.speak)
+
+ self.assertTrue(await self.cog._set_silence_overwrites(self.voice_channel, True))
+ self.assertFalse(self.voice_overwrite.speak or self.voice_overwrite.connect)
+
@autospec(silence.Silence, "unsilence_timestamps", pass_mocks=False)
class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
@@ -422,6 +641,42 @@ class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
await self.cog.unsilence.callback(self.cog, ctx)
ctx.channel.send.assert_called_once_with(message)
+ async def test_sent_to_correct_channel(self):
+ """Test function sends messages to the correct channels."""
+ unsilenced_overwrite = PermissionOverwrite(send_messages=True, add_reactions=True)
+ text_channel = MockTextChannel()
+ ctx = MockContext()
+
+ test_cases = (
+ (None, silence.MSG_UNSILENCE_SUCCESS.format(duration=10)),
+ (text_channel, silence.MSG_UNSILENCE_SUCCESS.format(duration=10)),
+ (ctx.channel, silence.MSG_UNSILENCE_SUCCESS.format(duration=10)),
+ )
+
+ for target, message in test_cases:
+ with self.subTest(target_channel=target, message=message):
+ with mock.patch.object(self.cog, "_unsilence", return_value=True):
+ # Assign Return
+ if ctx.channel == target or target is None:
+ ctx.channel.overwrites_for.return_value = unsilenced_overwrite
+ else:
+ target.overwrites_for.return_value = unsilenced_overwrite
+
+ await self.cog.unsilence.callback(self.cog, ctx, channel=target)
+
+ # Check Messages
+ if ctx.channel == target or target is None:
+ ctx.channel.send.assert_called_once_with(message)
+ else:
+ ctx.channel.send.assert_called_once_with(
+ message.replace("current channel", text_channel.mention)
+ )
+ target.send.assert_called_once_with(message)
+
+ ctx.channel.send.reset_mock()
+ if target is not None:
+ target.send.reset_mock()
+
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
@@ -435,7 +690,7 @@ class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
"""Channel's `send_message` and `add_reactions` overwrites were restored."""
await self.cog._unsilence(self.channel)
self.channel.set_permissions.assert_awaited_once_with(
- self.cog._verified_role,
+ self.cog._verified_msg_role,
overwrite=self.overwrite,
)
@@ -449,7 +704,7 @@ class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
await self.cog._unsilence(self.channel)
self.channel.set_permissions.assert_awaited_once_with(
- self.cog._verified_role,
+ self.cog._verified_msg_role,
overwrite=self.overwrite,
)
@@ -462,6 +717,10 @@ class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
await self.cog._unsilence(self.channel)
self.cog._mod_alerts_channel.send.assert_awaited_once()
+ self.cog._mod_alerts_channel.send.reset_mock()
+
+ 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`."""
@@ -500,3 +759,86 @@ class UnsilenceTests(unittest.IsolatedAsyncioTestCase):
del new_overwrite_dict['add_reactions']
self.assertDictEqual(prev_overwrite_dict, new_overwrite_dict)
+
+ @mock.patch.object(silence.Silence, "_unsilence", return_value=False)
+ @mock.patch.object(silence.Silence, "send_message")
+ async def test_unsilence_helper_fail(self, send_message, _):
+ """Tests unsilence_wrapper when `_unsilence` fails."""
+ ctx = MockContext()
+
+ text_channel = MockTextChannel()
+ text_role = self.cog.bot.get_guild(Guild.id).get_role(Roles.verified)
+
+ voice_channel = MockVoiceChannel()
+ voice_role = self.cog.bot.get_guild(Guild.id).get_role(Roles.voice_verified)
+
+ test_cases = (
+ (ctx, text_channel, text_role, True, silence.MSG_UNSILENCE_FAIL),
+ (ctx, text_channel, text_role, False, silence.MSG_UNSILENCE_MANUAL),
+ (ctx, voice_channel, voice_role, True, silence.MSG_UNSILENCE_FAIL),
+ (ctx, voice_channel, voice_role, False, silence.MSG_UNSILENCE_MANUAL),
+ )
+
+ class PermClass:
+ """Class to Mock return permissions"""
+ def __init__(self, value: bool):
+ self.val = value
+
+ def __getattr__(self, item):
+ return self.val
+
+ for context, channel, role, permission, message in test_cases:
+ with mock.patch.object(channel, "overwrites_for", return_value=PermClass(permission)) as overwrites:
+ with self.subTest(channel=channel, message=message):
+ await self.cog._unsilence_wrapper(channel, context)
+
+ overwrites.assert_called_once_with(role)
+ send_message.assert_called_once_with(message, ctx.channel, channel)
+
+ send_message.reset_mock()
+
+ @mock.patch.object(silence.Silence, "_force_voice_sync")
+ @mock.patch.object(silence.Silence, "send_message")
+ async def test_correct_overwrites(self, send_message, _):
+ """Tests the overwrites returned by the _unsilence_wrapper are correct for voice and text channels."""
+ ctx = MockContext()
+
+ text_channel = MockTextChannel()
+ text_role = self.cog.bot.get_guild(Guild.id).get_role(Roles.verified)
+
+ voice_channel = MockVoiceChannel()
+ voice_role = self.cog.bot.get_guild(Guild.id).get_role(Roles.voice_verified)
+
+ async def reset():
+ await text_channel.set_permissions(text_role, PermissionOverwrite(send_messages=False, add_reactions=False))
+ await voice_channel.set_permissions(voice_role, PermissionOverwrite(speak=False, connect=False))
+
+ text_channel.reset_mock()
+ voice_channel.reset_mock()
+ send_message.reset_mock()
+ await reset()
+
+ default_text_overwrites = text_channel.overwrites_for(text_role)
+ default_voice_overwrites = voice_channel.overwrites_for(voice_role)
+
+ test_cases = (
+ (ctx, text_channel, text_role, default_text_overwrites, silence.MSG_UNSILENCE_SUCCESS),
+ (ctx, voice_channel, voice_role, default_voice_overwrites, silence.MSG_UNSILENCE_SUCCESS),
+ (ctx, ctx.channel, text_role, ctx.channel.overwrites_for(text_role), silence.MSG_UNSILENCE_SUCCESS),
+ (None, text_channel, text_role, default_text_overwrites, silence.MSG_UNSILENCE_SUCCESS),
+ )
+
+ for context, channel, role, overwrites, message in test_cases:
+ with self.subTest(ctx=context, channel=channel):
+ await self.cog._unsilence_wrapper(channel, context)
+
+ if context is None:
+ send_message.assert_called_once_with(message, channel, channel, True)
+ else:
+ send_message.assert_called_once_with(message, context.channel, channel, True)
+
+ channel.set_permissions.assert_called_once_with(role, overwrite=overwrites)
+ if channel != ctx.channel:
+ ctx.channel.send.assert_not_called()
+
+ await reset()
diff --git a/tests/helpers.py b/tests/helpers.py
index 870f66197..5628ca31f 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -16,7 +16,6 @@ from bot.async_stats import AsyncStatsClient
from bot.bot import Bot
from tests._autospec import autospec # noqa: F401 other modules import it via this module
-
for logger in logging.Logger.manager.loggerDict.values():
# Set all loggers to CRITICAL by default to prevent screen clutter during testing
@@ -320,7 +319,10 @@ channel_data = {
}
state = unittest.mock.MagicMock()
guild = unittest.mock.MagicMock()
-channel_instance = discord.TextChannel(state=state, guild=guild, data=channel_data)
+text_channel_instance = discord.TextChannel(state=state, guild=guild, data=channel_data)
+
+channel_data["type"] = "VoiceChannel"
+voice_channel_instance = discord.VoiceChannel(state=state, guild=guild, data=channel_data)
class MockTextChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin):
@@ -330,7 +332,24 @@ class MockTextChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin):
Instances of this class will follow the specifications of `discord.TextChannel` instances. For
more information, see the `MockGuild` docstring.
"""
- spec_set = channel_instance
+ spec_set = text_channel_instance
+
+ def __init__(self, **kwargs) -> None:
+ default_kwargs = {'id': next(self.discord_id), 'name': 'channel', 'guild': MockGuild()}
+ super().__init__(**collections.ChainMap(kwargs, default_kwargs))
+
+ if 'mention' not in kwargs:
+ self.mention = f"#{self.name}"
+
+
+class MockVoiceChannel(CustomMockMixin, unittest.mock.Mock, HashableMixin):
+ """
+ A MagicMock subclass to mock VoiceChannel objects.
+
+ Instances of this class will follow the specifications of `discord.VoiceChannel` instances. For
+ more information, see the `MockGuild` docstring.
+ """
+ spec_set = voice_channel_instance
def __init__(self, **kwargs) -> None:
default_kwargs = {'id': next(self.discord_id), 'name': 'channel', 'guild': MockGuild()}