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

@@ -8,13 +8,14 @@ This module contains tests for:
- Related model updates during transitions
"""
from django.test import TestCase
from datetime import date
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone
from django.test import TestCase
from django_fsm import TransitionNotAllowed
from .models import Park, Company
from datetime import date
from .models import Company, Park
User = get_user_model()
@@ -47,7 +48,7 @@ class ParkTransitionTests(TestCase):
password='testpass123',
role='ADMIN'
)
# Create operator company
self.operator = Company.objects.create(
name='Test Operator',
@@ -75,21 +76,21 @@ class ParkTransitionTests(TestCase):
"""Test transition from OPERATING to CLOSED_TEMP."""
park = self._create_park(status='OPERATING')
self.assertEqual(park.status, 'OPERATING')
park.transition_to_closed_temp(user=self.user)
park.save()
park.refresh_from_db()
self.assertEqual(park.status, 'CLOSED_TEMP')
def test_operating_to_closed_perm_transition(self):
"""Test transition from OPERATING to CLOSED_PERM."""
park = self._create_park(status='OPERATING')
park.transition_to_closed_perm(user=self.moderator)
park.closing_date = date.today()
park.save()
park.refresh_from_db()
self.assertEqual(park.status, 'CLOSED_PERM')
self.assertIsNotNone(park.closing_date)
@@ -102,10 +103,10 @@ class ParkTransitionTests(TestCase):
"""Test transition from UNDER_CONSTRUCTION to OPERATING."""
park = self._create_park(status='UNDER_CONSTRUCTION')
self.assertEqual(park.status, 'UNDER_CONSTRUCTION')
park.transition_to_operating(user=self.user)
park.save()
park.refresh_from_db()
self.assertEqual(park.status, 'OPERATING')
@@ -116,21 +117,21 @@ class ParkTransitionTests(TestCase):
def test_closed_temp_to_operating_transition(self):
"""Test transition from CLOSED_TEMP to OPERATING (reopen)."""
park = self._create_park(status='CLOSED_TEMP')
park.transition_to_operating(user=self.user)
park.save()
park.refresh_from_db()
self.assertEqual(park.status, 'OPERATING')
def test_closed_temp_to_closed_perm_transition(self):
"""Test transition from CLOSED_TEMP to CLOSED_PERM."""
park = self._create_park(status='CLOSED_TEMP')
park.transition_to_closed_perm(user=self.moderator)
park.closing_date = date.today()
park.save()
park.refresh_from_db()
self.assertEqual(park.status, 'CLOSED_PERM')
@@ -141,20 +142,20 @@ class ParkTransitionTests(TestCase):
def test_closed_perm_to_demolished_transition(self):
"""Test transition from CLOSED_PERM to DEMOLISHED."""
park = self._create_park(status='CLOSED_PERM')
park.transition_to_demolished(user=self.moderator)
park.save()
park.refresh_from_db()
self.assertEqual(park.status, 'DEMOLISHED')
def test_closed_perm_to_relocated_transition(self):
"""Test transition from CLOSED_PERM to RELOCATED."""
park = self._create_park(status='CLOSED_PERM')
park.transition_to_relocated(user=self.moderator)
park.save()
park.refresh_from_db()
self.assertEqual(park.status, 'RELOCATED')
@@ -165,28 +166,28 @@ class ParkTransitionTests(TestCase):
def test_demolished_cannot_transition(self):
"""Test that DEMOLISHED state cannot transition further."""
park = self._create_park(status='DEMOLISHED')
with self.assertRaises(TransitionNotAllowed):
park.transition_to_operating(user=self.moderator)
def test_relocated_cannot_transition(self):
"""Test that RELOCATED state cannot transition further."""
park = self._create_park(status='RELOCATED')
with self.assertRaises(TransitionNotAllowed):
park.transition_to_operating(user=self.moderator)
def test_operating_cannot_directly_demolish(self):
"""Test that OPERATING cannot directly transition to DEMOLISHED."""
park = self._create_park(status='OPERATING')
with self.assertRaises(TransitionNotAllowed):
park.transition_to_demolished(user=self.moderator)
def test_operating_cannot_directly_relocate(self):
"""Test that OPERATING cannot directly transition to RELOCATED."""
park = self._create_park(status='OPERATING')
with self.assertRaises(TransitionNotAllowed):
park.transition_to_relocated(user=self.moderator)
@@ -197,18 +198,18 @@ class ParkTransitionTests(TestCase):
def test_reopen_wrapper_method(self):
"""Test the reopen() wrapper method."""
park = self._create_park(status='CLOSED_TEMP')
park.reopen(user=self.user)
park.refresh_from_db()
self.assertEqual(park.status, 'OPERATING')
def test_close_temporarily_wrapper_method(self):
"""Test the close_temporarily() wrapper method."""
park = self._create_park(status='OPERATING')
park.close_temporarily(user=self.user)
park.refresh_from_db()
self.assertEqual(park.status, 'CLOSED_TEMP')
@@ -216,9 +217,9 @@ class ParkTransitionTests(TestCase):
"""Test the close_permanently() wrapper method."""
park = self._create_park(status='OPERATING')
closing = date(2025, 12, 31)
park.close_permanently(closing_date=closing, user=self.moderator)
park.refresh_from_db()
self.assertEqual(park.status, 'CLOSED_PERM')
self.assertEqual(park.closing_date, closing)
@@ -226,9 +227,9 @@ class ParkTransitionTests(TestCase):
def test_close_permanently_without_date(self):
"""Test close_permanently() without closing_date."""
park = self._create_park(status='OPERATING')
park.close_permanently(user=self.moderator)
park.refresh_from_db()
self.assertEqual(park.status, 'CLOSED_PERM')
self.assertIsNone(park.closing_date)
@@ -236,18 +237,18 @@ class ParkTransitionTests(TestCase):
def test_demolish_wrapper_method(self):
"""Test the demolish() wrapper method."""
park = self._create_park(status='CLOSED_PERM')
park.demolish(user=self.moderator)
park.refresh_from_db()
self.assertEqual(park.status, 'DEMOLISHED')
def test_relocate_wrapper_method(self):
"""Test the relocate() wrapper method."""
park = self._create_park(status='CLOSED_PERM')
park.relocate(user=self.moderator)
park.refresh_from_db()
self.assertEqual(park.status, 'RELOCATED')
@@ -300,18 +301,18 @@ class ParkTransitionHistoryTests(TestCase):
def test_transition_creates_state_log(self):
"""Test that transitions create StateLog entries."""
from django_fsm_log.models import StateLog
park = self._create_park(status='OPERATING')
park.transition_to_closed_temp(user=self.moderator)
park.save()
park_ct = ContentType.objects.get_for_model(park)
log = StateLog.objects.filter(
content_type=park_ct,
object_id=park.id
).first()
self.assertIsNotNone(log)
self.assertEqual(log.state, 'CLOSED_TEMP')
self.assertEqual(log.by, self.moderator)
@@ -319,23 +320,23 @@ class ParkTransitionHistoryTests(TestCase):
def test_multiple_transitions_create_multiple_logs(self):
"""Test that multiple transitions create multiple log entries."""
from django_fsm_log.models import StateLog
park = self._create_park(status='OPERATING')
park_ct = ContentType.objects.get_for_model(park)
# First transition
park.transition_to_closed_temp(user=self.moderator)
park.save()
# Second transition
park.transition_to_operating(user=self.moderator)
park.save()
logs = StateLog.objects.filter(
content_type=park_ct,
object_id=park.id
).order_by('timestamp')
self.assertEqual(logs.count(), 2)
self.assertEqual(logs[0].state, 'CLOSED_TEMP')
self.assertEqual(logs[1].state, 'OPERATING')
@@ -343,18 +344,18 @@ class ParkTransitionHistoryTests(TestCase):
def test_transition_log_includes_user(self):
"""Test that transition logs include the user who made the change."""
from django_fsm_log.models import StateLog
park = self._create_park(status='OPERATING')
park.transition_to_closed_perm(user=self.moderator)
park.save()
park_ct = ContentType.objects.get_for_model(park)
log = StateLog.objects.filter(
content_type=park_ct,
object_id=park.id
).first()
self.assertEqual(log.by, self.moderator)
@@ -388,7 +389,7 @@ class ParkBusinessLogicTests(TestCase):
operator=self.operator,
timezone='America/New_York'
)
self.assertEqual(park.operator, self.operator)
def test_park_slug_auto_generated(self):
@@ -399,7 +400,7 @@ class ParkBusinessLogicTests(TestCase):
operator=self.operator,
timezone='America/New_York'
)
self.assertEqual(park.slug, 'my-amazing-theme-park')
def test_park_url_generated(self):
@@ -411,7 +412,7 @@ class ParkBusinessLogicTests(TestCase):
operator=self.operator,
timezone='America/New_York'
)
self.assertIn('test-park', park.url)
def test_opening_year_computed_from_opening_date(self):
@@ -424,7 +425,7 @@ class ParkBusinessLogicTests(TestCase):
opening_date=date(2020, 6, 15),
timezone='America/New_York'
)
self.assertEqual(park.opening_year, 2020)
def test_search_text_populated(self):
@@ -436,7 +437,7 @@ class ParkBusinessLogicTests(TestCase):
operator=self.operator,
timezone='America/New_York'
)
self.assertIn('test park', park.search_text)
self.assertIn('wonderful theme park', park.search_text)
self.assertIn('test operator', park.search_text)
@@ -451,7 +452,7 @@ class ParkBusinessLogicTests(TestCase):
property_owner=self.property_owner,
timezone='America/New_York'
)
self.assertEqual(park.operator, self.operator)
self.assertEqual(park.property_owner, self.property_owner)
@@ -475,21 +476,22 @@ class ParkSlugHistoryTests(TestCase):
def test_historical_slug_created_on_name_change(self):
"""Test that historical slug is created when name changes."""
from django.contrib.contenttypes.models import ContentType
from apps.core.history import HistoricalSlug
park = Park.objects.create(
name='Original Name',
description='A test park',
operator=self.operator,
timezone='America/New_York'
)
original_slug = park.slug
# Change name
park.name = 'New Name'
park.save()
# Check historical slug was created
park_ct = ContentType.objects.get_for_model(park)
historical = HistoricalSlug.objects.filter(
@@ -497,7 +499,7 @@ class ParkSlugHistoryTests(TestCase):
object_id=park.id,
slug=original_slug
).first()
self.assertIsNotNone(historical)
self.assertEqual(historical.slug, original_slug)
@@ -510,32 +512,30 @@ class ParkSlugHistoryTests(TestCase):
operator=self.operator,
timezone='America/New_York'
)
found_park, is_historical = Park.get_by_slug('test-park')
self.assertEqual(found_park, park)
self.assertFalse(is_historical)
def test_get_by_slug_finds_historical_slug(self):
"""Test get_by_slug finds park by historical slug."""
from django.contrib.contenttypes.models import ContentType
from apps.core.history import HistoricalSlug
park = Park.objects.create(
name='Original Name',
description='A test park',
operator=self.operator,
timezone='America/New_York'
)
original_slug = park.slug
# Change name to create historical slug
park.name = 'New Name'
park.save()
# Find by historical slug
found_park, is_historical = Park.get_by_slug(original_slug)
self.assertEqual(found_park, park)
self.assertTrue(is_historical)