mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-22 09:37:57 +00:00
This update introduces a new `is_catchup_enabled` function to determine if catch-up is allowed for users based on their custom properties and system settings. The `UserViewSet` is modified to restrict admin-managed properties, including catch-up access. Additionally, various views and tests are updated to incorporate catch-up checks, ensuring that users without access receive appropriate error responses. The frontend is enhanced with a catch-up toggle in user and system settings forms, allowing for better management of catch-up capabilities.
159 lines
5.7 KiB
Python
159 lines
5.7 KiB
Python
from django.test import TestCase
|
|
from django.contrib.auth import get_user_model
|
|
from rest_framework.test import APIClient
|
|
from rest_framework import status
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class UserPreferencesAPITests(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(
|
|
username="testuser",
|
|
password="testpass123",
|
|
user_level=10
|
|
)
|
|
self.client = APIClient()
|
|
self.client.force_authenticate(user=self.user)
|
|
self.me_url = "/api/accounts/users/me/"
|
|
|
|
def test_get_me_returns_user_data(self):
|
|
"""Test GET /me/ returns current user data"""
|
|
response = self.client.get(self.me_url)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["username"], "testuser")
|
|
|
|
def test_patch_me_updates_custom_properties(self):
|
|
"""Test PATCH /me/ updates custom_properties"""
|
|
nav_order = ["channels", "vods", "sources", "guide", "dvr", "stats"]
|
|
data = {
|
|
"custom_properties": {
|
|
"navOrder": nav_order
|
|
}
|
|
}
|
|
|
|
response = self.client.patch(self.me_url, data, format="json")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["custom_properties"]["navOrder"], nav_order)
|
|
|
|
# Verify database was updated
|
|
self.user.refresh_from_db()
|
|
self.assertEqual(self.user.custom_properties["navOrder"], nav_order)
|
|
|
|
def test_patch_me_nav_order_persists(self):
|
|
"""Test navOrder persists and returns correctly"""
|
|
nav_order = ["settings", "channels", "vods"]
|
|
data = {
|
|
"custom_properties": {
|
|
"navOrder": nav_order
|
|
}
|
|
}
|
|
|
|
# Update
|
|
self.client.patch(self.me_url, data, format="json")
|
|
|
|
# Fetch again
|
|
response = self.client.get(self.me_url)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["custom_properties"]["navOrder"], nav_order)
|
|
|
|
def test_patch_me_partial_update_preserves_other_properties(self):
|
|
"""Test partial update merges into existing custom_properties, preserving other keys"""
|
|
# Set initial custom_properties
|
|
self.user.custom_properties = {
|
|
"theme": "dark",
|
|
"someOtherSetting": True
|
|
}
|
|
self.user.save()
|
|
|
|
# Update only navOrder - send delta, not full object
|
|
nav_order = ["channels", "vods"]
|
|
data = {
|
|
"custom_properties": {
|
|
"navOrder": nav_order
|
|
}
|
|
}
|
|
|
|
response = self.client.patch(self.me_url, data, format="json")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
# Backend merge semantics: existing keys are preserved
|
|
self.assertEqual(response.data["custom_properties"]["navOrder"], nav_order)
|
|
self.assertEqual(response.data["custom_properties"]["theme"], "dark")
|
|
self.assertEqual(response.data["custom_properties"]["someOtherSetting"], True)
|
|
|
|
def test_patch_me_with_empty_nav_order(self):
|
|
"""Test PATCH with empty navOrder array"""
|
|
data = {
|
|
"custom_properties": {
|
|
"navOrder": []
|
|
}
|
|
}
|
|
|
|
response = self.client.patch(self.me_url, data, format="json")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["custom_properties"]["navOrder"], [])
|
|
|
|
def test_patch_me_updates_first_name(self):
|
|
"""Test PATCH /me/ can update other fields like first_name"""
|
|
data = {
|
|
"first_name": "Test"
|
|
}
|
|
|
|
response = self.client.patch(self.me_url, data, format="json")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.assertEqual(response.data["first_name"], "Test")
|
|
|
|
self.user.refresh_from_db()
|
|
self.assertEqual(self.user.first_name, "Test")
|
|
|
|
def test_patch_me_unauthenticated_fails(self):
|
|
"""Test PATCH /me/ fails for unauthenticated users"""
|
|
self.client.logout()
|
|
unauthenticated_client = APIClient()
|
|
|
|
data = {
|
|
"custom_properties": {
|
|
"navOrder": ["channels"]
|
|
}
|
|
}
|
|
|
|
response = unauthenticated_client.patch(self.me_url, data, format="json")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
|
|
|
def test_patch_me_cannot_escalate_privileges(self):
|
|
"""PATCH /me/ ignores privilege fields; they are stripped before save."""
|
|
original_level = self.user.user_level
|
|
|
|
data = {"user_level": 99, "is_staff": True, "is_superuser": True}
|
|
response = self.client.patch(self.me_url, data, format="json")
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
|
|
self.user.refresh_from_db()
|
|
self.assertEqual(self.user.user_level, original_level)
|
|
self.assertFalse(self.user.is_staff)
|
|
self.assertFalse(self.user.is_superuser)
|
|
|
|
def test_patch_me_cannot_set_catchup_enabled(self):
|
|
"""catchup_enabled is admin-managed; /me must not change it."""
|
|
self.user.custom_properties = {"catchup_enabled": False, "theme": "dark"}
|
|
self.user.save(update_fields=["custom_properties"])
|
|
|
|
response = self.client.patch(
|
|
self.me_url,
|
|
{"custom_properties": {"catchup_enabled": True, "theme": "light"}},
|
|
format="json",
|
|
)
|
|
|
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
self.user.refresh_from_db()
|
|
# Stripped from input; existing False preserved. Other props still update.
|
|
self.assertFalse(self.user.custom_properties.get("catchup_enabled"))
|
|
self.assertEqual(self.user.custom_properties.get("theme"), "light")
|