diff options
| author | 2020-07-22 12:45:29 -0700 | |
|---|---|---|
| committer | 2020-07-22 12:45:29 -0700 | |
| commit | 0f6b980a7f9a71c30f044b60f0441f3d9c6a9395 (patch) | |
| tree | 199ff60aa57ff3c7eb9269621606afabd3193850 | |
| parent | Use max_units for time since join in user command instead of precision (diff) | |
| parent | Merge branch 'master' into jam-test (diff) | |
Merge pull request #957 from ks129/jam-test
Code Jams unit tests
| -rw-r--r-- | bot/cogs/jams.py | 59 | ||||
| -rw-r--r-- | tests/bot/cogs/test_jams.py | 151 |
2 files changed, 193 insertions, 17 deletions
diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py index 1d062b0c2..a48dbc49a 100644 --- a/bot/cogs/jams.py +++ b/bot/cogs/jams.py @@ -1,6 +1,7 @@ import logging +import typing as t -from discord import Member, PermissionOverwrite, utils +from discord import CategoryChannel, Guild, Member, PermissionOverwrite, Role, utils from discord.ext import commands from more_itertools import unique_everseen @@ -40,22 +41,39 @@ class CodeJams(commands.Cog): ) return - code_jam_category = utils.get(ctx.guild.categories, name="Code Jam") + team_channel = await self.create_channels(ctx.guild, team_name, members) + await self.add_roles(ctx.guild, members) + + await ctx.send( + f":ok_hand: Team created: {team_channel}\n" + f"**Team Leader:** {members[0].mention}\n" + f"**Team Members:** {' '.join(member.mention for member in members[1:])}" + ) + + @staticmethod + async def get_category(guild: Guild) -> CategoryChannel: + """Create a Code Jam category if it doesn't exist and return it.""" + code_jam_category = utils.get(guild.categories, name="Code Jam") if code_jam_category is None: log.info("Code Jam category not found, creating it.") category_overwrites = { - ctx.guild.default_role: PermissionOverwrite(read_messages=False), - ctx.guild.me: PermissionOverwrite(read_messages=True) + guild.default_role: PermissionOverwrite(read_messages=False), + guild.me: PermissionOverwrite(read_messages=True) } - code_jam_category = await ctx.guild.create_category_channel( + code_jam_category = await guild.create_category_channel( "Code Jam", overwrites=category_overwrites, reason="It's code jam time!" ) + return code_jam_category + + @staticmethod + def get_overwrites(members: t.List[Member], guild: Guild) -> t.Dict[t.Union[Member, Role], PermissionOverwrite]: + """Get Code Jam team channels permission overwrites.""" # First member is always the team leader team_channel_overwrites = { members[0]: PermissionOverwrite( @@ -64,8 +82,8 @@ class CodeJams(commands.Cog): manage_webhooks=True, connect=True ), - ctx.guild.default_role: PermissionOverwrite(read_messages=False, connect=False), - ctx.guild.get_role(Roles.verified): PermissionOverwrite( + guild.default_role: PermissionOverwrite(read_messages=False, connect=False), + guild.get_role(Roles.verified): PermissionOverwrite( read_messages=False, connect=False ) @@ -78,8 +96,16 @@ class CodeJams(commands.Cog): connect=True ) + return team_channel_overwrites + + async def create_channels(self, guild: Guild, team_name: str, members: t.List[Member]) -> str: + """Create team text and voice channels. Return the mention for the text channel.""" + # Get permission overwrites and category + team_channel_overwrites = self.get_overwrites(members, guild) + code_jam_category = await self.get_category(guild) + # Create a text channel for the team - team_channel = await ctx.guild.create_text_channel( + team_channel = await guild.create_text_channel( team_name, overwrites=team_channel_overwrites, category=code_jam_category @@ -88,26 +114,25 @@ class CodeJams(commands.Cog): # Create a voice channel for the team team_voice_name = " ".join(team_name.split("-")).title() - await ctx.guild.create_voice_channel( + await guild.create_voice_channel( team_voice_name, overwrites=team_channel_overwrites, category=code_jam_category ) + return team_channel.mention + + @staticmethod + async def add_roles(guild: Guild, members: t.List[Member]) -> None: + """Assign team leader and jammer roles.""" # Assign team leader role - await members[0].add_roles(ctx.guild.get_role(Roles.team_leaders)) + await members[0].add_roles(guild.get_role(Roles.team_leaders)) # Assign rest of roles - jammer_role = ctx.guild.get_role(Roles.jammers) + jammer_role = guild.get_role(Roles.jammers) for member in members: await member.add_roles(jammer_role) - await ctx.send( - f":ok_hand: Team created: {team_channel.mention}\n" - f"**Team Leader:** {members[0].mention}\n" - f"**Team Members:** {' '.join(member.mention for member in members[1:])}" - ) - def setup(bot: Bot) -> None: """Load the CodeJams cog.""" diff --git a/tests/bot/cogs/test_jams.py b/tests/bot/cogs/test_jams.py new file mode 100644 index 000000000..81fbcb798 --- /dev/null +++ b/tests/bot/cogs/test_jams.py @@ -0,0 +1,151 @@ +import unittest +from unittest.mock import AsyncMock, MagicMock, patch + +from bot.cogs.jams import CodeJams, setup +from bot.constants import Roles +from tests.helpers import MockBot, MockContext, MockGuild, MockMember, MockRole, MockTextChannel + + +class JamCreateTeamTests(unittest.IsolatedAsyncioTestCase): + """Tests for `createteam` command.""" + + def setUp(self): + self.bot = MockBot() + self.admin_role = MockRole(name="Admins", id=Roles.admins) + self.command_user = MockMember([self.admin_role]) + self.guild = MockGuild([self.admin_role]) + self.ctx = MockContext(bot=self.bot, author=self.command_user, guild=self.guild) + self.cog = CodeJams(self.bot) + + utils_patcher = patch("bot.cogs.jams.utils") + self.utils_mock = utils_patcher.start() + self.addCleanup(utils_patcher.stop) + + async def test_too_small_amount_of_team_members_passed(self): + """Should `ctx.send` and exit early when too small amount of members.""" + for case in (1, 2): + with self.subTest(amount_of_members=case): + self.cog.create_channels = AsyncMock() + self.cog.add_roles = AsyncMock() + + self.ctx.reset_mock() + self.utils_mock.reset_mock() + members = (MockMember() for _ in range(case)) + await self.cog.createteam(self.cog, self.ctx, "foo", members) + + self.ctx.send.assert_awaited_once() + self.cog.create_channels.assert_not_awaited() + self.cog.add_roles.assert_not_awaited() + + async def test_duplicate_members_provided(self): + """Should `ctx.send` and exit early because duplicate members provided and total there is only 1 member.""" + self.cog.create_channels = AsyncMock() + self.cog.add_roles = AsyncMock() + + member = MockMember() + await self.cog.createteam(self.cog, self.ctx, "foo", (member for _ in range(5))) + + self.ctx.send.assert_awaited_once() + self.cog.create_channels.assert_not_awaited() + self.cog.add_roles.assert_not_awaited() + + async def test_result_sending(self): + """Should call `ctx.send` when everything goes right.""" + self.cog.create_channels = AsyncMock() + self.cog.add_roles = AsyncMock() + + members = [MockMember() for _ in range(5)] + await self.cog.createteam(self.cog, self.ctx, "foo", members) + + self.cog.create_channels.assert_awaited_once() + self.cog.add_roles.assert_awaited_once() + self.ctx.send.assert_awaited_once() + + async def test_category_dont_exist(self): + """Should create code jam category.""" + self.utils_mock.get.return_value = None + + await self.cog.get_category(self.guild) + + self.guild.create_category_channel.assert_awaited_once() + category_overwrites = self.guild.create_category_channel.call_args[1]["overwrites"] + + self.assertFalse(category_overwrites[self.guild.default_role].read_messages) + self.assertTrue(category_overwrites[self.guild.me].read_messages) + + async def test_category_channel_exist(self): + """Should not try to create category channel.""" + await self.cog.get_category(self.guild) + self.guild.create_category_channel.assert_not_awaited() + + async def test_channel_overwrites(self): + """Should have correct permission overwrites for users and roles.""" + leader = MockMember() + members = [leader] + [MockMember() for _ in range(4)] + overwrites = self.cog.get_overwrites(members, self.guild) + + # Leader permission overwrites + self.assertTrue(overwrites[leader].manage_messages) + self.assertTrue(overwrites[leader].read_messages) + self.assertTrue(overwrites[leader].manage_webhooks) + self.assertTrue(overwrites[leader].connect) + + # Other members permission overwrites + for member in members[1:]: + self.assertTrue(overwrites[member].read_messages) + self.assertTrue(overwrites[member].connect) + + # Everyone and verified role overwrite + self.assertFalse(overwrites[self.guild.default_role].read_messages) + self.assertFalse(overwrites[self.guild.default_role].connect) + self.assertFalse(overwrites[self.guild.get_role(Roles.verified)].read_messages) + self.assertFalse(overwrites[self.guild.get_role(Roles.verified)].connect) + + async def test_team_channels_creation(self): + """Should create new voice and text channel for team.""" + self.utils_mock.get.return_value = "foo" + members = [MockMember() for _ in range(5)] + + self.cog.get_overwrites = MagicMock() + self.cog.get_category = AsyncMock() + self.ctx.guild.create_text_channel.return_value = MockTextChannel(mention="foobar-channel") + actual = await self.cog.create_channels(self.guild, "my-team", members) + + self.assertEqual("foobar-channel", actual) + self.cog.get_overwrites.assert_called_once_with(members, self.guild) + self.cog.get_category.assert_awaited_once_with(self.guild) + + self.guild.create_text_channel.assert_awaited_once_with( + "my-team", + overwrites=self.cog.get_overwrites.return_value, + category=self.cog.get_category.return_value + ) + self.guild.create_voice_channel.assert_awaited_once_with( + "My Team", + overwrites=self.cog.get_overwrites.return_value, + category=self.cog.get_category.return_value + ) + + async def test_jam_roles_adding(self): + """Should add team leader role to leader and jam role to every team member.""" + leader_role = MockRole(name="Team Leader") + jam_role = MockRole(name="Jammer") + self.guild.get_role.side_effect = [leader_role, jam_role] + + leader = MockMember() + members = [leader] + [MockMember() for _ in range(4)] + await self.cog.add_roles(self.guild, members) + + leader.add_roles.assert_any_await(leader_role) + for member in members: + member.add_roles.assert_any_await(jam_role) + + +class CodeJamSetup(unittest.TestCase): + """Test for `setup` function of `CodeJam` cog.""" + + def test_setup(self): + """Should call `bot.add_cog`.""" + bot = MockBot() + setup(bot) + bot.add_cog.assert_called_once() |