Refactor test utilities and enhance ASGI settings

- Cleaned up and standardized assertions in ApiTestMixin for API response validation.
- Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE.
- Removed unused imports and improved formatting in settings.py.
- Refactored URL patterns in urls.py for better readability and organization.
- Enhanced view functions in views.py for consistency and clarity.
- Added .flake8 configuration for linting and style enforcement.
- Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
pacnpal
2025-08-20 19:51:59 -04:00
parent 69c07d1381
commit 66ed4347a9
230 changed files with 15094 additions and 11578 deletions

View File

@@ -1,31 +1,40 @@
from django.test import TestCase, Client
from django.urls import reverse
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.http import JsonResponse, HttpRequest
from django.utils import timezone
from django.utils.datastructures import MultiValueDict
from django.http import QueryDict
from .models import EditSubmission, PhotoSubmission
from .mixins import EditSubmissionMixin, PhotoSubmissionMixin, ModeratorRequiredMixin, AdminRequiredMixin, InlineEditMixin, HistoryMixin
from .models import EditSubmission
from .mixins import (
EditSubmissionMixin,
PhotoSubmissionMixin,
ModeratorRequiredMixin,
AdminRequiredMixin,
InlineEditMixin,
HistoryMixin,
)
from parks.models import Company as Operator
from django.views.generic import DetailView
from django.test import RequestFactory
import json
from typing import Optional
User = get_user_model()
class TestView(EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, HistoryMixin, DetailView):
class TestView(
EditSubmissionMixin,
PhotoSubmissionMixin,
InlineEditMixin,
HistoryMixin,
DetailView,
):
model = Operator
template_name = 'test.html'
pk_url_kwarg = 'pk'
slug_url_kwarg = 'slug'
template_name = "test.html"
pk_url_kwarg = "pk"
slug_url_kwarg = "slug"
def get_context_data(self, **kwargs):
if not hasattr(self, 'object'):
if not hasattr(self, "object"):
self.object = self.get_object()
return super().get_context_data(**kwargs)
@@ -33,44 +42,45 @@ class TestView(EditSubmissionMixin, PhotoSubmissionMixin, InlineEditMixin, Histo
super().setup(request, *args, **kwargs)
self.request = request
class ModerationMixinsTests(TestCase):
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
# Create users with different roles
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
username="testuser",
email="test@example.com",
password="testpass123",
)
self.moderator = User.objects.create_user(
username='moderator',
email='moderator@example.com',
password='modpass123',
role='MODERATOR'
username="moderator",
email="moderator@example.com",
password="modpass123",
role="MODERATOR",
)
self.admin = User.objects.create_user(
username='admin',
email='admin@example.com',
password='adminpass123',
role='ADMIN'
username="admin",
email="admin@example.com",
password="adminpass123",
role="ADMIN",
)
# Create test company
self.operator = Operator.objects.create(
name='Test Operator',
website='http://example.com',
description='Test Description'
name="Test Operator",
website="http://example.com",
description="Test Description",
)
def test_edit_submission_mixin_unauthenticated(self):
"""Test edit submission when not logged in"""
view = TestView()
request = self.factory.post(f'/test/{self.operator.pk}/')
request = self.factory.post(f"/test/{self.operator.pk}/")
request.user = AnonymousUser()
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
response = view.handle_edit_submission(request, {})
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 403)
@@ -79,13 +89,13 @@ class ModerationMixinsTests(TestCase):
"""Test edit submission with no changes"""
view = TestView()
request = self.factory.post(
f'/test/{self.operator.pk}/',
f"/test/{self.operator.pk}/",
data=json.dumps({}),
content_type='application/json'
content_type="application/json",
)
request.user = self.user
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
response = view.post(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 400)
@@ -94,13 +104,13 @@ class ModerationMixinsTests(TestCase):
"""Test edit submission with invalid JSON"""
view = TestView()
request = self.factory.post(
f'/test/{self.operator.pk}/',
data='invalid json',
content_type='application/json'
f"/test/{self.operator.pk}/",
data="invalid json",
content_type="application/json",
)
request.user = self.user
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
response = view.post(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 400)
@@ -108,41 +118,43 @@ class ModerationMixinsTests(TestCase):
def test_edit_submission_mixin_regular_user(self):
"""Test edit submission as regular user"""
view = TestView()
request = self.factory.post(f'/test/{self.operator.pk}/')
request = self.factory.post(f"/test/{self.operator.pk}/")
request.user = self.user
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
changes = {'name': 'New Name'}
response = view.handle_edit_submission(request, changes, 'Test reason', 'Test source')
view.kwargs = {"pk": self.operator.pk}
changes = {"name": "New Name"}
response = view.handle_edit_submission(
request, changes, "Test reason", "Test source"
)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 200)
data = json.loads(response.content.decode())
self.assertFalse(data['auto_approved'])
self.assertFalse(data["auto_approved"])
def test_edit_submission_mixin_moderator(self):
"""Test edit submission as moderator"""
view = TestView()
request = self.factory.post(f'/test/{self.operator.pk}/')
request = self.factory.post(f"/test/{self.operator.pk}/")
request.user = self.moderator
view.setup(request, pk=self.operator.pk)
view.kwargs = {'pk': self.operator.pk}
changes = {'name': 'New Name'}
response = view.handle_edit_submission(request, changes, 'Test reason', 'Test source')
view.kwargs = {"pk": self.operator.pk}
changes = {"name": "New Name"}
response = view.handle_edit_submission(
request, changes, "Test reason", "Test source"
)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 200)
data = json.loads(response.content.decode())
self.assertTrue(data['auto_approved'])
self.assertTrue(data["auto_approved"])
def test_photo_submission_mixin_unauthenticated(self):
"""Test photo submission when not logged in"""
view = TestView()
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
view.object = self.operator
request = self.factory.post(
f'/test/{self.operator.pk}/',
data={},
format='multipart'
f"/test/{self.operator.pk}/", data={}, format="multipart"
)
request.user = AnonymousUser()
view.setup(request, pk=self.operator.pk)
@@ -153,13 +165,11 @@ class ModerationMixinsTests(TestCase):
def test_photo_submission_mixin_no_photo(self):
"""Test photo submission with no photo"""
view = TestView()
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
view.object = self.operator
request = self.factory.post(
f'/test/{self.operator.pk}/',
data={},
format='multipart'
f"/test/{self.operator.pk}/", data={}, format="multipart"
)
request.user = self.user
view.setup(request, pk=self.operator.pk)
@@ -170,80 +180,89 @@ class ModerationMixinsTests(TestCase):
def test_photo_submission_mixin_regular_user(self):
"""Test photo submission as regular user"""
view = TestView()
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
view.object = self.operator
# Create a test photo file
photo = SimpleUploadedFile(
'test.gif',
b'GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;',
content_type='image/gif'
"test.gif",
b"GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;",
content_type="image/gif",
)
request = self.factory.post(
f'/test/{self.operator.pk}/',
data={'photo': photo, 'caption': 'Test Photo', 'date_taken': '2024-01-01'},
format='multipart'
f"/test/{self.operator.pk}/",
data={
"photo": photo,
"caption": "Test Photo",
"date_taken": "2024-01-01",
},
format="multipart",
)
request.user = self.user
view.setup(request, pk=self.operator.pk)
response = view.handle_photo_submission(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 200)
data = json.loads(response.content.decode())
self.assertFalse(data['auto_approved'])
self.assertFalse(data["auto_approved"])
def test_photo_submission_mixin_moderator(self):
"""Test photo submission as moderator"""
view = TestView()
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
view.object = self.operator
# Create a test photo file
photo = SimpleUploadedFile(
'test.gif',
b'GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;',
content_type='image/gif'
"test.gif",
b"GIF87a\x01\x00\x01\x00\x80\x01\x00\x00\x00\x00ccc,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;",
content_type="image/gif",
)
request = self.factory.post(
f'/test/{self.operator.pk}/',
data={'photo': photo, 'caption': 'Test Photo', 'date_taken': '2024-01-01'},
format='multipart'
f"/test/{self.operator.pk}/",
data={
"photo": photo,
"caption": "Test Photo",
"date_taken": "2024-01-01",
},
format="multipart",
)
request.user = self.moderator
view.setup(request, pk=self.operator.pk)
response = view.handle_photo_submission(request)
self.assertIsInstance(response, JsonResponse)
self.assertEqual(response.status_code, 200)
data = json.loads(response.content.decode())
self.assertTrue(data['auto_approved'])
self.assertTrue(data["auto_approved"])
def test_moderator_required_mixin(self):
"""Test moderator required mixin"""
class TestModeratorView(ModeratorRequiredMixin):
pass
view = TestModeratorView()
# Test unauthenticated user
request = self.factory.get('/test/')
request = self.factory.get("/test/")
request.user = AnonymousUser()
view.request = request
self.assertFalse(view.test_func())
# Test regular user
request.user = self.user
view.request = request
self.assertFalse(view.test_func())
# Test moderator
request.user = self.moderator
view.request = request
self.assertTrue(view.test_func())
# Test admin
request.user = self.admin
view.request = request
@@ -251,27 +270,28 @@ class ModerationMixinsTests(TestCase):
def test_admin_required_mixin(self):
"""Test admin required mixin"""
class TestAdminView(AdminRequiredMixin):
pass
view = TestAdminView()
# Test unauthenticated user
request = self.factory.get('/test/')
request = self.factory.get("/test/")
request.user = AnonymousUser()
view.request = request
self.assertFalse(view.test_func())
# Test regular user
request.user = self.user
view.request = request
self.assertFalse(view.test_func())
# Test moderator
request.user = self.moderator
view.request = request
self.assertFalse(view.test_func())
# Test admin
request.user = self.admin
view.request = request
@@ -280,50 +300,50 @@ class ModerationMixinsTests(TestCase):
def test_inline_edit_mixin(self):
"""Test inline edit mixin"""
view = TestView()
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
view.object = self.operator
# Test unauthenticated user
request = self.factory.get(f'/test/{self.operator.pk}/')
request = self.factory.get(f"/test/{self.operator.pk}/")
request.user = AnonymousUser()
view.setup(request, pk=self.operator.pk)
context = view.get_context_data()
self.assertNotIn('can_edit', context)
self.assertNotIn("can_edit", context)
# Test regular user
request.user = self.user
view.setup(request, pk=self.operator.pk)
context = view.get_context_data()
self.assertTrue(context['can_edit'])
self.assertFalse(context['can_auto_approve'])
self.assertTrue(context["can_edit"])
self.assertFalse(context["can_auto_approve"])
# Test moderator
request.user = self.moderator
view.setup(request, pk=self.operator.pk)
context = view.get_context_data()
self.assertTrue(context['can_edit'])
self.assertTrue(context['can_auto_approve'])
self.assertTrue(context["can_edit"])
self.assertTrue(context["can_auto_approve"])
def test_history_mixin(self):
"""Test history mixin"""
view = TestView()
view.kwargs = {'pk': self.operator.pk}
view.kwargs = {"pk": self.operator.pk}
view.object = self.operator
request = self.factory.get(f'/test/{self.operator.pk}/')
request = self.factory.get(f"/test/{self.operator.pk}/")
request.user = self.user
view.setup(request, pk=self.operator.pk)
# Create some edit submissions
EditSubmission.objects.create(
user=self.user,
content_type=ContentType.objects.get_for_model(Operator),
object_id=getattr(self.operator, 'id', None),
submission_type='EDIT',
changes={'name': 'New Name'},
status='APPROVED'
object_id=getattr(self.operator, "id", None),
submission_type="EDIT",
changes={"name": "New Name"},
status="APPROVED",
)
context = view.get_context_data()
self.assertIn('history', context)
self.assertIn('edit_submissions', context)
self.assertEqual(len(context['edit_submissions']), 1)
self.assertIn("history", context)
self.assertIn("edit_submissions", context)
self.assertEqual(len(context["edit_submissions"]), 1)