feat: Implement MFA authentication, add ride statistics model, and update various services, APIs, and tests across the application.

This commit is contained in:
pacnpal
2025-12-28 17:32:53 -05:00
parent aa56c46c27
commit c95f99ca10
452 changed files with 7948 additions and 6073 deletions

View File

@@ -6,7 +6,6 @@ state log, and history event admin classes including query optimization
and custom moderation actions.
"""
import pytest
from django.contrib.admin.sites import AdminSite
from django.contrib.auth import get_user_model
from django.test import RequestFactory, TestCase
@@ -14,7 +13,6 @@ from django.test import RequestFactory, TestCase
from apps.moderation.admin import (
EditSubmissionAdmin,
HistoryEventAdmin,
ModerationAdminSite,
PhotoSubmissionAdmin,
StateLogAdmin,
moderation_site,

View File

@@ -9,18 +9,18 @@ This module tests end-to-end moderation workflows including:
- Bulk operation workflow
"""
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.utils import timezone
from unittest.mock import patch, Mock
User = get_user_model()
class SubmissionApprovalWorkflowTests(TestCase):
"""Tests for the complete submission approval workflow."""
@classmethod
def setUpTestData(cls):
"""Set up test data for all tests."""
@@ -42,22 +42,22 @@ class SubmissionApprovalWorkflowTests(TestCase):
password='testpass123',
role='ADMIN'
)
def test_edit_submission_approval_workflow(self):
"""
Test complete edit submission approval workflow.
Flow: User submits → Moderator reviews → Moderator approves → Changes applied
"""
from apps.moderation.models import EditSubmission
from apps.parks.models import Company
# Create target object
company = Company.objects.create(
name='Test Company',
description='Original description'
)
# User submits an edit
content_type = ContentType.objects.get_for_model(company)
submission = EditSubmission.objects.create(
@@ -69,31 +69,31 @@ class SubmissionApprovalWorkflowTests(TestCase):
status='PENDING',
reason='Fixing typo'
)
self.assertEqual(submission.status, 'PENDING')
self.assertIsNone(submission.handled_by)
self.assertIsNone(submission.handled_at)
# Moderator approves
submission.transition_to_approved(user=self.moderator)
submission.handled_by = self.moderator
submission.handled_at = timezone.now()
submission.save()
submission.refresh_from_db()
self.assertEqual(submission.status, 'APPROVED')
self.assertEqual(submission.handled_by, self.moderator)
self.assertIsNotNone(submission.handled_at)
def test_photo_submission_approval_workflow(self):
"""
Test complete photo submission approval workflow.
Flow: User submits photo → Moderator reviews → Moderator approves → Photo created
"""
from apps.moderation.models import PhotoSubmission
from apps.parks.models import Park, Company
from apps.parks.models import Company, Park
# Create target park
operator = Company.objects.create(
name='Test Operator',
@@ -106,7 +106,7 @@ class SubmissionApprovalWorkflowTests(TestCase):
status='OPERATING',
timezone='America/New_York'
)
# User submits a photo
content_type = ContentType.objects.get_for_model(park)
submission = PhotoSubmission.objects.create(
@@ -117,22 +117,22 @@ class SubmissionApprovalWorkflowTests(TestCase):
photo_type='GENERAL',
description='Beautiful park entrance'
)
self.assertEqual(submission.status, 'PENDING')
# Moderator approves
submission.transition_to_approved(user=self.moderator)
submission.handled_by = self.moderator
submission.handled_at = timezone.now()
submission.save()
submission.refresh_from_db()
self.assertEqual(submission.status, 'APPROVED')
class SubmissionRejectionWorkflowTests(TestCase):
"""Tests for the submission rejection workflow."""
@classmethod
def setUpTestData(cls):
cls.regular_user = User.objects.create_user(
@@ -147,21 +147,21 @@ class SubmissionRejectionWorkflowTests(TestCase):
password='testpass123',
role='MODERATOR'
)
def test_edit_submission_rejection_with_reason(self):
"""
Test rejection workflow with reason.
Flow: User submits → Moderator rejects with reason → User notified
"""
from apps.moderation.models import EditSubmission
from apps.parks.models import Company
company = Company.objects.create(
name='Test Company',
description='Original'
)
content_type = ContentType.objects.get_for_model(company)
submission = EditSubmission.objects.create(
user=self.regular_user,
@@ -172,14 +172,14 @@ class SubmissionRejectionWorkflowTests(TestCase):
status='PENDING',
reason='Name change request'
)
# Moderator rejects
submission.transition_to_rejected(user=self.moderator)
submission.handled_by = self.moderator
submission.handled_at = timezone.now()
submission.notes = 'Rejected: Content appears to be spam'
submission.save()
submission.refresh_from_db()
self.assertEqual(submission.status, 'REJECTED')
self.assertIn('spam', submission.notes.lower())
@@ -187,7 +187,7 @@ class SubmissionRejectionWorkflowTests(TestCase):
class SubmissionEscalationWorkflowTests(TestCase):
"""Tests for the submission escalation workflow."""
@classmethod
def setUpTestData(cls):
cls.regular_user = User.objects.create_user(
@@ -208,21 +208,21 @@ class SubmissionEscalationWorkflowTests(TestCase):
password='testpass123',
role='ADMIN'
)
def test_escalation_workflow(self):
"""
Test complete escalation workflow.
Flow: User submits → Moderator escalates → Admin reviews → Admin approves
"""
from apps.moderation.models import EditSubmission
from apps.parks.models import Company
company = Company.objects.create(
name='Sensitive Company',
description='Original'
)
content_type = ContentType.objects.get_for_model(company)
submission = EditSubmission.objects.create(
user=self.regular_user,
@@ -233,20 +233,20 @@ class SubmissionEscalationWorkflowTests(TestCase):
status='PENDING',
reason='Major name change'
)
# Moderator escalates
submission.transition_to_escalated(user=self.moderator)
submission.notes = 'Escalated: Major change needs admin review'
submission.save()
self.assertEqual(submission.status, 'ESCALATED')
# Admin approves
submission.transition_to_approved(user=self.admin)
submission.handled_by = self.admin
submission.handled_at = timezone.now()
submission.save()
submission.refresh_from_db()
self.assertEqual(submission.status, 'APPROVED')
self.assertEqual(submission.handled_by, self.admin)
@@ -254,7 +254,7 @@ class SubmissionEscalationWorkflowTests(TestCase):
class ReportHandlingWorkflowTests(TestCase):
"""Tests for the moderation report handling workflow."""
@classmethod
def setUpTestData(cls):
cls.reporter = User.objects.create_user(
@@ -269,23 +269,23 @@ class ReportHandlingWorkflowTests(TestCase):
password='testpass123',
role='MODERATOR'
)
def test_report_resolution_workflow(self):
"""
Test complete report resolution workflow.
Flow: User reports → Moderator assigned → Moderator investigates → Resolved
"""
from apps.moderation.models import ModerationReport
from apps.parks.models import Company
reported_company = Company.objects.create(
name='Problematic Company',
description='Some inappropriate content'
)
content_type = ContentType.objects.get_for_model(reported_company)
# User reports content
report = ModerationReport.objects.create(
report_type='CONTENT',
@@ -298,44 +298,44 @@ class ReportHandlingWorkflowTests(TestCase):
description='This content is inappropriate',
reported_by=self.reporter
)
self.assertEqual(report.status, 'PENDING')
# Moderator claims and starts review
report.transition_to_under_review(user=self.moderator)
report.assigned_moderator = self.moderator
report.save()
self.assertEqual(report.status, 'UNDER_REVIEW')
self.assertEqual(report.assigned_moderator, self.moderator)
# Moderator resolves
report.transition_to_resolved(user=self.moderator)
report.resolution_action = 'CONTENT_REMOVED'
report.resolution_notes = 'Content was removed'
report.resolved_at = timezone.now()
report.save()
report.refresh_from_db()
self.assertEqual(report.status, 'RESOLVED')
self.assertIsNotNone(report.resolved_at)
def test_report_dismissal_workflow(self):
"""
Test report dismissal workflow for invalid reports.
Flow: User reports → Moderator reviews → Moderator dismisses
"""
from apps.moderation.models import ModerationReport
from apps.parks.models import Company
company = Company.objects.create(
name='Valid Company',
description='Normal content'
)
content_type = ContentType.objects.get_for_model(company)
report = ModerationReport.objects.create(
report_type='CONTENT',
status='PENDING',
@@ -347,25 +347,25 @@ class ReportHandlingWorkflowTests(TestCase):
description='I just do not like this',
reported_by=self.reporter
)
# Moderator claims
report.transition_to_under_review(user=self.moderator)
report.assigned_moderator = self.moderator
report.save()
# Moderator dismisses as invalid
report.transition_to_dismissed(user=self.moderator)
report.resolution_notes = 'Report does not violate any guidelines'
report.resolved_at = timezone.now()
report.save()
report.refresh_from_db()
self.assertEqual(report.status, 'DISMISSED')
class BulkOperationWorkflowTests(TestCase):
"""Tests for bulk operation workflows."""
@classmethod
def setUpTestData(cls):
cls.admin = User.objects.create_user(
@@ -374,15 +374,15 @@ class BulkOperationWorkflowTests(TestCase):
password='testpass123',
role='ADMIN'
)
def test_bulk_operation_success_workflow(self):
"""
Test successful bulk operation workflow.
Flow: Admin creates → Operation runs → Progress tracked → Completed
"""
from apps.moderation.models import BulkOperation
operation = BulkOperation.objects.create(
operation_type='APPROVE_SUBMISSIONS',
status='PENDING',
@@ -391,39 +391,39 @@ class BulkOperationWorkflowTests(TestCase):
created_by=self.admin,
parameters={'submission_ids': list(range(1, 11))}
)
self.assertEqual(operation.status, 'PENDING')
# Start operation
operation.transition_to_running(user=self.admin)
operation.started_at = timezone.now()
operation.save()
self.assertEqual(operation.status, 'RUNNING')
# Simulate progress
for i in range(1, 11):
operation.processed_items = i
operation.save()
# Complete operation
operation.transition_to_completed(user=self.admin)
operation.completed_at = timezone.now()
operation.results = {'approved': 10, 'failed': 0}
operation.save()
operation.refresh_from_db()
self.assertEqual(operation.status, 'COMPLETED')
self.assertEqual(operation.processed_items, 10)
def test_bulk_operation_failure_workflow(self):
"""
Test bulk operation failure workflow.
Flow: Admin creates → Operation runs → Error occurs → Failed
"""
from apps.moderation.models import BulkOperation
operation = BulkOperation.objects.create(
operation_type='DELETE_CONTENT',
status='PENDING',
@@ -432,11 +432,11 @@ class BulkOperationWorkflowTests(TestCase):
created_by=self.admin,
parameters={'content_ids': list(range(1, 6))}
)
operation.transition_to_running(user=self.admin)
operation.started_at = timezone.now()
operation.save()
# Simulate partial progress then failure
operation.processed_items = 2
operation.failed_items = 3
@@ -444,19 +444,19 @@ class BulkOperationWorkflowTests(TestCase):
operation.completed_at = timezone.now()
operation.results = {'error': 'Database connection lost', 'processed': 2}
operation.save()
operation.refresh_from_db()
self.assertEqual(operation.status, 'FAILED')
self.assertEqual(operation.failed_items, 3)
def test_bulk_operation_cancellation_workflow(self):
"""
Test bulk operation cancellation workflow.
Flow: Admin creates → Operation runs → Admin cancels
"""
from apps.moderation.models import BulkOperation
operation = BulkOperation.objects.create(
operation_type='BATCH_UPDATE',
status='PENDING',
@@ -466,20 +466,20 @@ class BulkOperationWorkflowTests(TestCase):
parameters={'update_field': 'status'},
can_cancel=True
)
operation.transition_to_running(user=self.admin)
operation.save()
# Partial progress
operation.processed_items = 30
operation.save()
# Admin cancels
operation.transition_to_cancelled(user=self.admin)
operation.completed_at = timezone.now()
operation.results = {'cancelled_at': 30, 'reason': 'User requested cancellation'}
operation.save()
operation.refresh_from_db()
self.assertEqual(operation.status, 'CANCELLED')
self.assertEqual(operation.processed_items, 30)
@@ -487,7 +487,7 @@ class BulkOperationWorkflowTests(TestCase):
class ModerationQueueWorkflowTests(TestCase):
"""Tests for moderation queue workflows."""
@classmethod
def setUpTestData(cls):
cls.moderator = User.objects.create_user(
@@ -496,15 +496,15 @@ class ModerationQueueWorkflowTests(TestCase):
password='testpass123',
role='MODERATOR'
)
def test_queue_completion_workflow(self):
"""
Test queue item completion workflow.
Flow: Item created → Moderator claims → Work done → Completed
"""
from apps.moderation.models import ModerationQueue
queue_item = ModerationQueue.objects.create(
queue_type='SUBMISSION_REVIEW',
status='PENDING',
@@ -512,21 +512,21 @@ class ModerationQueueWorkflowTests(TestCase):
item_type='edit_submission',
item_id=123
)
self.assertEqual(queue_item.status, 'PENDING')
# Moderator claims
queue_item.transition_to_in_progress(user=self.moderator)
queue_item.assigned_to = self.moderator
queue_item.assigned_at = timezone.now()
queue_item.save()
self.assertEqual(queue_item.status, 'IN_PROGRESS')
# Work completed
queue_item.transition_to_completed(user=self.moderator)
queue_item.completed_at = timezone.now()
queue_item.save()
queue_item.refresh_from_db()
self.assertEqual(queue_item.status, 'COMPLETED')