diff options
| author | 2020-10-30 17:43:13 +0200 | |
|---|---|---|
| committer | 2020-10-30 17:43:13 +0200 | |
| commit | 3fa3854eb192466ca59acb577bfe135b53c2da11 (patch) | |
| tree | af5aa98d8d521f21f25c45528c89941ac23e77aa /pydis_site/apps/home/tests | |
| parent | Cover fetching article GitHub information with tests (diff) | |
| parent | Merge pull request #395 from ks129/resources-home (diff) | |
Merge remote-tracking branch 'up/dewikification' into guides-app
Diffstat (limited to 'pydis_site/apps/home/tests')
| -rw-r--r-- | pydis_site/apps/home/tests/test_signal_listener.py | 458 | ||||
| -rw-r--r-- | pydis_site/apps/home/tests/test_views.py | 213 | 
2 files changed, 1 insertions, 670 deletions
| diff --git a/pydis_site/apps/home/tests/test_signal_listener.py b/pydis_site/apps/home/tests/test_signal_listener.py deleted file mode 100644 index d99d81a5..00000000 --- a/pydis_site/apps/home/tests/test_signal_listener.py +++ /dev/null @@ -1,458 +0,0 @@ -from unittest import mock - -from allauth.account.signals import user_logged_in -from allauth.socialaccount.models import SocialAccount, SocialLogin -from allauth.socialaccount.providers import registry -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, pre_save -from django.test import TestCase - -from pydis_site.apps.api.models import Role, User as DiscordUser -from pydis_site.apps.home.signals import AllauthSignalListener -from pydis_site.apps.staff.models import RoleMapping - - -class SignalListenerTests(TestCase): -    @classmethod -    def setUpTestData(cls): -        """ -        Executed when testing begins in order to set up database fixtures required for testing. - -        This sets up quite a lot of stuff, in order to try to cover every eventuality while -        ensuring that everything works when every possible situation is in the database -        at the same time. - -        That does unfortunately mean that half of this file is just test fixtures, but I couldn't -        think of a better way to do this. -        """ -        # This needs to be registered so we can test the role linking logic with a user that -        # doesn't have a Discord account linked, but is logged in somehow with another account -        # type anyway. The logic this is testing was designed so that the system would be -        # robust enough to handle that case, but it's impossible to fully test (and therefore -        # to have coverage of) those lines without an extra provider, and GH was the second -        # provider it was built with in mind. -        registry.register(GitHubProvider) - -        cls.admin_role = Role.objects.create( -            id=0, -            name="admin", -            colour=0, -            permissions=0, -            position=0 -        ) - -        cls.moderator_role = Role.objects.create( -            id=1, -            name="moderator", -            colour=0, -            permissions=0, -            position=1 -        ) - -        cls.unmapped_role = Role.objects.create( -            id=2, -            name="unmapped", -            colour=0, -            permissions=0, -            position=1 -        ) - -        cls.admin_group = Group.objects.create(name="admin") -        cls.moderator_group = Group.objects.create(name="moderator") - -        cls.admin_mapping = RoleMapping.objects.create( -            role=cls.admin_role, -            group=cls.admin_group, -            is_staff=True -        ) - -        cls.moderator_mapping = RoleMapping.objects.create( -            role=cls.moderator_role, -            group=cls.moderator_group, -            is_staff=False -        ) - -        cls.discord_user = DiscordUser.objects.create( -            id=0, -            name="user", -            discriminator=0, -        ) - -        cls.discord_unmapped = DiscordUser.objects.create( -            id=2, -            name="unmapped", -            discriminator=0, -        ) - -        cls.discord_unmapped.roles.append(cls.unmapped_role.id) -        cls.discord_unmapped.save() - -        cls.discord_not_in_guild = DiscordUser.objects.create( -            id=3, -            name="not-in-guild", -            discriminator=0, -            in_guild=False -        ) - -        cls.discord_admin = DiscordUser.objects.create( -            id=1, -            name="admin", -            discriminator=0, -        ) - -        cls.discord_admin.roles = [cls.admin_role.id] -        cls.discord_admin.save() - -        cls.discord_moderator = DiscordUser.objects.create( -            id=4, -            name="admin", -            discriminator=0, -        ) - -        cls.discord_moderator.roles = [cls.moderator_role.id] -        cls.discord_moderator.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  # No relation exists at all -        ) - -        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 -        ) - -        cls.django_moderator = DjangoUser.objects.create( -            username="moderator", -            is_staff=False, -            is_superuser=False -        ) - -        cls.social_moderator = SocialAccount.objects.create( -            user=cls.django_moderator, -            provider=DiscordProvider.id, -            uid=cls.discord_moderator.id -        ) - -    def test_model_save(self): -        """Test signal handling for when Discord user model objects are saved to DB.""" -        mock_obj = mock.Mock() - -        with mock.patch.object(AllauthSignalListener, "_apply_groups", mock_obj): -            AllauthSignalListener() - -            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): -        """Test the pre-social-login Allauth signal handling.""" -        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(AllauthSignalListener, "_apply_groups", mock_obj): -            AllauthSignalListener() - -            # Don't attempt to apply groups if the user doesn't have a linked Discord account -            pre_social_login.send(SocialLogin, sociallogin=github_login) -            mock_obj.assert_not_called() - -            # Don't attempt to apply groups if the user hasn't joined the Discord server -            pre_social_login.send(SocialLogin, sociallogin=unmapped_login) -            mock_obj.assert_not_called() - -            # Attempt to apply groups if everything checks out -            pre_social_login.send(SocialLogin, sociallogin=discord_login) -            mock_obj.assert_called_with(self.discord_user, self.social_user) - -    def test_social_added(self): -        """Test the social-account-added Allauth signal handling.""" -        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(AllauthSignalListener, "_apply_groups", mock_obj): -            AllauthSignalListener() - -            # Don't attempt to apply groups if the user doesn't have a linked Discord account -            social_account_added.send(SocialLogin, sociallogin=github_login) -            mock_obj.assert_not_called() - -            # Don't attempt to apply groups if the user hasn't joined the Discord server -            social_account_added.send(SocialLogin, sociallogin=unmapped_login) -            mock_obj.assert_not_called() - -            # Attempt to apply groups if everything checks out -            social_account_added.send(SocialLogin, sociallogin=discord_login) -            mock_obj.assert_called_with(self.discord_user, self.social_user) - -    def test_social_updated(self): -        """Test the social-account-updated Allauth signal handling.""" -        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(AllauthSignalListener, "_apply_groups", mock_obj): -            AllauthSignalListener() - -            # Don't attempt to apply groups if the user doesn't have a linked Discord account -            social_account_updated.send(SocialLogin, sociallogin=github_login) -            mock_obj.assert_not_called() - -            # Don't attempt to apply groups if the user hasn't joined the Discord server -            social_account_updated.send(SocialLogin, sociallogin=unmapped_login) -            mock_obj.assert_not_called() - -            # Attempt to apply groups if everything checks out -            social_account_updated.send(SocialLogin, sociallogin=discord_login) -            mock_obj.assert_called_with(self.discord_user, self.social_user) - -    def test_social_removed(self): -        """Test the social-account-removed Allauth signal handling.""" -        mock_obj = mock.Mock() - -        with mock.patch.object(AllauthSignalListener, "_apply_groups", mock_obj): -            AllauthSignalListener() - -            # Don't attempt to remove groups if the user doesn't have a linked Discord account -            social_account_removed.send(SocialLogin, socialaccount=self.social_user_github) -            mock_obj.assert_not_called() - -            # Don't attempt to remove groups if the social account doesn't map to a Django user -            social_account_removed.send(SocialLogin, socialaccount=self.social_unmapped) -            mock_obj.assert_not_called() - -            # Attempt to remove groups if everything checks out -            social_account_removed.send(SocialLogin, socialaccount=self.social_user) -            mock_obj.assert_called_with(self.discord_user, self.social_user, deletion=True) - -    def test_logged_in(self): -        """Test the user-logged-in Allauth signal handling.""" -        mock_obj = mock.Mock() - -        with mock.patch.object(AllauthSignalListener, "_apply_groups", mock_obj): -            AllauthSignalListener() - -            # Don't attempt to apply groups if the user doesn't have a linked Discord account -            user_logged_in.send(DjangoUser, user=self.django_user_discordless) -            mock_obj.assert_not_called() - -            # Don't attempt to apply groups if the user hasn't joined the Discord server -            user_logged_in.send(DjangoUser, user=self.django_user_never_joined) -            mock_obj.assert_not_called() - -            # Attempt to apply groups if everything checks out -            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): -        """Test application of groups by role, relating to an admin user.""" -        handler = AllauthSignalListener() - -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        # Apply groups based on admin role being present on Discord -        handler._apply_groups(self.discord_admin, self.social_admin) -        self.assertTrue(self.admin_group in self.django_admin.groups.all()) - -        # Remove groups based on the user apparently leaving the server -        handler._apply_groups(self.discord_admin, self.social_admin, True) -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        # Apply the admin role again -        handler._apply_groups(self.discord_admin, self.social_admin) - -        # Remove all of the roles from the user -        self.discord_admin.roles.clear() - -        # Remove groups based on the user no longer having the admin role on Discord -        handler._apply_groups(self.discord_admin, self.social_admin) -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        self.discord_admin.roles.append(self.admin_role.id) -        self.discord_admin.save() - -    def test_apply_groups_moderator(self): -        """Test application of groups by role, relating to a non-`is_staff` moderator user.""" -        handler = AllauthSignalListener() - -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        # Apply groups based on moderator role being present on Discord -        handler._apply_groups(self.discord_moderator, self.social_moderator) -        self.assertTrue(self.moderator_group in self.django_moderator.groups.all()) - -        # Remove groups based on the user apparently leaving the server -        handler._apply_groups(self.discord_moderator, self.social_moderator, True) -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        # Apply the moderator role again -        handler._apply_groups(self.discord_moderator, self.social_moderator) - -        # Remove all of the roles from the user -        self.discord_moderator.roles.clear() - -        # Remove groups based on the user no longer having the moderator role on Discord -        handler._apply_groups(self.discord_moderator, self.social_moderator) -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        self.discord_moderator.roles.append(self.moderator_role.id) -        self.discord_moderator.save() - -    def test_apply_groups_other(self): -        """Test application of groups by role, relating to non-standard cases.""" -        handler = AllauthSignalListener() - -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        # No groups should be applied when there's no user account yet -        handler._apply_groups(self.discord_unmapped, self.social_unmapped) -        self.assertEqual(self.django_user_discordless.groups.all().count(), 0) - -        # No groups should be applied when there are only unmapped roles to match -        handler._apply_groups(self.discord_unmapped, self.social_user) -        self.assertEqual(self.django_user.groups.all().count(), 0) - -        # No groups should be applied when the user isn't in the guild -        handler._apply_groups(self.discord_not_in_guild, self.social_user) -        self.assertEqual(self.django_user.groups.all().count(), 0) - -    def test_role_mapping_str(self): -        """Test that role mappings stringify correctly.""" -        self.assertEqual( -            str(self.admin_mapping), -            f"@{self.admin_role.name} -> {self.admin_group.name}" -        ) - -    def test_role_mapping_changes(self): -        """Test that role mapping listeners work when changes are made.""" -        # Set up (just for this test) -        self.django_moderator.groups.add(self.moderator_group) -        self.django_admin.groups.add(self.admin_group) - -        self.assertEqual(self.django_moderator.groups.all().count(), 1) -        self.assertEqual(self.django_admin.groups.all().count(), 1) - -        # Test is_staff changes -        self.admin_mapping.is_staff = False -        self.admin_mapping.save() - -        self.assertFalse(self.django_moderator.is_staff) -        self.assertFalse(self.django_admin.is_staff) - -        self.admin_mapping.is_staff = True -        self.admin_mapping.save() - -        self.django_admin.refresh_from_db(fields=("is_staff", )) -        self.assertTrue(self.django_admin.is_staff) - -        # Test mapping deletion -        self.admin_mapping.delete() - -        self.django_admin.refresh_from_db(fields=("is_staff",)) -        self.assertEqual(self.django_admin.groups.all().count(), 0) -        self.assertFalse(self.django_admin.is_staff) - -        # Test mapping update -        self.moderator_mapping.group = self.admin_group -        self.moderator_mapping.save() - -        self.assertEqual(self.django_moderator.groups.all().count(), 1) -        self.assertTrue(self.admin_group in self.django_moderator.groups.all()) - -        # Test mapping creation -        new_mapping = RoleMapping.objects.create( -            role=self.admin_role, -            group=self.moderator_group, -            is_staff=True -        ) - -        self.assertEqual(self.django_admin.groups.all().count(), 1) -        self.assertTrue(self.moderator_group in self.django_admin.groups.all()) - -        self.django_admin.refresh_from_db(fields=("is_staff",)) -        self.assertTrue(self.django_admin.is_staff) - -        new_mapping.delete() - -        # Test mapping creation (without is_staff) -        new_mapping = RoleMapping.objects.create( -            role=self.admin_role, -            group=self.moderator_group, -        ) - -        self.assertEqual(self.django_admin.groups.all().count(), 1) -        self.assertTrue(self.moderator_group in self.django_admin.groups.all()) - -        self.django_admin.refresh_from_db(fields=("is_staff",)) -        self.assertFalse(self.django_admin.is_staff) - -        # Test that nothing happens when fixtures are loaded -        pre_save.send(RoleMapping, instance=new_mapping, raw=True) - -        self.assertEqual(self.django_admin.groups.all().count(), 1) -        self.assertTrue(self.moderator_group in self.django_admin.groups.all()) diff --git a/pydis_site/apps/home/tests/test_views.py b/pydis_site/apps/home/tests/test_views.py index 572317a7..bd1671b1 100644 --- a/pydis_site/apps/home/tests/test_views.py +++ b/pydis_site/apps/home/tests/test_views.py @@ -1,198 +1,5 @@ -from allauth.socialaccount.models import SocialAccount -from django.contrib.auth.models import User -from django.http import HttpResponseRedirect  from django.test import TestCase -from django_hosts.resolvers import get_host, reverse, reverse_host - - -def check_redirect_url( -        response: HttpResponseRedirect, reversed_url: str, strip_params=True -) -> bool: -    """ -    Check whether a given redirect response matches a specific reversed URL. - -    Arguments: -    * `response`: The HttpResponseRedirect returned by the test client -    * `reversed_url`: The URL returned by `reverse()` -    * `strip_params`: Whether to strip URL parameters (following a "?") from the URL given in the -                     `response` object -    """ -    host = get_host(None) -    hostname = reverse_host(host) - -    redirect_url = response.url - -    if strip_params and "?" in redirect_url: -        redirect_url = redirect_url.split("?", 1)[0] - -    result = reversed_url == f"//{hostname}{redirect_url}" -    return result - - -class TestAccountDeleteView(TestCase): -    def setUp(self) -> None: -        """Create an authorized Django user for testing purposes.""" -        self.user = User.objects.create( -            username="user#0000" -        ) - -    def test_redirect_when_logged_out(self): -        """Test that the user is redirected to the homepage when not logged in.""" -        url = reverse("account_delete") -        resp = self.client.get(url) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) - -    def test_get_when_logged_in(self): -        """Test that the view returns a HTTP 200 when the user is logged in.""" -        url = reverse("account_delete") - -        self.client.force_login(self.user) -        resp = self.client.get(url) -        self.client.logout() - -        self.assertEqual(resp.status_code, 200) - -    def test_post_invalid(self): -        """Test that the user is redirected when the form is filled out incorrectly.""" -        url = reverse("account_delete") - -        self.client.force_login(self.user) - -        resp = self.client.post(url, {}) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, url)) - -        resp = self.client.post(url, {"username": "user"}) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, url)) - -        self.client.logout() - -    def test_post_valid(self): -        """Test that the account is deleted when the form is filled out correctly..""" -        url = reverse("account_delete") - -        self.client.force_login(self.user) - -        resp = self.client.post(url, {"username": "user#0000"}) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) - -        with self.assertRaises(User.DoesNotExist): -            User.objects.get(username=self.user.username) - -        self.client.logout() - - -class TestAccountSettingsView(TestCase): -    def setUp(self) -> None: -        """Create an authorized Django user for testing purposes.""" -        self.user = User.objects.create( -            username="user#0000" -        ) - -        self.user_unlinked = User.objects.create( -            username="user#9999" -        ) - -        self.user_unlinked_discord = User.objects.create( -            username="user#1234" -        ) - -        self.user_unlinked_github = User.objects.create( -            username="user#1111" -        ) - -        self.github_account = SocialAccount.objects.create( -            user=self.user, -            provider="github", -            uid="0" -        ) - -        self.discord_account = SocialAccount.objects.create( -            user=self.user, -            provider="discord", -            uid="0000" -        ) - -        self.github_account_secondary = SocialAccount.objects.create( -            user=self.user_unlinked_discord, -            provider="github", -            uid="1" -        ) - -        self.discord_account_secondary = SocialAccount.objects.create( -            user=self.user_unlinked_github, -            provider="discord", -            uid="1111" -        ) - -    def test_redirect_when_logged_out(self): -        """Check that the user is redirected to the homepage when not logged in.""" -        url = reverse("account_settings") -        resp = self.client.get(url) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) - -    def test_get_when_logged_in(self): -        """Test that the view returns a HTTP 200 when the user is logged in.""" -        url = reverse("account_settings") - -        self.client.force_login(self.user) -        resp = self.client.get(url) -        self.client.logout() - -        self.assertEqual(resp.status_code, 200) - -        self.client.force_login(self.user_unlinked) -        resp = self.client.get(url) -        self.client.logout() - -        self.assertEqual(resp.status_code, 200) - -        self.client.force_login(self.user_unlinked_discord) -        resp = self.client.get(url) -        self.client.logout() - -        self.assertEqual(resp.status_code, 200) - -        self.client.force_login(self.user_unlinked_github) -        resp = self.client.get(url) -        self.client.logout() - -        self.assertEqual(resp.status_code, 200) - -    def test_post_invalid(self): -        """Test the behaviour of invalid POST submissions.""" -        url = reverse("account_settings") - -        self.client.force_login(self.user_unlinked) - -        resp = self.client.post(url, {"provider": "discord"}) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) - -        resp = self.client.post(url, {"provider": "github"}) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) - -        self.client.logout() - -    def test_post_valid(self): -        """Ensure that GitHub is unlinked with a valid POST submission.""" -        url = reverse("account_settings") - -        self.client.force_login(self.user) - -        resp = self.client.post(url, {"provider": "github"}) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) - -        with self.assertRaises(SocialAccount.DoesNotExist): -            SocialAccount.objects.get(user=self.user, provider="github") - -        self.client.logout() +from django_hosts.resolvers import reverse  class TestIndexReturns200(TestCase): @@ -201,21 +8,3 @@ class TestIndexReturns200(TestCase):          url = reverse('home')          resp = self.client.get(url)          self.assertEqual(resp.status_code, 200) - - -class TestLoginCancelledReturns302(TestCase): -    def test_login_cancelled_returns_302(self): -        """Check that the login cancelled redirect returns a HTTP 302 response.""" -        url = reverse('socialaccount_login_cancelled') -        resp = self.client.get(url) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) - - -class TestLoginErrorReturns302(TestCase): -    def test_login_error_returns_302(self): -        """Check that the login error redirect returns a HTTP 302 response.""" -        url = reverse('socialaccount_login_error') -        resp = self.client.get(url) -        self.assertEqual(resp.status_code, 302) -        self.assertTrue(check_redirect_url(resp, reverse("home"))) | 
