aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps
diff options
context:
space:
mode:
authorGravatar Gareth Coles <[email protected]>2019-10-04 22:31:19 +0100
committerGravatar Gareth Coles <[email protected]>2019-10-04 22:31:19 +0100
commit292aed85964ae5ed226d329d61c49d4c67bf6c81 (patch)
tree5eb7e37ffaf2e3629f7f43970cd52e9786bfd034 /pydis_site/apps
parentMerge branch 'master' into #201-django-allauth (diff)
Tests for signal handlers
Diffstat (limited to 'pydis_site/apps')
-rw-r--r--pydis_site/apps/home/signals.py13
-rw-r--r--pydis_site/apps/home/tests/test_signal_listener.py283
2 files changed, 291 insertions, 5 deletions
diff --git a/pydis_site/apps/home/signals.py b/pydis_site/apps/home/signals.py
index 687a99fe..691332d2 100644
--- a/pydis_site/apps/home/signals.py
+++ b/pydis_site/apps/home/signals.py
@@ -1,4 +1,4 @@
-from typing import List, Optional, Type
+from typing import List, Type
from allauth.account.signals import user_logged_in
from allauth.socialaccount.models import SocialAccount, SocialLogin
@@ -42,9 +42,9 @@ class SignalListener:
social_account_updated.connect(self.social_account_updated)
social_account_removed.connect(self.social_account_removed)
- user_logged_in.connect(self.user_logged_in)
+ user_logged_in.connect(self.user_login)
- def user_logged_in(self, sender: Type[DjangoUser], **kwargs) -> None:
+ def user_login(self, sender: Type[DjangoUser], **kwargs) -> None:
"""Handles signals relating to Allauth logins."""
user: DjangoUser = kwargs["user"]
@@ -54,6 +54,7 @@ class SignalListener:
)
except SocialAccount.DoesNotExist:
return # User's never linked a Discord account
+
try:
discord_user: DiscordUser = DiscordUser.objects.get(id=int(account.uid))
except DiscordUser.DoesNotExist:
@@ -106,14 +107,16 @@ class SignalListener:
return
try:
- account: SocialAccount = SocialAccount.objects.get(uid=str(instance.id))
+ account: SocialAccount = SocialAccount.objects.get(
+ uid=str(instance.id), provider=DiscordProvider.id
+ )
except SocialAccount.DoesNotExist:
return # User has never logged in with Discord on the site
self._apply_groups(instance, account)
def _apply_groups(
- self, user: DiscordUser, account: Optional[SocialAccount], deletion: bool = False
+ self, user: DiscordUser, account: SocialAccount, deletion: bool = False
) -> None:
mappings = RoleMapping.objects.all()
diff --git a/pydis_site/apps/home/tests/test_signal_listener.py b/pydis_site/apps/home/tests/test_signal_listener.py
new file mode 100644
index 00000000..507714b9
--- /dev/null
+++ b/pydis_site/apps/home/tests/test_signal_listener.py
@@ -0,0 +1,283 @@
+from unittest import mock
+
+from allauth.account.signals import user_logged_in
+from allauth.socialaccount.models import SocialAccount, SocialLogin
+from allauth.socialaccount.providers.discord.provider import DiscordProvider
+from allauth.socialaccount.providers.github.provider import GitHubProvider
+from allauth.socialaccount.signals import (
+ pre_social_login, social_account_added, social_account_removed,
+ social_account_updated)
+from django.contrib.auth.models import Group, User as DjangoUser
+from django.db.models.signals import post_save
+from django.test import TestCase
+
+from pydis_site.apps.api.models import Role, User as DiscordUser
+from pydis_site.apps.home.signals import SignalListener
+from pydis_site.apps.staff.models import RoleMapping
+
+
+class SignalListenerTests(TestCase):
+ @classmethod
+ def setUpTestData(cls):
+ cls.admin_role = Role.objects.create(
+ id=0,
+ name="admin",
+ colour=0,
+ permissions=0,
+ position=0
+ )
+
+ cls.admin_group = Group.objects.create(
+ name="admin"
+ )
+
+ cls.role_mapping = RoleMapping.objects.create(
+ role=cls.admin_role,
+ group=cls.admin_group
+ )
+
+ cls.unmapped_role = Role.objects.create(
+ id=1,
+ name="unmapped",
+ colour=0,
+ permissions=0,
+ position=1
+ )
+
+ cls.discord_user = DiscordUser.objects.create(
+ id=0,
+ name="user",
+ discriminator=0,
+ avatar_hash=None
+ )
+
+ cls.discord_unmapped = DiscordUser.objects.create(
+ id=2,
+ name="unmapped",
+ discriminator=0,
+ avatar_hash=None
+ )
+
+ cls.discord_unmapped.roles.add(cls.unmapped_role)
+ cls.discord_unmapped.save()
+
+ cls.discord_not_in_guild = DiscordUser.objects.create(
+ id=3,
+ name="not-in-guild",
+ discriminator=0,
+ avatar_hash=None,
+ in_guild=False
+ )
+
+ cls.discord_admin = DiscordUser.objects.create(
+ id=1,
+ name="admin",
+ discriminator=0,
+ avatar_hash=None
+ )
+
+ cls.discord_admin.roles.set([cls.admin_role])
+ cls.discord_admin.save()
+
+ cls.django_user_discordless = DjangoUser.objects.create(
+ username="no-discord"
+ )
+
+ cls.django_user_never_joined = DjangoUser.objects.create(
+ username="never-joined"
+ )
+
+ cls.social_never_joined = SocialAccount.objects.create(
+ user=cls.django_user_never_joined,
+ provider=DiscordProvider.id,
+ uid=5
+ )
+
+ cls.django_user = DjangoUser.objects.create(
+ username="user"
+ )
+
+ cls.social_user = SocialAccount.objects.create(
+ user=cls.django_user,
+ provider=DiscordProvider.id,
+ uid=cls.discord_user.id
+ )
+
+ cls.social_user_github = SocialAccount.objects.create(
+ user=cls.django_user,
+ provider=GitHubProvider.id,
+ uid=cls.discord_user.id
+ )
+
+ cls.social_unmapped = SocialAccount(
+ # We instantiate it and don't put it in the DB, this is (surprisingly)
+ # a realistic test case, so we need to check for it
+
+ provider=DiscordProvider.id,
+ uid=5,
+ user_id=None # Doesn't exist (yes, this is possible)
+ )
+
+ cls.django_admin = DjangoUser.objects.create(
+ username="admin",
+ is_staff=True,
+ is_superuser=True
+ )
+
+ cls.social_admin = SocialAccount.objects.create(
+ user=cls.django_admin,
+ provider=DiscordProvider.id,
+ uid=cls.discord_admin.id
+ )
+
+ def test_model_save(self):
+ mock_obj = mock.Mock()
+
+ with mock.patch.object(SignalListener, "_apply_groups", mock_obj):
+ _ = SignalListener()
+
+ post_save.send(
+ DiscordUser,
+ instance=self.discord_user,
+ raw=True,
+ created=None, # Not realistic, but we don't use it
+ using=None, # Again, we don't use it
+ update_fields=False # Always false during integration testing
+ )
+
+ mock_obj.assert_not_called()
+
+ post_save.send(
+ DiscordUser,
+ instance=self.discord_user,
+ raw=False,
+ created=None, # Not realistic, but we don't use it
+ using=None, # Again, we don't use it
+ update_fields=False # Always false during integration testing
+ )
+
+ mock_obj.assert_called_with(self.discord_user, self.social_user)
+
+ def test_pre_social_login(self):
+ mock_obj = mock.Mock()
+
+ discord_login = SocialLogin(self.django_user, self.social_user)
+ github_login = SocialLogin(self.django_user, self.social_user_github)
+ unmapped_login = SocialLogin(self.django_user, self.social_unmapped)
+
+ with mock.patch.object(SignalListener, "_apply_groups", mock_obj):
+ _ = SignalListener()
+
+ pre_social_login.send(SocialLogin, sociallogin=github_login)
+ mock_obj.assert_not_called()
+
+ pre_social_login.send(SocialLogin, sociallogin=unmapped_login)
+ mock_obj.assert_not_called()
+
+ pre_social_login.send(SocialLogin, sociallogin=discord_login)
+ mock_obj.assert_called_with(self.discord_user, self.social_user)
+
+ def test_social_added(self):
+ mock_obj = mock.Mock()
+
+ discord_login = SocialLogin(self.django_user, self.social_user)
+ github_login = SocialLogin(self.django_user, self.social_user_github)
+ unmapped_login = SocialLogin(self.django_user, self.social_unmapped)
+
+ with mock.patch.object(SignalListener, "_apply_groups", mock_obj):
+ _ = SignalListener()
+
+ social_account_added.send(SocialLogin, sociallogin=github_login)
+ mock_obj.assert_not_called()
+
+ social_account_added.send(SocialLogin, sociallogin=unmapped_login)
+ mock_obj.assert_not_called()
+
+ social_account_added.send(SocialLogin, sociallogin=discord_login)
+ mock_obj.assert_called_with(self.discord_user, self.social_user)
+
+ def test_social_updated(self):
+ mock_obj = mock.Mock()
+
+ discord_login = SocialLogin(self.django_user, self.social_user)
+ github_login = SocialLogin(self.django_user, self.social_user_github)
+ unmapped_login = SocialLogin(self.django_user, self.social_unmapped)
+
+ with mock.patch.object(SignalListener, "_apply_groups", mock_obj):
+ _ = SignalListener()
+
+ social_account_updated.send(SocialLogin, sociallogin=github_login)
+ mock_obj.assert_not_called()
+
+ social_account_updated.send(SocialLogin, sociallogin=unmapped_login)
+ mock_obj.assert_not_called()
+
+ social_account_updated.send(SocialLogin, sociallogin=discord_login)
+ mock_obj.assert_called_with(self.discord_user, self.social_user)
+
+ def test_social_removed(self):
+ mock_obj = mock.Mock()
+
+ with mock.patch.object(SignalListener, "_apply_groups", mock_obj):
+ _ = SignalListener()
+
+ social_account_removed.send(SocialLogin, socialaccount=self.social_user_github)
+ mock_obj.assert_not_called()
+
+ social_account_removed.send(SocialLogin, socialaccount=self.social_unmapped)
+ mock_obj.assert_not_called()
+
+ social_account_removed.send(SocialLogin, socialaccount=self.social_user)
+ mock_obj.assert_called_with(self.discord_user, self.social_user, True)
+
+ def test_logged_in(self):
+ mock_obj = mock.Mock()
+
+ with mock.patch.object(SignalListener, "_apply_groups", mock_obj):
+ _ = SignalListener()
+
+ user_logged_in.send(DjangoUser, user=self.django_user_discordless)
+ mock_obj.assert_not_called()
+
+ user_logged_in.send(DjangoUser, user=self.django_user_never_joined)
+ mock_obj.assert_not_called()
+
+ user_logged_in.send(DjangoUser, user=self.django_user)
+ mock_obj.assert_called_with(self.discord_user, self.social_user)
+
+ def test_apply_groups_admin(self):
+ handler = SignalListener()
+
+ self.assertTrue(self.django_user_discordless.groups.all().count() == 0)
+
+ handler._apply_groups(self.discord_admin, self.social_admin)
+ self.assertTrue(self.admin_group in self.django_admin.groups.all())
+
+ handler._apply_groups(self.discord_admin, self.social_admin, True)
+ self.assertTrue(self.admin_group not in self.django_admin.groups.all())
+
+ handler._apply_groups(self.discord_admin, self.social_admin)
+ self.discord_admin.roles.clear()
+
+ handler._apply_groups(self.discord_admin, self.social_admin)
+ self.assertTrue(self.django_user_discordless.groups.all().count() == 0)
+
+ self.discord_admin.roles.add(self.admin_role)
+ self.discord_admin.save()
+
+ def test_apply_groups_other(self):
+ handler = SignalListener()
+
+ self.assertTrue(self.django_user_discordless.groups.all().count() == 0)
+
+ handler._apply_groups(self.discord_unmapped, self.social_unmapped)
+ self.assertTrue(self.django_user_discordless.groups.all().count() == 0)
+
+ handler._apply_groups(self.discord_unmapped, self.social_user)
+ self.assertTrue(self.django_user.groups.all().count() == 0)
+
+ handler._apply_groups(self.discord_not_in_guild, self.social_user)
+ self.assertTrue(self.django_user.groups.all().count() == 0)
+
+ handler._apply_groups(self.discord_not_in_guild, self.social_user)
+ self.assertTrue(self.django_user.groups.all().count() == 0)