aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps/api
diff options
context:
space:
mode:
authorGravatar jchristgit <[email protected]>2025-05-01 19:20:52 +0200
committerGravatar GitHub <[email protected]>2025-05-01 19:20:52 +0200
commit7a6effee36a7a9e71eb506f353bbf8c80be2e335 (patch)
treef68ac0eff920f6ed72af8c46211d56daf97f65fa /pydis_site/apps/api
parentMerge pull request #1515 from python-discord/resource-missing-semester (diff)
parentMark unique constraint error checks as no cover (diff)
Merge pull request #1499 from python-discord/dependabot/pip/djangorestframework-3.16.0
Bump djangorestframework from 3.14.0 to 3.16.0
Diffstat (limited to 'pydis_site/apps/api')
-rw-r--r--pydis_site/apps/api/tests/test_bumped_threads.py5
-rw-r--r--pydis_site/apps/api/tests/test_filters.py9
-rw-r--r--pydis_site/apps/api/tests/test_infractions.py6
-rw-r--r--pydis_site/apps/api/tests/test_nominations.py4
-rw-r--r--pydis_site/apps/api/tests/test_roles.py5
-rw-r--r--pydis_site/apps/api/tests/test_users.py2
-rw-r--r--pydis_site/apps/api/urls.py6
-rw-r--r--pydis_site/apps/api/viewsets/bot/infraction.py7
-rw-r--r--pydis_site/apps/api/viewsets/bot/user.py9
9 files changed, 40 insertions, 13 deletions
diff --git a/pydis_site/apps/api/tests/test_bumped_threads.py b/pydis_site/apps/api/tests/test_bumped_threads.py
index 2e3892c7..fbd21e92 100644
--- a/pydis_site/apps/api/tests/test_bumped_threads.py
+++ b/pydis_site/apps/api/tests/test_bumped_threads.py
@@ -60,4 +60,7 @@ class BumpedThreadAPITests(AuthenticatedAPITestCase):
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
- self.assertEqual(response.json(), {"detail": "Not found."})
+ self.assertEqual(
+ response.json(),
+ {"detail": "No BumpedThread matches the given query."},
+ )
diff --git a/pydis_site/apps/api/tests/test_filters.py b/pydis_site/apps/api/tests/test_filters.py
index 96b3a65c..831c4649 100644
--- a/pydis_site/apps/api/tests/test_filters.py
+++ b/pydis_site/apps/api/tests/test_filters.py
@@ -210,8 +210,15 @@ class GenericFilterTests(AuthenticatedAPITestCase):
sequence.model.objects.all().delete()
response = self.client.get(f"{sequence.url()}/42")
+ if "filter-lists" in sequence.url():
+ kind = "FilterList"
+ else:
+ kind = "Filter"
self.assertEqual(response.status_code, 404)
- self.assertDictEqual(response.json(), {'detail': 'Not found.'})
+ self.assertDictEqual(
+ response.json(),
+ {'detail': f"No {kind} matches the given query."},
+ )
def test_creation(self) -> None:
for name, sequence in get_test_sequences().items():
diff --git a/pydis_site/apps/api/tests/test_infractions.py b/pydis_site/apps/api/tests/test_infractions.py
index 82722285..4830833c 100644
--- a/pydis_site/apps/api/tests/test_infractions.py
+++ b/pydis_site/apps/api/tests/test_infractions.py
@@ -329,7 +329,7 @@ class InfractionTests(AuthenticatedAPITestCase):
def test_partial_update_returns_400_for_frozen_field(self):
url = reverse('api:bot:infraction-detail', args=(self.ban_hidden.id,))
- data = {'user': 6}
+ data = {'user': 6, 'active': True}
response = self.client.patch(url, data=data)
self.assertEqual(response.status_code, 400)
@@ -559,7 +559,7 @@ class CreationTests(AuthenticatedAPITestCase):
second_response.json(),
{
'non_field_errors': [
- 'This user already has an active infraction of this type.'
+ 'The fields user, type must make a unique set.'
]
}
)
@@ -824,7 +824,7 @@ class SerializerTests(AuthenticatedAPITestCase):
self.create_infraction('ban', active=True)
instance = self.create_infraction('ban', active=False)
- data = {'reason': 'hello'}
+ data = {'reason': 'hello', 'active': True}
serializer = InfractionSerializer(instance, data=data, partial=True)
self.assertTrue(serializer.is_valid(), msg=serializer.errors)
diff --git a/pydis_site/apps/api/tests/test_nominations.py b/pydis_site/apps/api/tests/test_nominations.py
index e4dfe36a..7c6f1bbb 100644
--- a/pydis_site/apps/api/tests/test_nominations.py
+++ b/pydis_site/apps/api/tests/test_nominations.py
@@ -379,7 +379,7 @@ class NominationTests(AuthenticatedAPITestCase):
response = self.client.get(url, data={})
self.assertEqual(response.status_code, 404)
self.assertEqual(response.json(), {
- "detail": "Not found."
+ "detail": "No Nomination matches the given query."
})
def test_returns_404_on_patch_unknown_nomination(self):
@@ -391,7 +391,7 @@ class NominationTests(AuthenticatedAPITestCase):
response = self.client.patch(url, data={})
self.assertEqual(response.status_code, 404)
self.assertEqual(response.json(), {
- "detail": "Not found."
+ "detail": "No Nomination matches the given query."
})
def test_returns_405_on_list_put(self):
diff --git a/pydis_site/apps/api/tests/test_roles.py b/pydis_site/apps/api/tests/test_roles.py
index d3031990..da879b00 100644
--- a/pydis_site/apps/api/tests/test_roles.py
+++ b/pydis_site/apps/api/tests/test_roles.py
@@ -208,4 +208,7 @@ class CreationTests(AuthenticatedAPITestCase):
for method in ('get', 'put', 'patch', 'delete'):
response = getattr(self.client, method)(url)
self.assertEqual(response.status_code, 404)
- self.assertJSONEqual(response.content, '{"detail": "Not found."}')
+ self.assertJSONEqual(
+ response.content,
+ '{"detail": "No Role matches the given query."}',
+ )
diff --git a/pydis_site/apps/api/tests/test_users.py b/pydis_site/apps/api/tests/test_users.py
index 3799b631..99673e53 100644
--- a/pydis_site/apps/api/tests/test_users.py
+++ b/pydis_site/apps/api/tests/test_users.py
@@ -746,7 +746,7 @@ class UserAltUpdateTests(AuthenticatedAPITestCase):
response = self.client.post(repeated_url, repeated_data)
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json(), {
- 'source': ["This relationship has already been established"]
+ 'non_field_errors': ["The fields source, target must make a unique set."]
})
def test_removing_existing_alt_source_from_target(self) -> None:
diff --git a/pydis_site/apps/api/urls.py b/pydis_site/apps/api/urls.py
index 5cda033a..ae3172c7 100644
--- a/pydis_site/apps/api/urls.py
+++ b/pydis_site/apps/api/urls.py
@@ -28,9 +28,11 @@ from .viewsets import (
# https://www.django-rest-framework.org/api-guide/routers/#defaultrouter
bot_router = DefaultRouter(trailing_slash=False)
+# XXX: We should probably figure out why we have this registered twice.
bot_router.register(
'filter/filter_lists',
- FilterListViewSet
+ FilterListViewSet,
+ basename="filter-filterlists-list",
)
bot_router.register(
"aoc-account-links",
@@ -62,7 +64,7 @@ bot_router.register(
)
bot_router.register(
'filter-lists',
- FilterListViewSet
+ FilterListViewSet,
)
bot_router.register(
'infractions',
diff --git a/pydis_site/apps/api/viewsets/bot/infraction.py b/pydis_site/apps/api/viewsets/bot/infraction.py
index 8da82822..19b6f2d8 100644
--- a/pydis_site/apps/api/viewsets/bot/infraction.py
+++ b/pydis_site/apps/api/viewsets/bot/infraction.py
@@ -284,7 +284,12 @@ class InfractionViewSet(
"""
try:
return super().create(request, *args, **kwargs)
- except IntegrityError as err:
+ except IntegrityError as err: # pragma: no cover - see below
+ # Not covered: DRF handles this via `UniqueTogetherValidator` these
+ # days, which means it's hard to test this branch specifically.
+ # However, in a productive deployment, it's still very much
+ # possible for two concurrent inserts to run into IntegrityError.
+
# We need to use `__cause__` here, as Django reraises the internal
# UniqueViolation emitted by psycopg2 (which contains the attribute
# that we actually need)
diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py
index c0b4ca0f..79e867c3 100644
--- a/pydis_site/apps/api/viewsets/bot/user.py
+++ b/pydis_site/apps/api/viewsets/bot/user.py
@@ -395,7 +395,14 @@ class UserViewSet(ModelViewSet):
raise ParseError(detail={
"source": ["The user may not be an alternate account of itself"]
})
- if err.__cause__.diag.constraint_name == 'api_useraltrelationship_unique_relationships':
+ if (
+ err.__cause__.diag.constraint_name == 'api_useraltrelationship_unique_relationships'
+ ): # pragma: no cover - see below
+ # This is not covered because newer DRF versions automatically validate this,
+ # however the validation is done via a SELECT query which may race concurrent
+ # inserts in prod. The only correct way is e.g. what Ecto does which is
+ # associating the validators to the unique constraint errors to match in
+ # errors, anything else may race.
raise ParseError(detail={
"source": ["This relationship has already been established"]
})