mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-01-01 22:07:03 -05:00
834 lines
30 KiB
Python
834 lines
30 KiB
Python
"""
|
|
Comprehensive tests for the rides app state machine.
|
|
|
|
This module contains tests for:
|
|
- Ride status FSM transitions
|
|
- Ride transition wrapper methods
|
|
- Post-closing status automation
|
|
- Transition history logging
|
|
- Related model updates during transitions
|
|
"""
|
|
|
|
from datetime import date, timedelta
|
|
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.core.exceptions import ValidationError
|
|
from django.test import TestCase
|
|
from django_fsm import TransitionNotAllowed
|
|
|
|
from apps.parks.models import Company as ParkCompany
|
|
from apps.parks.models import Park
|
|
|
|
from .models import Company, Ride
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
# ============================================================================
|
|
# Ride FSM Transition Tests
|
|
# ============================================================================
|
|
|
|
|
|
class RideTransitionTests(TestCase):
|
|
"""Comprehensive tests for Ride FSM transitions."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.user = User.objects.create_user(
|
|
username="testuser", email="test@example.com", password="testpass123", role="USER"
|
|
)
|
|
self.moderator = User.objects.create_user(
|
|
username="moderator", email="moderator@example.com", password="testpass123", role="MODERATOR"
|
|
)
|
|
self.admin = User.objects.create_user(
|
|
username="admin", email="admin@example.com", password="testpass123", role="ADMIN"
|
|
)
|
|
|
|
# Create operator and park
|
|
self.operator = ParkCompany.objects.create(
|
|
name="Test Operator", description="Test operator company", roles=["OPERATOR"]
|
|
)
|
|
self.park = Park.objects.create(
|
|
name="Test Park",
|
|
slug="test-park",
|
|
description="A test park",
|
|
operator=self.operator,
|
|
timezone="America/New_York",
|
|
)
|
|
|
|
# Create manufacturer
|
|
self.manufacturer = Company.objects.create(
|
|
name="Test Manufacturer", description="Test manufacturer company", roles=["MANUFACTURER"]
|
|
)
|
|
|
|
def _create_ride(self, status="OPERATING", **kwargs):
|
|
"""Helper to create a Ride with specified status."""
|
|
defaults = {
|
|
"name": "Test Ride",
|
|
"slug": "test-ride",
|
|
"description": "A test ride",
|
|
"park": self.park,
|
|
"manufacturer": self.manufacturer,
|
|
}
|
|
defaults.update(kwargs)
|
|
return Ride.objects.create(status=status, **defaults)
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Operating status transitions
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_operating_to_closed_temp_transition(self):
|
|
"""Test transition from OPERATING to CLOSED_TEMP."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
self.assertEqual(ride.status, "OPERATING")
|
|
|
|
ride.transition_to_closed_temp(user=self.user)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSED_TEMP")
|
|
|
|
def test_operating_to_sbno_transition(self):
|
|
"""Test transition from OPERATING to SBNO."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
ride.transition_to_sbno(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "SBNO")
|
|
|
|
def test_operating_to_closing_transition(self):
|
|
"""Test transition from OPERATING to CLOSING."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
ride.transition_to_closing(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSING")
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Under construction transitions
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_under_construction_to_operating_transition(self):
|
|
"""Test transition from UNDER_CONSTRUCTION to OPERATING."""
|
|
ride = self._create_ride(status="UNDER_CONSTRUCTION")
|
|
self.assertEqual(ride.status, "UNDER_CONSTRUCTION")
|
|
|
|
ride.transition_to_operating(user=self.user)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "OPERATING")
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Closed temp transitions
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_closed_temp_to_operating_transition(self):
|
|
"""Test transition from CLOSED_TEMP to OPERATING (reopen)."""
|
|
ride = self._create_ride(status="CLOSED_TEMP")
|
|
|
|
ride.transition_to_operating(user=self.user)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "OPERATING")
|
|
|
|
def test_closed_temp_to_sbno_transition(self):
|
|
"""Test transition from CLOSED_TEMP to SBNO."""
|
|
ride = self._create_ride(status="CLOSED_TEMP")
|
|
|
|
ride.transition_to_sbno(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "SBNO")
|
|
|
|
def test_closed_temp_to_closed_perm_transition(self):
|
|
"""Test transition from CLOSED_TEMP to CLOSED_PERM."""
|
|
ride = self._create_ride(status="CLOSED_TEMP")
|
|
|
|
ride.transition_to_closed_perm(user=self.moderator)
|
|
ride.closing_date = date.today()
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSED_PERM")
|
|
|
|
# -------------------------------------------------------------------------
|
|
# SBNO transitions
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_sbno_to_operating_transition(self):
|
|
"""Test transition from SBNO to OPERATING (revival)."""
|
|
ride = self._create_ride(status="SBNO")
|
|
|
|
ride.transition_to_operating(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "OPERATING")
|
|
|
|
def test_sbno_to_closed_perm_transition(self):
|
|
"""Test transition from SBNO to CLOSED_PERM."""
|
|
ride = self._create_ride(status="SBNO")
|
|
|
|
ride.transition_to_closed_perm(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSED_PERM")
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Closing transitions
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_closing_to_closed_perm_transition(self):
|
|
"""Test transition from CLOSING to CLOSED_PERM."""
|
|
ride = self._create_ride(status="CLOSING")
|
|
|
|
ride.transition_to_closed_perm(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSED_PERM")
|
|
|
|
def test_closing_to_sbno_transition(self):
|
|
"""Test transition from CLOSING to SBNO."""
|
|
ride = self._create_ride(status="CLOSING")
|
|
|
|
ride.transition_to_sbno(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "SBNO")
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Closed perm transitions (to final states)
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_closed_perm_to_demolished_transition(self):
|
|
"""Test transition from CLOSED_PERM to DEMOLISHED."""
|
|
ride = self._create_ride(status="CLOSED_PERM")
|
|
|
|
ride.transition_to_demolished(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "DEMOLISHED")
|
|
|
|
def test_closed_perm_to_relocated_transition(self):
|
|
"""Test transition from CLOSED_PERM to RELOCATED."""
|
|
ride = self._create_ride(status="CLOSED_PERM")
|
|
|
|
ride.transition_to_relocated(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "RELOCATED")
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Invalid transitions (final states)
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_demolished_cannot_transition(self):
|
|
"""Test that DEMOLISHED state cannot transition further."""
|
|
ride = self._create_ride(status="DEMOLISHED")
|
|
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
ride.transition_to_operating(user=self.moderator)
|
|
|
|
def test_relocated_cannot_transition(self):
|
|
"""Test that RELOCATED state cannot transition further."""
|
|
ride = self._create_ride(status="RELOCATED")
|
|
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
ride.transition_to_operating(user=self.moderator)
|
|
|
|
def test_operating_cannot_directly_demolish(self):
|
|
"""Test that OPERATING cannot directly transition to DEMOLISHED."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
ride.transition_to_demolished(user=self.moderator)
|
|
|
|
def test_operating_cannot_directly_relocate(self):
|
|
"""Test that OPERATING cannot directly transition to RELOCATED."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
with self.assertRaises(TransitionNotAllowed):
|
|
ride.transition_to_relocated(user=self.moderator)
|
|
|
|
# -------------------------------------------------------------------------
|
|
# Wrapper method tests
|
|
# -------------------------------------------------------------------------
|
|
|
|
def test_open_wrapper_method(self):
|
|
"""Test the open() wrapper method."""
|
|
ride = self._create_ride(status="CLOSED_TEMP")
|
|
|
|
ride.open(user=self.user)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "OPERATING")
|
|
|
|
def test_close_temporarily_wrapper_method(self):
|
|
"""Test the close_temporarily() wrapper method."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
ride.close_temporarily(user=self.user)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSED_TEMP")
|
|
|
|
def test_mark_sbno_wrapper_method(self):
|
|
"""Test the mark_sbno() wrapper method."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
ride.mark_sbno(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "SBNO")
|
|
|
|
def test_mark_closing_wrapper_method(self):
|
|
"""Test the mark_closing() wrapper method."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
closing = date(2025, 12, 31)
|
|
|
|
ride.mark_closing(closing_date=closing, post_closing_status="DEMOLISHED", user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSING")
|
|
self.assertEqual(ride.closing_date, closing)
|
|
self.assertEqual(ride.post_closing_status, "DEMOLISHED")
|
|
|
|
def test_mark_closing_requires_post_closing_status(self):
|
|
"""Test that mark_closing() requires post_closing_status."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
with self.assertRaises(ValidationError):
|
|
ride.mark_closing(closing_date=date(2025, 12, 31), post_closing_status="", user=self.moderator)
|
|
|
|
def test_close_permanently_wrapper_method(self):
|
|
"""Test the close_permanently() wrapper method."""
|
|
ride = self._create_ride(status="SBNO")
|
|
|
|
ride.close_permanently(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSED_PERM")
|
|
|
|
def test_demolish_wrapper_method(self):
|
|
"""Test the demolish() wrapper method."""
|
|
ride = self._create_ride(status="CLOSED_PERM")
|
|
|
|
ride.demolish(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "DEMOLISHED")
|
|
|
|
def test_relocate_wrapper_method(self):
|
|
"""Test the relocate() wrapper method."""
|
|
ride = self._create_ride(status="CLOSED_PERM")
|
|
|
|
ride.relocate(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "RELOCATED")
|
|
|
|
|
|
# ============================================================================
|
|
# Ride Post-Closing Status Tests
|
|
# ============================================================================
|
|
|
|
|
|
class RidePostClosingTests(TestCase):
|
|
"""Tests for Ride post_closing_status automation."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.moderator = User.objects.create_user(
|
|
username="moderator", email="moderator@example.com", password="testpass123", role="MODERATOR"
|
|
)
|
|
self.operator = ParkCompany.objects.create(
|
|
name="Test Operator", description="Test operator company", roles=["OPERATOR"]
|
|
)
|
|
self.park = Park.objects.create(
|
|
name="Test Park",
|
|
slug="test-park",
|
|
description="A test park",
|
|
operator=self.operator,
|
|
timezone="America/New_York",
|
|
)
|
|
self.manufacturer = Company.objects.create(
|
|
name="Test Manufacturer", description="Test manufacturer company", roles=["MANUFACTURER"]
|
|
)
|
|
|
|
def _create_ride(self, status="OPERATING", **kwargs):
|
|
"""Helper to create a Ride."""
|
|
defaults = {
|
|
"name": "Test Ride",
|
|
"slug": "test-ride",
|
|
"description": "A test ride",
|
|
"park": self.park,
|
|
"manufacturer": self.manufacturer,
|
|
}
|
|
defaults.update(kwargs)
|
|
return Ride.objects.create(status=status, **defaults)
|
|
|
|
def test_apply_post_closing_status_to_demolished(self):
|
|
"""Test apply_post_closing_status transitions to DEMOLISHED."""
|
|
yesterday = date.today() - timedelta(days=1)
|
|
ride = self._create_ride(status="CLOSING", closing_date=yesterday, post_closing_status="DEMOLISHED")
|
|
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "DEMOLISHED")
|
|
|
|
def test_apply_post_closing_status_to_relocated(self):
|
|
"""Test apply_post_closing_status transitions to RELOCATED."""
|
|
yesterday = date.today() - timedelta(days=1)
|
|
ride = self._create_ride(status="CLOSING", closing_date=yesterday, post_closing_status="RELOCATED")
|
|
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "RELOCATED")
|
|
|
|
def test_apply_post_closing_status_to_sbno(self):
|
|
"""Test apply_post_closing_status transitions to SBNO."""
|
|
yesterday = date.today() - timedelta(days=1)
|
|
ride = self._create_ride(status="CLOSING", closing_date=yesterday, post_closing_status="SBNO")
|
|
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "SBNO")
|
|
|
|
def test_apply_post_closing_status_to_closed_perm(self):
|
|
"""Test apply_post_closing_status transitions to CLOSED_PERM."""
|
|
yesterday = date.today() - timedelta(days=1)
|
|
ride = self._create_ride(status="CLOSING", closing_date=yesterday, post_closing_status="CLOSED_PERM")
|
|
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.status, "CLOSED_PERM")
|
|
|
|
def test_apply_post_closing_status_not_yet_reached(self):
|
|
"""Test apply_post_closing_status does nothing if date not reached."""
|
|
tomorrow = date.today() + timedelta(days=1)
|
|
ride = self._create_ride(status="CLOSING", closing_date=tomorrow, post_closing_status="DEMOLISHED")
|
|
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
ride.refresh_from_db()
|
|
# Status should remain CLOSING since date hasn't been reached
|
|
self.assertEqual(ride.status, "CLOSING")
|
|
|
|
def test_apply_post_closing_status_requires_closing_status(self):
|
|
"""Test apply_post_closing_status requires CLOSING status."""
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
with self.assertRaises(ValidationError) as ctx:
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
self.assertIn("CLOSING", str(ctx.exception))
|
|
|
|
def test_apply_post_closing_status_requires_closing_date(self):
|
|
"""Test apply_post_closing_status requires closing_date."""
|
|
ride = self._create_ride(status="CLOSING", post_closing_status="DEMOLISHED")
|
|
ride.closing_date = None
|
|
ride.save()
|
|
|
|
with self.assertRaises(ValidationError) as ctx:
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
self.assertIn("closing_date", str(ctx.exception))
|
|
|
|
def test_apply_post_closing_status_requires_post_closing_status(self):
|
|
"""Test apply_post_closing_status requires post_closing_status."""
|
|
yesterday = date.today() - timedelta(days=1)
|
|
ride = self._create_ride(status="CLOSING", closing_date=yesterday)
|
|
ride.post_closing_status = None
|
|
ride.save()
|
|
|
|
with self.assertRaises(ValidationError) as ctx:
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
self.assertIn("post_closing_status", str(ctx.exception))
|
|
|
|
|
|
# ============================================================================
|
|
# Ride Transition History Tests
|
|
# ============================================================================
|
|
|
|
|
|
class RideTransitionHistoryTests(TestCase):
|
|
"""Tests for Ride transition history logging."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.moderator = User.objects.create_user(
|
|
username="moderator", email="moderator@example.com", password="testpass123", role="MODERATOR"
|
|
)
|
|
self.operator = ParkCompany.objects.create(
|
|
name="Test Operator", description="Test operator company", roles=["OPERATOR"]
|
|
)
|
|
self.park = Park.objects.create(
|
|
name="Test Park",
|
|
slug="test-park",
|
|
description="A test park",
|
|
operator=self.operator,
|
|
timezone="America/New_York",
|
|
)
|
|
self.manufacturer = Company.objects.create(
|
|
name="Test Manufacturer", description="Test manufacturer company", roles=["MANUFACTURER"]
|
|
)
|
|
|
|
def _create_ride(self, status="OPERATING"):
|
|
"""Helper to create a Ride."""
|
|
return Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park,
|
|
manufacturer=self.manufacturer,
|
|
status=status,
|
|
)
|
|
|
|
def test_transition_creates_state_log(self):
|
|
"""Test that transitions create StateLog entries."""
|
|
from django_fsm_log.models import StateLog
|
|
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
ride.transition_to_closed_temp(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride_ct = ContentType.objects.get_for_model(ride)
|
|
log = StateLog.objects.filter(content_type=ride_ct, object_id=ride.id).first()
|
|
|
|
self.assertIsNotNone(log)
|
|
self.assertEqual(log.state, "CLOSED_TEMP")
|
|
self.assertEqual(log.by, self.moderator)
|
|
|
|
def test_multiple_transitions_create_multiple_logs(self):
|
|
"""Test that multiple transitions create multiple log entries."""
|
|
from django_fsm_log.models import StateLog
|
|
|
|
ride = self._create_ride(status="OPERATING")
|
|
ride_ct = ContentType.objects.get_for_model(ride)
|
|
|
|
# First transition
|
|
ride.transition_to_closed_temp(user=self.moderator)
|
|
ride.save()
|
|
|
|
# Second transition
|
|
ride.transition_to_operating(user=self.moderator)
|
|
ride.save()
|
|
|
|
logs = StateLog.objects.filter(content_type=ride_ct, object_id=ride.id).order_by("timestamp")
|
|
|
|
self.assertEqual(logs.count(), 2)
|
|
self.assertEqual(logs[0].state, "CLOSED_TEMP")
|
|
self.assertEqual(logs[1].state, "OPERATING")
|
|
|
|
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
|
|
|
|
ride = self._create_ride(status="OPERATING")
|
|
|
|
ride.transition_to_sbno(user=self.moderator)
|
|
ride.save()
|
|
|
|
ride_ct = ContentType.objects.get_for_model(ride)
|
|
log = StateLog.objects.filter(content_type=ride_ct, object_id=ride.id).first()
|
|
|
|
self.assertEqual(log.by, self.moderator)
|
|
|
|
def test_post_closing_transition_logged(self):
|
|
"""Test that post_closing_status transitions are logged."""
|
|
from django_fsm_log.models import StateLog
|
|
|
|
yesterday = date.today() - timedelta(days=1)
|
|
ride = self._create_ride(status="CLOSING")
|
|
ride.closing_date = yesterday
|
|
ride.post_closing_status = "DEMOLISHED"
|
|
ride.save()
|
|
|
|
ride.apply_post_closing_status(user=self.moderator)
|
|
|
|
ride_ct = ContentType.objects.get_for_model(ride)
|
|
log = StateLog.objects.filter(content_type=ride_ct, object_id=ride.id, state="DEMOLISHED").first()
|
|
|
|
self.assertIsNotNone(log)
|
|
self.assertEqual(log.by, self.moderator)
|
|
|
|
|
|
# ============================================================================
|
|
# Ride Model Business Logic Tests
|
|
# ============================================================================
|
|
|
|
|
|
class RideBusinessLogicTests(TestCase):
|
|
"""Tests for Ride model business logic."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.operator = ParkCompany.objects.create(
|
|
name="Test Operator", description="Test operator company", roles=["OPERATOR"]
|
|
)
|
|
self.park = Park.objects.create(
|
|
name="Test Park",
|
|
slug="test-park",
|
|
description="A test park",
|
|
operator=self.operator,
|
|
timezone="America/New_York",
|
|
)
|
|
self.manufacturer = Company.objects.create(
|
|
name="Test Manufacturer", description="Test manufacturer company", roles=["MANUFACTURER"]
|
|
)
|
|
|
|
def test_ride_creates_with_valid_park(self):
|
|
"""Test ride can be created with valid park."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
self.assertEqual(ride.park, self.park)
|
|
|
|
def test_ride_slug_auto_generated(self):
|
|
"""Test that ride slug is auto-generated from name."""
|
|
ride = Ride.objects.create(
|
|
name="My Amazing Roller Coaster", description="A test ride", park=self.park, manufacturer=self.manufacturer
|
|
)
|
|
|
|
self.assertEqual(ride.slug, "my-amazing-roller-coaster")
|
|
|
|
def test_ride_url_generated(self):
|
|
"""Test that frontend URL is generated on save."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
self.assertIn("test-park", ride.url)
|
|
self.assertIn("test-ride", ride.url)
|
|
|
|
def test_opening_year_computed_from_opening_date(self):
|
|
"""Test that opening_year is computed from opening_date."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park,
|
|
manufacturer=self.manufacturer,
|
|
opening_date=date(2020, 6, 15),
|
|
)
|
|
|
|
self.assertEqual(ride.opening_year, 2020)
|
|
|
|
def test_search_text_populated(self):
|
|
"""Test that search_text is populated on save."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A thrilling roller coaster",
|
|
park=self.park,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
self.assertIn("test ride", ride.search_text)
|
|
self.assertIn("thrilling roller coaster", ride.search_text)
|
|
self.assertIn("test park", ride.search_text)
|
|
self.assertIn("test manufacturer", ride.search_text)
|
|
|
|
def test_ride_slug_unique_within_park(self):
|
|
"""Test that ride slugs are unique within a park."""
|
|
Ride.objects.create(
|
|
name="Test Ride", slug="test-ride", description="First ride", park=self.park, manufacturer=self.manufacturer
|
|
)
|
|
|
|
# Creating another ride with same name should get different slug
|
|
ride2 = Ride.objects.create(
|
|
name="Test Ride", description="Second ride", park=self.park, manufacturer=self.manufacturer
|
|
)
|
|
|
|
self.assertNotEqual(ride2.slug, "test-ride")
|
|
self.assertTrue(ride2.slug.startswith("test-ride"))
|
|
|
|
|
|
# ============================================================================
|
|
# Ride Move to Park Tests
|
|
# ============================================================================
|
|
|
|
|
|
class RideMoveTests(TestCase):
|
|
"""Tests for moving rides between parks."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.operator = ParkCompany.objects.create(
|
|
name="Test Operator", description="Test operator company", roles=["OPERATOR"]
|
|
)
|
|
self.park1 = Park.objects.create(
|
|
name="Park One",
|
|
slug="park-one",
|
|
description="First park",
|
|
operator=self.operator,
|
|
timezone="America/New_York",
|
|
)
|
|
self.park2 = Park.objects.create(
|
|
name="Park Two",
|
|
slug="park-two",
|
|
description="Second park",
|
|
operator=self.operator,
|
|
timezone="America/Los_Angeles",
|
|
)
|
|
self.manufacturer = Company.objects.create(
|
|
name="Test Manufacturer", description="Test manufacturer company", roles=["MANUFACTURER"]
|
|
)
|
|
|
|
def test_move_ride_to_different_park(self):
|
|
"""Test moving a ride to a different park."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park1,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
changes = ride.move_to_park(self.park2)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertEqual(ride.park, self.park2)
|
|
self.assertEqual(changes["old_park"]["id"], self.park1.id)
|
|
self.assertEqual(changes["new_park"]["id"], self.park2.id)
|
|
|
|
def test_move_ride_updates_url(self):
|
|
"""Test that moving a ride updates the URL."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park1,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
old_url = ride.url
|
|
|
|
changes = ride.move_to_park(self.park2)
|
|
|
|
ride.refresh_from_db()
|
|
self.assertNotEqual(ride.url, old_url)
|
|
self.assertIn("park-two", ride.url)
|
|
self.assertTrue(changes["url_changed"])
|
|
|
|
def test_move_ride_handles_slug_conflict(self):
|
|
"""Test that moving a ride handles slug conflicts in destination park."""
|
|
# Create ride in park1
|
|
ride1 = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park1,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
# Create ride with same slug in park2
|
|
Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="Another test ride",
|
|
park=self.park2,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
# Move ride1 to park2
|
|
changes = ride1.move_to_park(self.park2)
|
|
|
|
ride1.refresh_from_db()
|
|
self.assertEqual(ride1.park, self.park2)
|
|
# Slug should have been modified to avoid conflict
|
|
self.assertNotEqual(ride1.slug, "test-ride")
|
|
self.assertTrue(changes["slug_changed"])
|
|
|
|
|
|
# ============================================================================
|
|
# Ride Historical Slug Tests
|
|
# ============================================================================
|
|
|
|
|
|
class RideSlugHistoryTests(TestCase):
|
|
"""Tests for Ride historical slug tracking."""
|
|
|
|
def setUp(self):
|
|
"""Set up test fixtures."""
|
|
self.operator = ParkCompany.objects.create(
|
|
name="Test Operator", description="Test operator company", roles=["OPERATOR"]
|
|
)
|
|
self.park = Park.objects.create(
|
|
name="Test Park",
|
|
slug="test-park",
|
|
description="A test park",
|
|
operator=self.operator,
|
|
timezone="America/New_York",
|
|
)
|
|
self.manufacturer = Company.objects.create(
|
|
name="Test Manufacturer", description="Test manufacturer company", roles=["MANUFACTURER"]
|
|
)
|
|
|
|
def test_get_by_slug_finds_current_slug(self):
|
|
"""Test get_by_slug finds ride by current slug."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
found_ride, is_historical = Ride.get_by_slug("test-ride", park=self.park)
|
|
|
|
self.assertEqual(found_ride, ride)
|
|
self.assertFalse(is_historical)
|
|
|
|
def test_get_by_slug_with_park_filter(self):
|
|
"""Test get_by_slug filters by park."""
|
|
ride = Ride.objects.create(
|
|
name="Test Ride",
|
|
slug="test-ride",
|
|
description="A test ride",
|
|
park=self.park,
|
|
manufacturer=self.manufacturer,
|
|
)
|
|
|
|
# Should find ride in correct park
|
|
found_ride, is_historical = Ride.get_by_slug("test-ride", park=self.park)
|
|
self.assertEqual(found_ride, ride)
|
|
|
|
# Should not find ride in different park
|
|
other_park = Park.objects.create(
|
|
name="Other Park",
|
|
slug="other-park",
|
|
description="Another park",
|
|
operator=self.operator,
|
|
timezone="America/New_York",
|
|
)
|
|
with self.assertRaises(Ride.DoesNotExist):
|
|
Ride.get_by_slug("test-ride", park=other_park)
|