diff options
author | 2021-05-14 13:58:56 +0800 | |
---|---|---|
committer | 2021-05-14 14:23:29 +0800 | |
commit | 274efc3ec73e2bcfee9cd93b26f737ee68fd4638 (patch) | |
tree | c74a0fb5cb80b605d21843b8bd424a192198dd8a /pydis_site/apps/api/tests | |
parent | Merge pull request #485 from python-discord/ks129/dewikification/redirection (diff) |
Merge branch main into dewikification
Diffstat (limited to 'pydis_site/apps/api/tests')
-rw-r--r-- | pydis_site/apps/api/tests/test_dblogger.py | 27 | ||||
-rw-r--r-- | pydis_site/apps/api/tests/test_documentation_links.py | 15 | ||||
-rw-r--r-- | pydis_site/apps/api/tests/test_infractions.py | 30 | ||||
-rw-r--r-- | pydis_site/apps/api/tests/test_models.py | 16 | ||||
-rw-r--r-- | pydis_site/apps/api/tests/test_nominations.py | 118 | ||||
-rw-r--r-- | pydis_site/apps/api/tests/test_users.py | 337 |
6 files changed, 492 insertions, 51 deletions
diff --git a/pydis_site/apps/api/tests/test_dblogger.py b/pydis_site/apps/api/tests/test_dblogger.py deleted file mode 100644 index bb19f297..00000000 --- a/pydis_site/apps/api/tests/test_dblogger.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging -from datetime import datetime - -from django.test import TestCase - -from ..dblogger import DatabaseLogHandler -from ..models import LogEntry - - -class DatabaseLogHandlerTests(TestCase): - def test_logs_to_database(self): - module_basename = __name__.split('.')[-1] - logger = logging.getLogger(__name__) - logger.handlers = [DatabaseLogHandler()] - logger.warning("I am a test case!") - - # Ensure we only have a single record in the database - # after the logging call above. - [entry] = LogEntry.objects.all() - - self.assertEqual(entry.application, 'site') - self.assertEqual(entry.logger_name, __name__) - self.assertIsInstance(entry.timestamp, datetime) - self.assertEqual(entry.level, 'warning') - self.assertEqual(entry.module, module_basename) - self.assertIsInstance(entry.line, int) - self.assertEqual(entry.message, "I am a test case!") diff --git a/pydis_site/apps/api/tests/test_documentation_links.py b/pydis_site/apps/api/tests/test_documentation_links.py index e560a2fd..39fb08f3 100644 --- a/pydis_site/apps/api/tests/test_documentation_links.py +++ b/pydis_site/apps/api/tests/test_documentation_links.py @@ -60,7 +60,7 @@ class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase): def setUpTestData(cls): cls.doc_link = DocumentationLink.objects.create( package='testpackage', - base_url='https://example.com', + base_url='https://example.com/', inventory_url='https://example.com' ) @@ -108,6 +108,17 @@ class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase): self.assertEqual(response.status_code, 400) + def test_create_invalid_package_name_returns_400(self): + test_cases = ("InvalidPackage", "invalid package", "i\u0150valid") + for case in test_cases: + with self.subTest(package_name=case): + body = self.doc_json.copy() + body['package'] = case + url = reverse('bot:documentationlink-list', host='api') + response = self.client.post(url, data=body) + + self.assertEqual(response.status_code, 400) + class DocumentationLinkCreationTests(APISubdomainTestCase): def setUp(self): @@ -115,7 +126,7 @@ class DocumentationLinkCreationTests(APISubdomainTestCase): self.body = { 'package': 'example', - 'base_url': 'https://example.com', + 'base_url': 'https://example.com/', 'inventory_url': 'https://docs.example.com' } diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py index 93ef8171..82b497aa 100644 --- a/pydis_site/apps/api/tests/test_infractions.py +++ b/pydis_site/apps/api/tests/test_infractions.py @@ -512,6 +512,36 @@ class CreationTests(APISubdomainTestCase): ) +class InfractionDeletionTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): + cls.user = User.objects.create( + id=9876, + name='Unknown user', + discriminator=9876, + ) + + cls.warning = Infraction.objects.create( + user_id=cls.user.id, + actor_id=cls.user.id, + type='warning', + active=False + ) + + def test_delete_unknown_infraction_returns_404(self): + url = reverse('bot:infraction-detail', args=('something',), host='api') + response = self.client.delete(url) + + self.assertEqual(response.status_code, 404) + + def test_delete_known_infraction_returns_204(self): + url = reverse('bot:infraction-detail', args=(self.warning.id,), host='api') + response = self.client.delete(url) + + self.assertEqual(response.status_code, 204) + self.assertRaises(Infraction.DoesNotExist, Infraction.objects.get, id=self.warning.id) + + class ExpandedTests(APISubdomainTestCase): @classmethod def setUpTestData(cls): diff --git a/pydis_site/apps/api/tests/test_models.py b/pydis_site/apps/api/tests/test_models.py index 853e6621..66052e01 100644 --- a/pydis_site/apps/api/tests/test_models.py +++ b/pydis_site/apps/api/tests/test_models.py @@ -10,6 +10,7 @@ from pydis_site.apps.api.models import ( Message, MessageDeletionContext, Nomination, + NominationEntry, OffTopicChannelName, OffensiveMessage, Reminder, @@ -37,17 +38,11 @@ class StringDunderMethodTests(SimpleTestCase): def setUp(self): self.nomination = Nomination( id=123, - actor=User( - id=9876, - name='Mr. Hemlock', - discriminator=6666, - ), user=User( id=9876, name="Hemlock's Cat", discriminator=7777, ), - reason="He purrrrs like the best!", ) self.objects = ( @@ -135,6 +130,15 @@ class StringDunderMethodTests(SimpleTestCase): ), content="oh no", expiration=dt(5018, 11, 20, 15, 52, tzinfo=timezone.utc) + ), + NominationEntry( + nomination_id=self.nomination.id, + actor=User( + id=9876, + name='Mr. Hemlock', + discriminator=6666, + ), + reason="He purrrrs like the best!", ) ) diff --git a/pydis_site/apps/api/tests/test_nominations.py b/pydis_site/apps/api/tests/test_nominations.py index b37135f8..9cefbd8f 100644 --- a/pydis_site/apps/api/tests/test_nominations.py +++ b/pydis_site/apps/api/tests/test_nominations.py @@ -3,7 +3,7 @@ from datetime import datetime as dt, timedelta, timezone from django_hosts.resolvers import reverse from .base import APISubdomainTestCase -from ..models import Nomination, User +from ..models import Nomination, NominationEntry, User class CreationTests(APISubdomainTestCase): @@ -14,6 +14,11 @@ class CreationTests(APISubdomainTestCase): name='joe dart', discriminator=1111, ) + cls.user2 = User.objects.create( + id=9876, + name='Who?', + discriminator=1234 + ) def test_accepts_valid_data(self): url = reverse('bot:nomination-list', host='api') @@ -27,17 +32,39 @@ class CreationTests(APISubdomainTestCase): self.assertEqual(response.status_code, 201) nomination = Nomination.objects.get(id=response.json()['id']) + nomination_entry = NominationEntry.objects.get( + nomination_id=nomination.id, + actor_id=self.user.id + ) self.assertAlmostEqual( nomination.inserted_at, dt.now(timezone.utc), delta=timedelta(seconds=2) ) self.assertEqual(nomination.user.id, data['user']) - self.assertEqual(nomination.actor.id, data['actor']) - self.assertEqual(nomination.reason, data['reason']) + self.assertEqual(nomination_entry.reason, data['reason']) self.assertEqual(nomination.active, True) - def test_returns_400_on_second_active_nomination(self): + def test_returns_200_on_second_active_nomination_by_different_user(self): + url = reverse('bot:nomination-list', host='api') + first_data = { + 'actor': self.user.id, + 'reason': 'Joe Dart on Fender Bass', + 'user': self.user.id, + } + second_data = { + 'actor': self.user2.id, + 'reason': 'Great user', + 'user': self.user.id + } + + response1 = self.client.post(url, data=first_data) + self.assertEqual(response1.status_code, 201) + + response2 = self.client.post(url, data=second_data) + self.assertEqual(response2.status_code, 201) + + def test_returns_400_on_second_active_nomination_by_existing_nominator(self): url = reverse('bot:nomination-list', host='api') data = { 'actor': self.user.id, @@ -51,7 +78,7 @@ class CreationTests(APISubdomainTestCase): response2 = self.client.post(url, data=data) self.assertEqual(response2.status_code, 400) self.assertEqual(response2.json(), { - 'active': ['There can only be one active nomination.'] + 'actor': ['This actor has already endorsed this nomination.'] }) def test_returns_400_for_missing_user(self): @@ -189,30 +216,40 @@ class NominationTests(APISubdomainTestCase): ) cls.active_nomination = Nomination.objects.create( - user=cls.user, + user=cls.user + ) + cls.active_nomination_entry = NominationEntry.objects.create( + nomination=cls.active_nomination, actor=cls.user, reason="He's pretty funky" ) cls.inactive_nomination = Nomination.objects.create( user=cls.user, - actor=cls.user, - reason="He's pretty funky", active=False, end_reason="His neck couldn't hold the funk", ended_at="5018-11-20T15:52:00+00:00" ) + cls.inactive_nomination_entry = NominationEntry.objects.create( + nomination=cls.inactive_nomination, + actor=cls.user, + reason="He's pretty funky" + ) - def test_returns_200_update_reason_on_active(self): + def test_returns_200_update_reason_on_active_with_actor(self): url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api') data = { - 'reason': "He's one funky duck" + 'reason': "He's one funky duck", + 'actor': self.user.id } response = self.client.patch(url, data=data) self.assertEqual(response.status_code, 200) - nomination = Nomination.objects.get(id=response.json()['id']) - self.assertEqual(nomination.reason, data['reason']) + nomination_entry = NominationEntry.objects.get( + nomination_id=response.json()['id'], + actor_id=self.user.id + ) + self.assertEqual(nomination_entry.reason, data['reason']) def test_returns_400_on_frozen_field_update(self): url = reverse('bot:nomination-detail', args=(self.active_nomination.id,), host='api') @@ -241,14 +278,18 @@ class NominationTests(APISubdomainTestCase): def test_returns_200_update_reason_on_inactive(self): url = reverse('bot:nomination-detail', args=(self.inactive_nomination.id,), host='api') data = { - 'reason': "He's one funky duck" + 'reason': "He's one funky duck", + 'actor': self.user.id } response = self.client.patch(url, data=data) self.assertEqual(response.status_code, 200) - nomination = Nomination.objects.get(id=response.json()['id']) - self.assertEqual(nomination.reason, data['reason']) + nomination_entry = NominationEntry.objects.get( + nomination_id=response.json()['id'], + actor_id=self.user.id + ) + self.assertEqual(nomination_entry.reason, data['reason']) def test_returns_200_update_end_reason_on_inactive(self): url = reverse('bot:nomination-detail', args=(self.inactive_nomination.id,), host='api') @@ -442,3 +483,50 @@ class NominationTests(APISubdomainTestCase): infractions = response.json() self.assertEqual(len(infractions), 2) + + def test_patch_nomination_set_reviewed_of_active_nomination(self): + url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api') + data = {'reviewed': True} + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 200) + + def test_patch_nomination_set_reviewed_of_inactive_nomination(self): + url = reverse('api:nomination-detail', args=(self.inactive_nomination.id,), host='api') + data = {'reviewed': True} + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), { + 'reviewed': ['This field cannot be set if the nomination is inactive.'] + }) + + def test_patch_nomination_set_reviewed_and_end(self): + url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api') + data = {'reviewed': True, 'active': False, 'end_reason': "What?"} + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), { + 'reviewed': ['This field cannot be set while you are ending a nomination.'] + }) + + def test_modifying_reason_without_actor(self): + url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api') + data = {'reason': 'That is my reason!'} + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), { + 'actor': ['This field is required when editing the reason.'] + }) + + def test_modifying_reason_with_unknown_actor(self): + url = reverse('api:nomination-detail', args=(self.active_nomination.id,), host='api') + data = {'reason': 'That is my reason!', 'actor': 90909090909090} + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.json(), { + 'actor': ["The actor doesn't exist or has not nominated the user."] + }) diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py index a02fce8a..c43b916a 100644 --- a/pydis_site/apps/api/tests/test_users.py +++ b/pydis_site/apps/api/tests/test_users.py @@ -1,7 +1,11 @@ +from unittest.mock import patch + +from django.core.exceptions import ObjectDoesNotExist from django_hosts.resolvers import reverse from .base import APISubdomainTestCase from ..models import Role, User +from ..models.bot.metricity import NotFound class UnauthedUserAPITests(APISubdomainTestCase): @@ -45,6 +49,13 @@ class CreationTests(APISubdomainTestCase): position=1 ) + cls.user = User.objects.create( + id=11, + name="Name doesn't matter.", + discriminator=1122, + in_guild=True + ) + def test_accepts_valid_data(self): url = reverse('bot:user-list', host='api') data = { @@ -89,7 +100,7 @@ class CreationTests(APISubdomainTestCase): response = self.client.post(url, data=data) self.assertEqual(response.status_code, 201) - self.assertEqual(response.json(), data) + self.assertEqual(response.json(), []) def test_returns_400_for_unknown_role_id(self): url = reverse('bot:user-list', host='api') @@ -115,6 +126,176 @@ class CreationTests(APISubdomainTestCase): response = self.client.post(url, data=data) self.assertEqual(response.status_code, 400) + def test_returns_400_for_user_recreation(self): + """Return 201 if User is already present in database as it skips User creation.""" + url = reverse('bot:user-list', host='api') + data = [{ + 'id': 11, + 'name': 'You saw nothing.', + 'discriminator': 112, + 'in_guild': True + }] + response = self.client.post(url, data=data) + self.assertEqual(response.status_code, 201) + + def test_returns_400_for_duplicate_request_users(self): + """Return 400 if 2 Users with same ID is passed in the request data.""" + url = reverse('bot:user-list', host='api') + data = [ + { + 'id': 11, + 'name': 'You saw nothing.', + 'discriminator': 112, + 'in_guild': True + }, + { + 'id': 11, + 'name': 'You saw nothing part 2.', + 'discriminator': 1122, + 'in_guild': False + } + ] + response = self.client.post(url, data=data) + self.assertEqual(response.status_code, 400) + + def test_returns_400_for_existing_user(self): + """Returns 400 if user is already present in DB.""" + url = reverse('bot:user-list', host='api') + data = { + 'id': 11, + 'name': 'You saw nothing part 3.', + 'discriminator': 1122, + 'in_guild': True + } + response = self.client.post(url, data=data) + self.assertEqual(response.status_code, 400) + + +class MultiPatchTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): + cls.role_developer = Role.objects.create( + id=159, + name="Developer", + colour=2, + permissions=0b01010010101, + position=10, + ) + cls.user_1 = User.objects.create( + id=1, + name="Patch test user 1.", + discriminator=1111, + in_guild=True + ) + cls.user_2 = User.objects.create( + id=2, + name="Patch test user 2.", + discriminator=2222, + in_guild=True + ) + + def test_multiple_users_patch(self): + url = reverse("bot:user-bulk-patch", host="api") + data = [ + { + "id": 1, + "name": "User 1 patched!", + "discriminator": 1010, + "roles": [self.role_developer.id], + "in_guild": False + }, + { + "id": 2, + "name": "User 2 patched!" + } + ] + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()[0], data[0]) + + user_2 = User.objects.get(id=2) + self.assertEqual(user_2.name, data[1]["name"]) + + def test_returns_400_for_missing_user_id(self): + url = reverse("bot:user-bulk-patch", host="api") + data = [ + { + "name": "I am ghost user!", + "discriminator": 1010, + "roles": [self.role_developer.id], + "in_guild": False + }, + { + "name": "patch me? whats my id?" + } + ] + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + + def test_returns_404_for_not_found_user(self): + url = reverse("bot:user-bulk-patch", host="api") + data = [ + { + "id": 1, + "name": "User 1 patched again!!!", + "discriminator": 1010, + "roles": [self.role_developer.id], + "in_guild": False + }, + { + "id": 22503405, + "name": "User unknown not patched!" + } + ] + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 404) + + def test_returns_400_for_bad_data(self): + url = reverse("bot:user-bulk-patch", host="api") + data = [ + { + "id": 1, + "in_guild": "Catch me!" + }, + { + "id": 2, + "discriminator": "find me!" + } + ] + + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + + def test_returns_400_for_insufficient_data(self): + url = reverse("bot:user-bulk-patch", host="api") + data = [ + { + "id": 1, + }, + { + "id": 2, + } + ] + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + + def test_returns_400_for_duplicate_request_users(self): + """Return 400 if 2 Users with same ID is passed in the request data.""" + url = reverse("bot:user-bulk-patch", host="api") + data = [ + { + 'id': 1, + 'name': 'You saw nothing.', + }, + { + 'id': 1, + 'name': 'You saw nothing part 2.', + } + ] + response = self.client.patch(url, data=data) + self.assertEqual(response.status_code, 400) + class UserModelTests(APISubdomainTestCase): @classmethod @@ -170,3 +351,157 @@ class UserModelTests(APISubdomainTestCase): def test_correct_username_formatting(self): """Tests the username property with both name and discriminator formatted together.""" self.assertEqual(self.user_with_roles.username, "Test User with two roles#0001") + + +class UserPaginatorTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): + users = [] + for i in range(1, 10_001): + users.append(User( + id=i, + name=f"user{i}", + discriminator=1111, + in_guild=True + )) + cls.users = User.objects.bulk_create(users) + + def test_returns_single_page_response(self): + url = reverse("bot:user-list", host="api") + response = self.client.get(url).json() + self.assertIsNone(response["next_page_no"]) + self.assertIsNone(response["previous_page_no"]) + + def test_returns_next_page_number(self): + User.objects.create( + id=10_001, + name="user10001", + discriminator=1111, + in_guild=True + ) + url = reverse("bot:user-list", host="api") + response = self.client.get(url).json() + self.assertEqual(2, response["next_page_no"]) + + def test_returns_previous_page_number(self): + User.objects.create( + id=10_001, + name="user10001", + discriminator=1111, + in_guild=True + ) + url = reverse("bot:user-list", host="api") + response = self.client.get(url, {"page": 2}).json() + self.assertEqual(1, response["previous_page_no"]) + + +class UserMetricityTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): + User.objects.create( + id=0, + name="Test user", + discriminator=1, + in_guild=True, + ) + + def test_get_metricity_data(self): + # Given + joined_at = "foo" + total_messages = 1 + total_blocks = 1 + self.mock_metricity_user(joined_at, total_messages, total_blocks, []) + + # When + url = reverse('bot:user-metricity-data', args=[0], host='api') + response = self.client.get(url) + + # Then + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), { + "joined_at": joined_at, + "total_messages": total_messages, + "voice_banned": False, + "activity_blocks": total_blocks + }) + + def test_no_metricity_user(self): + # Given + self.mock_no_metricity_user() + + # When + url = reverse('bot:user-metricity-data', args=[0], host='api') + response = self.client.get(url) + + # Then + self.assertEqual(response.status_code, 404) + + def test_no_metricity_user_for_review(self): + # Given + self.mock_no_metricity_user() + + # When + url = reverse('bot:user-metricity-review-data', args=[0], host='api') + response = self.client.get(url) + + # Then + self.assertEqual(response.status_code, 404) + + def test_metricity_voice_banned(self): + cases = [ + {'exception': None, 'voice_banned': True}, + {'exception': ObjectDoesNotExist, 'voice_banned': False}, + ] + + self.mock_metricity_user("foo", 1, 1, [["bar", 1]]) + + for case in cases: + with self.subTest(exception=case['exception'], voice_banned=case['voice_banned']): + with patch("pydis_site.apps.api.viewsets.bot.user.Infraction.objects.get") as p: + p.side_effect = case['exception'] + + url = reverse('bot:user-metricity-data', args=[0], host='api') + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json()["voice_banned"], case["voice_banned"]) + + def test_metricity_review_data(self): + # Given + joined_at = "foo" + total_messages = 10 + total_blocks = 1 + channel_activity = [["bar", 4], ["buzz", 6]] + self.mock_metricity_user(joined_at, total_messages, total_blocks, channel_activity) + + # When + url = reverse('bot:user-metricity-review-data', args=[0], host='api') + response = self.client.get(url) + + # Then + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), { + "joined_at": joined_at, + "top_channel_activity": channel_activity, + "total_messages": total_messages + }) + + def mock_metricity_user(self, joined_at, total_messages, total_blocks, top_channel_activity): + patcher = patch("pydis_site.apps.api.viewsets.bot.user.Metricity") + self.metricity = patcher.start() + self.addCleanup(patcher.stop) + self.metricity = self.metricity.return_value.__enter__.return_value + self.metricity.user.return_value = dict(joined_at=joined_at) + self.metricity.total_messages.return_value = total_messages + self.metricity.total_message_blocks.return_value = total_blocks + self.metricity.top_channel_activity.return_value = top_channel_activity + + def mock_no_metricity_user(self): + patcher = patch("pydis_site.apps.api.viewsets.bot.user.Metricity") + self.metricity = patcher.start() + self.addCleanup(patcher.stop) + self.metricity = self.metricity.return_value.__enter__.return_value + self.metricity.user.side_effect = NotFound() + self.metricity.total_messages.side_effect = NotFound() + self.metricity.total_message_blocks.side_effect = NotFound() + self.metricity.top_channel_activity.side_effect = NotFound() |