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 companies.models import Company 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): model = Company template_name = 'test.html' pk_url_kwarg = 'pk' slug_url_kwarg = 'slug' def get_context_data(self, **kwargs): if not hasattr(self, 'object'): self.object = self.get_object() return super().get_context_data(**kwargs) def setup(self, request: HttpRequest, *args, **kwargs): 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' ) self.moderator = User.objects.create_user( 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' ) # Create test company self.company = Company.objects.create( name='Test Company', website='http://example.com', headquarters='Test HQ', 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.company.pk}/') request.user = AnonymousUser() view.setup(request, pk=self.company.pk) view.kwargs = {'pk': self.company.pk} response = view.handle_edit_submission(request, {}) self.assertIsInstance(response, JsonResponse) self.assertEqual(response.status_code, 403) def test_edit_submission_mixin_no_changes(self): """Test edit submission with no changes""" view = TestView() request = self.factory.post( f'/test/{self.company.pk}/', data=json.dumps({}), content_type='application/json' ) request.user = self.user view.setup(request, pk=self.company.pk) view.kwargs = {'pk': self.company.pk} response = view.post(request) self.assertIsInstance(response, JsonResponse) self.assertEqual(response.status_code, 400) def test_edit_submission_mixin_invalid_json(self): """Test edit submission with invalid JSON""" view = TestView() request = self.factory.post( f'/test/{self.company.pk}/', data='invalid json', content_type='application/json' ) request.user = self.user view.setup(request, pk=self.company.pk) view.kwargs = {'pk': self.company.pk} response = view.post(request) self.assertIsInstance(response, JsonResponse) self.assertEqual(response.status_code, 400) def test_edit_submission_mixin_regular_user(self): """Test edit submission as regular user""" view = TestView() request = self.factory.post(f'/test/{self.company.pk}/') request.user = self.user view.setup(request, pk=self.company.pk) view.kwargs = {'pk': self.company.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']) def test_edit_submission_mixin_moderator(self): """Test edit submission as moderator""" view = TestView() request = self.factory.post(f'/test/{self.company.pk}/') request.user = self.moderator view.setup(request, pk=self.company.pk) view.kwargs = {'pk': self.company.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']) def test_photo_submission_mixin_unauthenticated(self): """Test photo submission when not logged in""" view = TestView() view.kwargs = {'pk': self.company.pk} view.object = self.company request = self.factory.post( f'/test/{self.company.pk}/', data={}, format='multipart' ) request.user = AnonymousUser() view.setup(request, pk=self.company.pk) response = view.handle_photo_submission(request) self.assertIsInstance(response, JsonResponse) self.assertEqual(response.status_code, 403) def test_photo_submission_mixin_no_photo(self): """Test photo submission with no photo""" view = TestView() view.kwargs = {'pk': self.company.pk} view.object = self.company request = self.factory.post( f'/test/{self.company.pk}/', data={}, format='multipart' ) request.user = self.user view.setup(request, pk=self.company.pk) response = view.handle_photo_submission(request) self.assertIsInstance(response, JsonResponse) self.assertEqual(response.status_code, 400) def test_photo_submission_mixin_regular_user(self): """Test photo submission as regular user""" view = TestView() view.kwargs = {'pk': self.company.pk} view.object = self.company # 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' ) request = self.factory.post( f'/test/{self.company.pk}/', data={'photo': photo, 'caption': 'Test Photo', 'date_taken': '2024-01-01'}, format='multipart' ) request.user = self.user view.setup(request, pk=self.company.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']) def test_photo_submission_mixin_moderator(self): """Test photo submission as moderator""" view = TestView() view.kwargs = {'pk': self.company.pk} view.object = self.company # 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' ) request = self.factory.post( f'/test/{self.company.pk}/', data={'photo': photo, 'caption': 'Test Photo', 'date_taken': '2024-01-01'}, format='multipart' ) request.user = self.moderator view.setup(request, pk=self.company.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']) 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.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 self.assertTrue(view.test_func()) 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.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 self.assertTrue(view.test_func()) def test_inline_edit_mixin(self): """Test inline edit mixin""" view = TestView() view.kwargs = {'pk': self.company.pk} view.object = self.company # Test unauthenticated user request = self.factory.get(f'/test/{self.company.pk}/') request.user = AnonymousUser() view.setup(request, pk=self.company.pk) context = view.get_context_data() self.assertNotIn('can_edit', context) # Test regular user request.user = self.user view.setup(request, pk=self.company.pk) context = view.get_context_data() self.assertTrue(context['can_edit']) self.assertFalse(context['can_auto_approve']) # Test moderator request.user = self.moderator view.setup(request, pk=self.company.pk) context = view.get_context_data() 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.company.pk} view.object = self.company request = self.factory.get(f'/test/{self.company.pk}/') request.user = self.user view.setup(request, pk=self.company.pk) # Create some edit submissions EditSubmission.objects.create( user=self.user, content_type=ContentType.objects.get_for_model(Company), object_id=getattr(self.company, '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)