Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions contentcuration/contentcuration/tests/viewsets/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from contentcuration.models import Change
from contentcuration.tests import testdata
from contentcuration.tests.base import StudioAPITestCase
from contentcuration.tests.helpers import reverse_with_query
from contentcuration.tests.viewsets.base import generate_create_event
from contentcuration.tests.viewsets.base import generate_delete_event
from contentcuration.tests.viewsets.base import SyncTestMixin
Expand Down Expand Up @@ -367,6 +368,28 @@ def test_fetch_users_no_permissions(self):
self.assertEqual(response.status_code, 200, response.content)
self.assertEqual(response.json(), [])

def test_remove_self_with_invalid_channel_id_returns_bad_request(self):
self.client.force_authenticate(user=self.user)
response = self.client.delete(
reverse_with_query(
"channeluser-remove-self",
kwargs={"pk": self.user.id},
query={"channel_id": "not-a-valid-uuid"},
)
)
self.assertEqual(response.status_code, 400, response.content)

def test_remove_self_with_missing_channel_returns_not_found(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Good defensive coverage — the 404 test for a valid-but-nonexistent UUID confirms that the new validation block doesn't accidentally swallow legitimate Channel.DoesNotExist paths.

self.client.force_authenticate(user=self.user)
response = self.client.delete(
reverse_with_query(
"channeluser-remove-self",
kwargs={"pk": self.user.id},
query={"channel_id": "00000000-0000-0000-0000-000000000000"},
)
)
self.assertEqual(response.status_code, 404, response.content)


class MarkReadNotificationsTimestampTestCase(StudioAPITestCase):
def setUp(self):
Expand Down
5 changes: 5 additions & 0 deletions contentcuration/contentcuration/viewsets/user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import csv
import logging
import uuid
from datetime import date
from functools import reduce

Expand Down Expand Up @@ -341,6 +342,10 @@ def remove_self(self, request, pk=None):

if not channel_id:
return HttpResponseBadRequest("Channel ID is required.")
try:
channel_id = uuid.UUID(channel_id).hex

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Normalizing to .hex before the downstream Channel.objects.get() is a nice defensive touch — it canonicalizes any valid UUID variant (with/without dashes, mixed case) into a consistent 32-char hex string, preventing a class of subtle mismatch bugs.

except ValueError:
return HttpResponseBadRequest("Invalid channel ID")

try:
channel = Channel.objects.get(id=channel_id)
Expand Down
Loading