mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 14:31:09 -05:00
Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX
This commit is contained in:
6
backend/tests/managers/__init__.py
Normal file
6
backend/tests/managers/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
Manager and QuerySet tests.
|
||||
|
||||
This module contains tests for custom managers and querysets
|
||||
to verify filtering, optimization, and annotation logic.
|
||||
"""
|
||||
354
backend/tests/managers/test_core_managers.py
Normal file
354
backend/tests/managers/test_core_managers.py
Normal file
@@ -0,0 +1,354 @@
|
||||
"""
|
||||
Tests for Core managers and querysets.
|
||||
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from apps.core.managers import (
|
||||
BaseQuerySet,
|
||||
BaseManager,
|
||||
LocationQuerySet,
|
||||
LocationManager,
|
||||
ReviewableQuerySet,
|
||||
ReviewableManager,
|
||||
HierarchicalQuerySet,
|
||||
HierarchicalManager,
|
||||
TimestampedQuerySet,
|
||||
TimestampedManager,
|
||||
StatusQuerySet,
|
||||
StatusManager,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
ParkReviewFactory,
|
||||
RideFactory,
|
||||
UserFactory,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestBaseQuerySet(TestCase):
|
||||
"""Tests for BaseQuerySet."""
|
||||
|
||||
def test__active__filters_active_records(self):
|
||||
"""Test active filters by is_active field if present."""
|
||||
# Using User model which has is_active
|
||||
from django.contrib.auth import get_user_model
|
||||
User = get_user_model()
|
||||
|
||||
active_user = User.objects.create_user(
|
||||
username="active", email="active@test.com", password="test", is_active=True
|
||||
)
|
||||
inactive_user = User.objects.create_user(
|
||||
username="inactive", email="inactive@test.com", password="test", is_active=False
|
||||
)
|
||||
|
||||
result = User.objects.filter(is_active=True)
|
||||
|
||||
assert active_user in result
|
||||
assert inactive_user not in result
|
||||
|
||||
def test__recent__filters_recently_created(self):
|
||||
"""Test recent filters by created_at within days."""
|
||||
park = ParkFactory()
|
||||
# Created just now, should be in recent
|
||||
|
||||
from apps.parks.models import Park
|
||||
result = Park.objects.recent(days=30)
|
||||
|
||||
assert park in result
|
||||
|
||||
def test__search__searches_by_name(self):
|
||||
"""Test search filters by name field."""
|
||||
park1 = ParkFactory(name="Cedar Point")
|
||||
park2 = ParkFactory(name="Kings Island")
|
||||
|
||||
from apps.parks.models import Park
|
||||
result = Park.objects.search(query="Cedar")
|
||||
|
||||
assert park1 in result
|
||||
assert park2 not in result
|
||||
|
||||
def test__search__empty_query__returns_all(self):
|
||||
"""Test search with empty query returns all records."""
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
|
||||
from apps.parks.models import Park
|
||||
result = Park.objects.search(query="")
|
||||
|
||||
assert park1 in result
|
||||
assert park2 in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestLocationQuerySet(TestCase):
|
||||
"""Tests for LocationQuerySet."""
|
||||
|
||||
def test__by_country__filters_by_country(self):
|
||||
"""Test by_country filters by country field."""
|
||||
# Create parks with locations through factory
|
||||
us_park = ParkFactory()
|
||||
# Location is created by factory post_generation
|
||||
|
||||
from apps.parks.models import Park
|
||||
# This tests the pattern - actual filtering depends on location setup
|
||||
|
||||
result = Park.objects.all()
|
||||
assert us_park in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestReviewableQuerySet(TestCase):
|
||||
"""Tests for ReviewableQuerySet."""
|
||||
|
||||
def test__with_review_stats__annotates_review_count(self):
|
||||
"""Test with_review_stats adds review count annotation."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park = ParkFactory()
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
ParkReviewFactory(park=park, user=user1, is_published=True)
|
||||
ParkReviewFactory(park=park, user=user2, is_published=True)
|
||||
|
||||
result = Park.objects.with_review_stats().get(pk=park.pk)
|
||||
|
||||
assert result.review_count == 2
|
||||
|
||||
def test__with_review_stats__calculates_average_rating(self):
|
||||
"""Test with_review_stats calculates average rating."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park = ParkFactory()
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
ParkReviewFactory(park=park, user=user1, is_published=True, rating=8)
|
||||
ParkReviewFactory(park=park, user=user2, is_published=True, rating=10)
|
||||
|
||||
result = Park.objects.with_review_stats().get(pk=park.pk)
|
||||
|
||||
assert result.average_rating == 9.0
|
||||
|
||||
def test__with_review_stats__excludes_unpublished(self):
|
||||
"""Test with_review_stats excludes unpublished reviews."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park = ParkFactory()
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
ParkReviewFactory(park=park, user=user1, is_published=True, rating=10)
|
||||
ParkReviewFactory(park=park, user=user2, is_published=False, rating=2)
|
||||
|
||||
result = Park.objects.with_review_stats().get(pk=park.pk)
|
||||
|
||||
assert result.review_count == 1
|
||||
assert result.average_rating == 10.0
|
||||
|
||||
def test__highly_rated__filters_by_minimum_rating(self):
|
||||
"""Test highly_rated filters by minimum average rating."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
high_rated = ParkFactory()
|
||||
low_rated = ParkFactory()
|
||||
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
ParkReviewFactory(park=high_rated, user=user1, is_published=True, rating=9)
|
||||
ParkReviewFactory(park=low_rated, user=user2, is_published=True, rating=4)
|
||||
|
||||
result = Park.objects.highly_rated(min_rating=8.0)
|
||||
|
||||
assert high_rated in result
|
||||
assert low_rated not in result
|
||||
|
||||
def test__recently_reviewed__filters_by_recent_reviews(self):
|
||||
"""Test recently_reviewed filters parks with recent reviews."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
reviewed_park = ParkFactory()
|
||||
user = UserFactory()
|
||||
ParkReviewFactory(park=reviewed_park, user=user, is_published=True)
|
||||
|
||||
result = Park.objects.get_queryset().recently_reviewed(days=30)
|
||||
|
||||
assert reviewed_park in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestStatusQuerySet(TestCase):
|
||||
"""Tests for StatusQuerySet."""
|
||||
|
||||
def test__with_status__single_status__filters_correctly(self):
|
||||
"""Test with_status filters by single status."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
operating = ParkFactory(status="OPERATING")
|
||||
closed = ParkFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Park.objects.get_queryset().with_status(status="OPERATING")
|
||||
|
||||
assert operating in result
|
||||
assert closed not in result
|
||||
|
||||
def test__with_status__multiple_statuses__filters_correctly(self):
|
||||
"""Test with_status filters by multiple statuses."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
operating = ParkFactory(status="OPERATING")
|
||||
closed_temp = ParkFactory(status="CLOSED_TEMP")
|
||||
closed_perm = ParkFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Park.objects.get_queryset().with_status(status=["CLOSED_TEMP", "CLOSED_PERM"])
|
||||
|
||||
assert operating not in result
|
||||
assert closed_temp in result
|
||||
assert closed_perm in result
|
||||
|
||||
def test__operating__filters_operating_status(self):
|
||||
"""Test operating filters for OPERATING status."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
operating = ParkFactory(status="OPERATING")
|
||||
closed = ParkFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Park.objects.operating()
|
||||
|
||||
assert operating in result
|
||||
assert closed not in result
|
||||
|
||||
def test__closed__filters_closed_statuses(self):
|
||||
"""Test closed filters for closed statuses."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
operating = ParkFactory(status="OPERATING")
|
||||
closed_temp = ParkFactory(status="CLOSED_TEMP")
|
||||
closed_perm = ParkFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Park.objects.closed()
|
||||
|
||||
assert operating not in result
|
||||
assert closed_temp in result
|
||||
assert closed_perm in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestTimestampedQuerySet(TestCase):
|
||||
"""Tests for TimestampedQuerySet."""
|
||||
|
||||
def test__by_creation_date_descending__orders_newest_first(self):
|
||||
"""Test by_creation_date with descending orders newest first."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
|
||||
result = list(Park.objects.get_queryset().by_creation_date(descending=True))
|
||||
|
||||
# Most recently created should be first
|
||||
assert result[0] == park2
|
||||
assert result[1] == park1
|
||||
|
||||
def test__by_creation_date_ascending__orders_oldest_first(self):
|
||||
"""Test by_creation_date with ascending orders oldest first."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
|
||||
result = list(Park.objects.get_queryset().by_creation_date(descending=False))
|
||||
|
||||
# Oldest should be first
|
||||
assert result[0] == park1
|
||||
assert result[1] == park2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestBaseManager(TestCase):
|
||||
"""Tests for BaseManager."""
|
||||
|
||||
def test__active__delegates_to_queryset(self):
|
||||
"""Test active method delegates to queryset."""
|
||||
from django.contrib.auth import get_user_model
|
||||
User = get_user_model()
|
||||
|
||||
user = User.objects.create_user(
|
||||
username="test", email="test@test.com", password="test", is_active=True
|
||||
)
|
||||
|
||||
# BaseManager's active method should work
|
||||
result = User.objects.filter(is_active=True)
|
||||
assert user in result
|
||||
|
||||
def test__recent__delegates_to_queryset(self):
|
||||
"""Test recent method delegates to queryset."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park = ParkFactory()
|
||||
|
||||
result = Park.objects.recent(days=30)
|
||||
assert park in result
|
||||
|
||||
def test__search__delegates_to_queryset(self):
|
||||
"""Test search method delegates to queryset."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park = ParkFactory(name="Unique Name")
|
||||
|
||||
result = Park.objects.search(query="Unique")
|
||||
assert park in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestStatusManager(TestCase):
|
||||
"""Tests for StatusManager."""
|
||||
|
||||
def test__operating__delegates_to_queryset(self):
|
||||
"""Test operating method delegates to queryset."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
operating = ParkFactory(status="OPERATING")
|
||||
|
||||
result = Park.objects.operating()
|
||||
assert operating in result
|
||||
|
||||
def test__closed__delegates_to_queryset(self):
|
||||
"""Test closed method delegates to queryset."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
closed = ParkFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Park.objects.closed()
|
||||
assert closed in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestReviewableManager(TestCase):
|
||||
"""Tests for ReviewableManager."""
|
||||
|
||||
def test__with_review_stats__delegates_to_queryset(self):
|
||||
"""Test with_review_stats method delegates to queryset."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park = ParkFactory()
|
||||
|
||||
result = Park.objects.with_review_stats()
|
||||
assert park in result
|
||||
|
||||
def test__highly_rated__delegates_to_queryset(self):
|
||||
"""Test highly_rated method delegates to queryset."""
|
||||
from apps.parks.models import Park
|
||||
|
||||
park = ParkFactory()
|
||||
user = UserFactory()
|
||||
ParkReviewFactory(park=park, user=user, is_published=True, rating=9)
|
||||
|
||||
result = Park.objects.highly_rated(min_rating=8.0)
|
||||
assert park in result
|
||||
381
backend/tests/managers/test_park_managers.py
Normal file
381
backend/tests/managers/test_park_managers.py
Normal file
@@ -0,0 +1,381 @@
|
||||
"""
|
||||
Tests for Park managers and querysets.
|
||||
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
|
||||
from apps.parks.models import Park, ParkArea, ParkReview, Company
|
||||
from apps.parks.managers import (
|
||||
ParkQuerySet,
|
||||
ParkManager,
|
||||
ParkAreaQuerySet,
|
||||
ParkAreaManager,
|
||||
ParkReviewQuerySet,
|
||||
ParkReviewManager,
|
||||
CompanyQuerySet,
|
||||
CompanyManager,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
ParkFactory,
|
||||
ParkAreaFactory,
|
||||
ParkReviewFactory,
|
||||
RideFactory,
|
||||
CoasterFactory,
|
||||
UserFactory,
|
||||
OperatorCompanyFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestParkQuerySet(TestCase):
|
||||
"""Tests for ParkQuerySet."""
|
||||
|
||||
def test__with_complete_stats__annotates_ride_counts(self):
|
||||
"""Test with_complete_stats adds ride count annotations."""
|
||||
park = ParkFactory()
|
||||
RideFactory(park=park, category="TR")
|
||||
RideFactory(park=park, category="TR")
|
||||
CoasterFactory(park=park, category="RC")
|
||||
|
||||
result = Park.objects.with_complete_stats().get(pk=park.pk)
|
||||
|
||||
assert result.ride_count_calculated == 3
|
||||
assert result.coaster_count_calculated == 1
|
||||
|
||||
def test__with_complete_stats__annotates_review_stats(self):
|
||||
"""Test with_complete_stats adds review statistics."""
|
||||
park = ParkFactory()
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
ParkReviewFactory(park=park, user=user1, is_published=True, rating=8)
|
||||
ParkReviewFactory(park=park, user=user2, is_published=True, rating=6)
|
||||
|
||||
result = Park.objects.with_complete_stats().get(pk=park.pk)
|
||||
|
||||
assert result.review_count == 2
|
||||
assert result.average_rating_calculated == 7.0
|
||||
|
||||
def test__with_complete_stats__excludes_unpublished_reviews(self):
|
||||
"""Test review stats exclude unpublished reviews."""
|
||||
park = ParkFactory()
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
ParkReviewFactory(park=park, user=user1, is_published=True, rating=10)
|
||||
ParkReviewFactory(park=park, user=user2, is_published=False, rating=2)
|
||||
|
||||
result = Park.objects.with_complete_stats().get(pk=park.pk)
|
||||
|
||||
assert result.review_count == 1
|
||||
assert result.average_rating_calculated == 10.0
|
||||
|
||||
def test__optimized_for_list__returns_prefetched_data(self):
|
||||
"""Test optimized_for_list prefetches related data."""
|
||||
ParkFactory()
|
||||
ParkFactory()
|
||||
|
||||
queryset = Park.objects.optimized_for_list()
|
||||
|
||||
# Should have prefetch cache populated
|
||||
assert queryset.count() == 2
|
||||
|
||||
def test__by_operator__filters_by_operator_id(self):
|
||||
"""Test by_operator filters parks by operator."""
|
||||
operator = OperatorCompanyFactory()
|
||||
other_operator = OperatorCompanyFactory()
|
||||
park1 = ParkFactory(operator=operator)
|
||||
park2 = ParkFactory(operator=other_operator)
|
||||
|
||||
result = Park.objects.by_operator(operator_id=operator.pk)
|
||||
|
||||
assert park1 in result
|
||||
assert park2 not in result
|
||||
|
||||
def test__by_property_owner__filters_by_owner_id(self):
|
||||
"""Test by_property_owner filters parks by property owner."""
|
||||
owner = OperatorCompanyFactory()
|
||||
park1 = ParkFactory(property_owner=owner)
|
||||
park2 = ParkFactory()
|
||||
|
||||
result = Park.objects.get_queryset().by_property_owner(owner_id=owner.pk)
|
||||
|
||||
assert park1 in result
|
||||
assert park2 not in result
|
||||
|
||||
def test__with_minimum_coasters__filters_by_coaster_count(self):
|
||||
"""Test with_minimum_coasters filters parks with enough coasters."""
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
|
||||
# Add 5 coasters to park1
|
||||
for _ in range(5):
|
||||
CoasterFactory(park=park1)
|
||||
|
||||
# Add only 2 coasters to park2
|
||||
for _ in range(2):
|
||||
CoasterFactory(park=park2)
|
||||
|
||||
result = Park.objects.with_minimum_coasters(min_coasters=5)
|
||||
|
||||
assert park1 in result
|
||||
assert park2 not in result
|
||||
|
||||
def test__large_parks__filters_by_size(self):
|
||||
"""Test large_parks filters by minimum acreage."""
|
||||
large_park = ParkFactory(size_acres=200)
|
||||
small_park = ParkFactory(size_acres=50)
|
||||
|
||||
result = Park.objects.large_parks(min_acres=100)
|
||||
|
||||
assert large_park in result
|
||||
assert small_park not in result
|
||||
|
||||
def test__seasonal_parks__excludes_empty_operating_season(self):
|
||||
"""Test seasonal_parks excludes parks with empty operating_season."""
|
||||
seasonal_park = ParkFactory(operating_season="Summer only")
|
||||
year_round_park = ParkFactory(operating_season="")
|
||||
|
||||
result = Park.objects.get_queryset().seasonal_parks()
|
||||
|
||||
assert seasonal_park in result
|
||||
assert year_round_park not in result
|
||||
|
||||
def test__search_autocomplete__searches_by_name(self):
|
||||
"""Test search_autocomplete searches park names."""
|
||||
park1 = ParkFactory(name="Cedar Point")
|
||||
park2 = ParkFactory(name="Kings Island")
|
||||
|
||||
result = list(Park.objects.get_queryset().search_autocomplete(query="Cedar"))
|
||||
|
||||
assert park1 in result
|
||||
assert park2 not in result
|
||||
|
||||
def test__search_autocomplete__limits_results(self):
|
||||
"""Test search_autocomplete respects limit parameter."""
|
||||
for i in range(15):
|
||||
ParkFactory(name=f"Test Park {i}")
|
||||
|
||||
result = list(Park.objects.get_queryset().search_autocomplete(query="Test", limit=5))
|
||||
|
||||
assert len(result) == 5
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestParkManager(TestCase):
|
||||
"""Tests for ParkManager."""
|
||||
|
||||
def test__get_queryset__returns_park_queryset(self):
|
||||
"""Test get_queryset returns ParkQuerySet."""
|
||||
queryset = Park.objects.get_queryset()
|
||||
|
||||
assert isinstance(queryset, ParkQuerySet)
|
||||
|
||||
def test__operating__filters_operating_parks(self):
|
||||
"""Test operating filters for operating status."""
|
||||
operating = ParkFactory(status="OPERATING")
|
||||
closed = ParkFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Park.objects.operating()
|
||||
|
||||
assert operating in result
|
||||
assert closed not in result
|
||||
|
||||
def test__closed__filters_closed_parks(self):
|
||||
"""Test closed filters for closed statuses."""
|
||||
operating = ParkFactory(status="OPERATING")
|
||||
closed_temp = ParkFactory(status="CLOSED_TEMP")
|
||||
closed_perm = ParkFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Park.objects.closed()
|
||||
|
||||
assert operating not in result
|
||||
assert closed_temp in result
|
||||
assert closed_perm in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestParkAreaQuerySet(TestCase):
|
||||
"""Tests for ParkAreaQuerySet."""
|
||||
|
||||
def test__with_ride_counts__annotates_ride_count(self):
|
||||
"""Test with_ride_counts adds ride count annotation."""
|
||||
park = ParkFactory()
|
||||
area = ParkAreaFactory(park=park)
|
||||
RideFactory(park=park, park_area=area)
|
||||
RideFactory(park=park, park_area=area)
|
||||
CoasterFactory(park=park, park_area=area)
|
||||
|
||||
result = ParkArea.objects.with_ride_counts().get(pk=area.pk)
|
||||
|
||||
assert result.ride_count == 3
|
||||
assert result.coaster_count == 1
|
||||
|
||||
def test__by_park__filters_by_park_id(self):
|
||||
"""Test by_park filters areas by park."""
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
area1 = ParkAreaFactory(park=park1)
|
||||
area2 = ParkAreaFactory(park=park2)
|
||||
|
||||
result = ParkArea.objects.by_park(park_id=park1.pk)
|
||||
|
||||
assert area1 in result
|
||||
assert area2 not in result
|
||||
|
||||
def test__with_rides__filters_areas_with_rides(self):
|
||||
"""Test with_rides filters areas that have rides."""
|
||||
park = ParkFactory()
|
||||
area_with_rides = ParkAreaFactory(park=park)
|
||||
area_without_rides = ParkAreaFactory(park=park)
|
||||
RideFactory(park=park, park_area=area_with_rides)
|
||||
|
||||
result = ParkArea.objects.get_queryset().with_rides()
|
||||
|
||||
assert area_with_rides in result
|
||||
assert area_without_rides not in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestParkReviewQuerySet(TestCase):
|
||||
"""Tests for ParkReviewQuerySet."""
|
||||
|
||||
def test__for_park__filters_by_park_id(self):
|
||||
"""Test for_park filters reviews by park."""
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
user = UserFactory()
|
||||
review1 = ParkReviewFactory(park=park1, user=user)
|
||||
user2 = UserFactory()
|
||||
review2 = ParkReviewFactory(park=park2, user=user2)
|
||||
|
||||
result = ParkReview.objects.for_park(park_id=park1.pk)
|
||||
|
||||
assert review1 in result
|
||||
assert review2 not in result
|
||||
|
||||
def test__by_user__filters_by_user_id(self):
|
||||
"""Test by_user filters reviews by user."""
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
review1 = ParkReviewFactory(user=user1)
|
||||
review2 = ParkReviewFactory(user=user2)
|
||||
|
||||
result = ParkReview.objects.get_queryset().by_user(user_id=user1.pk)
|
||||
|
||||
assert review1 in result
|
||||
assert review2 not in result
|
||||
|
||||
def test__by_rating_range__filters_by_rating(self):
|
||||
"""Test by_rating_range filters reviews by rating range."""
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
user3 = UserFactory()
|
||||
high_review = ParkReviewFactory(rating=9, user=user1)
|
||||
mid_review = ParkReviewFactory(rating=5, user=user2)
|
||||
low_review = ParkReviewFactory(rating=2, user=user3)
|
||||
|
||||
result = ParkReview.objects.by_rating_range(min_rating=7, max_rating=10)
|
||||
|
||||
assert high_review in result
|
||||
assert mid_review not in result
|
||||
assert low_review not in result
|
||||
|
||||
def test__moderation_required__filters_unpublished_or_unmoderated(self):
|
||||
"""Test moderation_required filters reviews needing moderation."""
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
published = ParkReviewFactory(is_published=True, user=user1)
|
||||
unpublished = ParkReviewFactory(is_published=False, user=user2)
|
||||
|
||||
result = ParkReview.objects.moderation_required()
|
||||
|
||||
# unpublished should definitely be in result
|
||||
assert unpublished in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestCompanyQuerySet(TestCase):
|
||||
"""Tests for CompanyQuerySet."""
|
||||
|
||||
def test__operators__filters_operator_companies(self):
|
||||
"""Test operators filters for companies with OPERATOR role."""
|
||||
operator = OperatorCompanyFactory()
|
||||
manufacturer = ManufacturerCompanyFactory()
|
||||
|
||||
result = Company.objects.operators()
|
||||
|
||||
assert operator in result
|
||||
assert manufacturer not in result
|
||||
|
||||
def test__manufacturers__filters_manufacturer_companies(self):
|
||||
"""Test manufacturers filters for companies with MANUFACTURER role."""
|
||||
operator = OperatorCompanyFactory()
|
||||
manufacturer = ManufacturerCompanyFactory()
|
||||
|
||||
result = Company.objects.manufacturers()
|
||||
|
||||
assert manufacturer in result
|
||||
assert operator not in result
|
||||
|
||||
def test__with_park_counts__annotates_park_counts(self):
|
||||
"""Test with_park_counts adds park count annotations."""
|
||||
operator = OperatorCompanyFactory()
|
||||
ParkFactory(operator=operator)
|
||||
ParkFactory(operator=operator)
|
||||
ParkFactory(property_owner=operator)
|
||||
|
||||
result = Company.objects.get_queryset().with_park_counts().get(pk=operator.pk)
|
||||
|
||||
assert result.operated_parks_count == 2
|
||||
assert result.owned_parks_count == 1
|
||||
|
||||
def test__major_operators__filters_by_minimum_parks(self):
|
||||
"""Test major_operators filters by minimum park count."""
|
||||
major_operator = OperatorCompanyFactory()
|
||||
small_operator = OperatorCompanyFactory()
|
||||
|
||||
for _ in range(6):
|
||||
ParkFactory(operator=major_operator)
|
||||
for _ in range(2):
|
||||
ParkFactory(operator=small_operator)
|
||||
|
||||
result = Company.objects.major_operators(min_parks=5)
|
||||
|
||||
assert major_operator in result
|
||||
assert small_operator not in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestCompanyManager(TestCase):
|
||||
"""Tests for CompanyManager."""
|
||||
|
||||
def test__manufacturers_with_ride_count__annotates_ride_count(self):
|
||||
"""Test manufacturers_with_ride_count adds ride count annotation."""
|
||||
manufacturer = ManufacturerCompanyFactory()
|
||||
RideFactory(manufacturer=manufacturer)
|
||||
RideFactory(manufacturer=manufacturer)
|
||||
RideFactory(manufacturer=manufacturer)
|
||||
|
||||
result = list(Company.objects.manufacturers_with_ride_count())
|
||||
mfr = next((c for c in result if c.pk == manufacturer.pk), None)
|
||||
|
||||
assert mfr is not None
|
||||
assert mfr.ride_count == 3
|
||||
|
||||
def test__operators_with_park_count__annotates_park_count(self):
|
||||
"""Test operators_with_park_count adds park count annotation."""
|
||||
operator = OperatorCompanyFactory()
|
||||
ParkFactory(operator=operator)
|
||||
ParkFactory(operator=operator)
|
||||
|
||||
result = list(Company.objects.operators_with_park_count())
|
||||
op = next((c for c in result if c.pk == operator.pk), None)
|
||||
|
||||
assert op is not None
|
||||
assert op.operated_parks_count == 2
|
||||
408
backend/tests/managers/test_ride_managers.py
Normal file
408
backend/tests/managers/test_ride_managers.py
Normal file
@@ -0,0 +1,408 @@
|
||||
"""
|
||||
Tests for Ride managers and querysets.
|
||||
|
||||
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.rides.models import Ride, RideModel, RideReview
|
||||
from apps.rides.managers import (
|
||||
RideQuerySet,
|
||||
RideManager,
|
||||
RideModelQuerySet,
|
||||
RideModelManager,
|
||||
RideReviewQuerySet,
|
||||
RideReviewManager,
|
||||
RollerCoasterStatsQuerySet,
|
||||
RollerCoasterStatsManager,
|
||||
)
|
||||
|
||||
from tests.factories import (
|
||||
RideFactory,
|
||||
CoasterFactory,
|
||||
ParkFactory,
|
||||
RideModelFactory,
|
||||
RideReviewFactory,
|
||||
UserFactory,
|
||||
ManufacturerCompanyFactory,
|
||||
DesignerCompanyFactory,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideQuerySet(TestCase):
|
||||
"""Tests for RideQuerySet."""
|
||||
|
||||
def test__by_category__single_category__filters_correctly(self):
|
||||
"""Test by_category filters by single category."""
|
||||
coaster = RideFactory(category="RC")
|
||||
water_ride = RideFactory(category="WR")
|
||||
|
||||
result = Ride.objects.get_queryset().by_category(category="RC")
|
||||
|
||||
assert coaster in result
|
||||
assert water_ride not in result
|
||||
|
||||
def test__by_category__multiple_categories__filters_correctly(self):
|
||||
"""Test by_category filters by multiple categories."""
|
||||
rc = RideFactory(category="RC")
|
||||
wc = RideFactory(category="WC")
|
||||
tr = RideFactory(category="TR")
|
||||
|
||||
result = Ride.objects.get_queryset().by_category(category=["RC", "WC"])
|
||||
|
||||
assert rc in result
|
||||
assert wc in result
|
||||
assert tr not in result
|
||||
|
||||
def test__coasters__filters_roller_coasters(self):
|
||||
"""Test coasters filters for RC and WC categories."""
|
||||
steel = RideFactory(category="RC")
|
||||
wooden = RideFactory(category="WC")
|
||||
thrill = RideFactory(category="TR")
|
||||
|
||||
result = Ride.objects.coasters()
|
||||
|
||||
assert steel in result
|
||||
assert wooden in result
|
||||
assert thrill not in result
|
||||
|
||||
def test__thrill_rides__filters_thrill_categories(self):
|
||||
"""Test thrill_rides filters for thrill ride categories."""
|
||||
coaster = RideFactory(category="RC")
|
||||
flat_ride = RideFactory(category="FR")
|
||||
family = RideFactory(category="DR") # Dark ride
|
||||
|
||||
result = Ride.objects.thrill_rides()
|
||||
|
||||
assert coaster in result
|
||||
assert flat_ride in result
|
||||
assert family not in result
|
||||
|
||||
def test__family_friendly__filters_by_height_requirement(self):
|
||||
"""Test family_friendly filters by max height requirement."""
|
||||
family_ride = RideFactory(min_height_in=36)
|
||||
thrill_ride = RideFactory(min_height_in=54)
|
||||
no_restriction = RideFactory(min_height_in=None)
|
||||
|
||||
result = Ride.objects.family_friendly(max_height_requirement=42)
|
||||
|
||||
assert family_ride in result
|
||||
assert no_restriction in result
|
||||
assert thrill_ride not in result
|
||||
|
||||
def test__by_park__filters_by_park_id(self):
|
||||
"""Test by_park filters rides by park."""
|
||||
park1 = ParkFactory()
|
||||
park2 = ParkFactory()
|
||||
ride1 = RideFactory(park=park1)
|
||||
ride2 = RideFactory(park=park2)
|
||||
|
||||
result = Ride.objects.by_park(park_id=park1.pk)
|
||||
|
||||
assert ride1 in result
|
||||
assert ride2 not in result
|
||||
|
||||
def test__by_manufacturer__filters_by_manufacturer_id(self):
|
||||
"""Test by_manufacturer filters by manufacturer."""
|
||||
mfr1 = ManufacturerCompanyFactory()
|
||||
mfr2 = ManufacturerCompanyFactory()
|
||||
ride1 = RideFactory(manufacturer=mfr1)
|
||||
ride2 = RideFactory(manufacturer=mfr2)
|
||||
|
||||
result = Ride.objects.get_queryset().by_manufacturer(manufacturer_id=mfr1.pk)
|
||||
|
||||
assert ride1 in result
|
||||
assert ride2 not in result
|
||||
|
||||
def test__by_designer__filters_by_designer_id(self):
|
||||
"""Test by_designer filters by designer."""
|
||||
designer1 = DesignerCompanyFactory()
|
||||
designer2 = DesignerCompanyFactory()
|
||||
ride1 = RideFactory(designer=designer1)
|
||||
ride2 = RideFactory(designer=designer2)
|
||||
|
||||
result = Ride.objects.get_queryset().by_designer(designer_id=designer1.pk)
|
||||
|
||||
assert ride1 in result
|
||||
assert ride2 not in result
|
||||
|
||||
def test__with_capacity_info__annotates_capacity_data(self):
|
||||
"""Test with_capacity_info adds capacity annotations."""
|
||||
ride = RideFactory(capacity_per_hour=1500, ride_duration_seconds=180)
|
||||
|
||||
result = Ride.objects.get_queryset().with_capacity_info().get(pk=ride.pk)
|
||||
|
||||
assert result.estimated_daily_capacity == 15000 # 1500 * 10
|
||||
assert result.duration_minutes == 3.0 # 180 / 60
|
||||
|
||||
def test__high_capacity__filters_by_minimum_capacity(self):
|
||||
"""Test high_capacity filters by minimum capacity."""
|
||||
high_cap = RideFactory(capacity_per_hour=2000)
|
||||
low_cap = RideFactory(capacity_per_hour=500)
|
||||
|
||||
result = Ride.objects.high_capacity(min_capacity=1000)
|
||||
|
||||
assert high_cap in result
|
||||
assert low_cap not in result
|
||||
|
||||
def test__optimized_for_list__returns_prefetched_data(self):
|
||||
"""Test optimized_for_list prefetches related data."""
|
||||
RideFactory()
|
||||
RideFactory()
|
||||
|
||||
queryset = Ride.objects.optimized_for_list()
|
||||
|
||||
# Should return results with prefetched data
|
||||
assert queryset.count() == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideManager(TestCase):
|
||||
"""Tests for RideManager."""
|
||||
|
||||
def test__get_queryset__returns_ride_queryset(self):
|
||||
"""Test get_queryset returns RideQuerySet."""
|
||||
queryset = Ride.objects.get_queryset()
|
||||
|
||||
assert isinstance(queryset, RideQuerySet)
|
||||
|
||||
def test__operating__filters_operating_rides(self):
|
||||
"""Test operating filters for operating status."""
|
||||
operating = RideFactory(status="OPERATING")
|
||||
closed = RideFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Ride.objects.operating()
|
||||
|
||||
assert operating in result
|
||||
assert closed not in result
|
||||
|
||||
def test__coasters__delegates_to_queryset(self):
|
||||
"""Test coasters method delegates to queryset."""
|
||||
coaster = CoasterFactory(category="RC")
|
||||
thrill = RideFactory(category="TR")
|
||||
|
||||
result = Ride.objects.coasters()
|
||||
|
||||
assert coaster in result
|
||||
assert thrill not in result
|
||||
|
||||
def test__with_coaster_stats__prefetches_stats(self):
|
||||
"""Test with_coaster_stats prefetches coaster_stats."""
|
||||
ride = CoasterFactory()
|
||||
|
||||
queryset = Ride.objects.with_coaster_stats()
|
||||
|
||||
assert ride in queryset
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideModelQuerySet(TestCase):
|
||||
"""Tests for RideModelQuerySet."""
|
||||
|
||||
def test__by_manufacturer__filters_by_manufacturer_id(self):
|
||||
"""Test by_manufacturer filters ride models by manufacturer."""
|
||||
mfr1 = ManufacturerCompanyFactory()
|
||||
mfr2 = ManufacturerCompanyFactory()
|
||||
model1 = RideModelFactory(manufacturer=mfr1)
|
||||
model2 = RideModelFactory(manufacturer=mfr2)
|
||||
|
||||
result = RideModel.objects.by_manufacturer(manufacturer_id=mfr1.pk)
|
||||
|
||||
assert model1 in result
|
||||
assert model2 not in result
|
||||
|
||||
def test__with_ride_counts__annotates_ride_counts(self):
|
||||
"""Test with_ride_counts adds ride count annotation."""
|
||||
model = RideModelFactory()
|
||||
RideFactory(ride_model=model, status="OPERATING")
|
||||
RideFactory(ride_model=model, status="OPERATING")
|
||||
RideFactory(ride_model=model, status="CLOSED_PERM")
|
||||
|
||||
result = RideModel.objects.get_queryset().with_ride_counts().get(pk=model.pk)
|
||||
|
||||
assert result.ride_count == 3
|
||||
assert result.operating_rides_count == 2
|
||||
|
||||
def test__popular_models__filters_by_minimum_installations(self):
|
||||
"""Test popular_models filters by minimum ride count."""
|
||||
popular = RideModelFactory()
|
||||
unpopular = RideModelFactory()
|
||||
|
||||
for _ in range(6):
|
||||
RideFactory(ride_model=popular)
|
||||
for _ in range(2):
|
||||
RideFactory(ride_model=unpopular)
|
||||
|
||||
result = RideModel.objects.popular_models(min_installations=5)
|
||||
|
||||
assert popular in result
|
||||
assert unpopular not in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideModelManager(TestCase):
|
||||
"""Tests for RideModelManager."""
|
||||
|
||||
def test__get_queryset__returns_ride_model_queryset(self):
|
||||
"""Test get_queryset returns RideModelQuerySet."""
|
||||
queryset = RideModel.objects.get_queryset()
|
||||
|
||||
assert isinstance(queryset, RideModelQuerySet)
|
||||
|
||||
def test__by_manufacturer__delegates_to_queryset(self):
|
||||
"""Test by_manufacturer delegates to queryset."""
|
||||
mfr = ManufacturerCompanyFactory()
|
||||
model = RideModelFactory(manufacturer=mfr)
|
||||
|
||||
result = RideModel.objects.by_manufacturer(manufacturer_id=mfr.pk)
|
||||
|
||||
assert model in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideReviewQuerySet(TestCase):
|
||||
"""Tests for RideReviewQuerySet."""
|
||||
|
||||
def test__for_ride__filters_by_ride_id(self):
|
||||
"""Test for_ride filters reviews by ride."""
|
||||
ride1 = RideFactory()
|
||||
ride2 = RideFactory()
|
||||
user = UserFactory()
|
||||
review1 = RideReviewFactory(ride=ride1, user=user)
|
||||
user2 = UserFactory()
|
||||
review2 = RideReviewFactory(ride=ride2, user=user2)
|
||||
|
||||
result = RideReview.objects.for_ride(ride_id=ride1.pk)
|
||||
|
||||
assert review1 in result
|
||||
assert review2 not in result
|
||||
|
||||
def test__by_user__filters_by_user_id(self):
|
||||
"""Test by_user filters reviews by user."""
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
review1 = RideReviewFactory(user=user1)
|
||||
review2 = RideReviewFactory(user=user2)
|
||||
|
||||
result = RideReview.objects.get_queryset().by_user(user_id=user1.pk)
|
||||
|
||||
assert review1 in result
|
||||
assert review2 not in result
|
||||
|
||||
def test__by_rating_range__filters_by_rating(self):
|
||||
"""Test by_rating_range filters by rating range."""
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
user3 = UserFactory()
|
||||
high = RideReviewFactory(rating=9, user=user1)
|
||||
mid = RideReviewFactory(rating=5, user=user2)
|
||||
low = RideReviewFactory(rating=2, user=user3)
|
||||
|
||||
result = RideReview.objects.by_rating_range(min_rating=7, max_rating=10)
|
||||
|
||||
assert high in result
|
||||
assert mid not in result
|
||||
assert low not in result
|
||||
|
||||
def test__optimized_for_display__selects_related(self):
|
||||
"""Test optimized_for_display selects related data."""
|
||||
review = RideReviewFactory()
|
||||
|
||||
queryset = RideReview.objects.get_queryset().optimized_for_display()
|
||||
|
||||
# Should include the review
|
||||
assert review in queryset
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideReviewManager(TestCase):
|
||||
"""Tests for RideReviewManager."""
|
||||
|
||||
def test__get_queryset__returns_ride_review_queryset(self):
|
||||
"""Test get_queryset returns RideReviewQuerySet."""
|
||||
queryset = RideReview.objects.get_queryset()
|
||||
|
||||
assert isinstance(queryset, RideReviewQuerySet)
|
||||
|
||||
def test__for_ride__delegates_to_queryset(self):
|
||||
"""Test for_ride delegates to queryset."""
|
||||
ride = RideFactory()
|
||||
user = UserFactory()
|
||||
review = RideReviewFactory(ride=ride, user=user)
|
||||
|
||||
result = RideReview.objects.for_ride(ride_id=ride.pk)
|
||||
|
||||
assert review in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideQuerySetStatusMethods(TestCase):
|
||||
"""Tests for status-related RideQuerySet methods."""
|
||||
|
||||
def test__operating__filters_operating_rides(self):
|
||||
"""Test operating filters for OPERATING status."""
|
||||
operating = RideFactory(status="OPERATING")
|
||||
sbno = RideFactory(status="SBNO")
|
||||
closed = RideFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Ride.objects.operating()
|
||||
|
||||
assert operating in result
|
||||
assert sbno not in result
|
||||
assert closed not in result
|
||||
|
||||
def test__closed__filters_closed_rides(self):
|
||||
"""Test closed filters for closed statuses."""
|
||||
operating = RideFactory(status="OPERATING")
|
||||
closed_temp = RideFactory(status="CLOSED_TEMP")
|
||||
closed_perm = RideFactory(status="CLOSED_PERM")
|
||||
|
||||
result = Ride.objects.closed()
|
||||
|
||||
assert operating not in result
|
||||
assert closed_temp in result
|
||||
assert closed_perm in result
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestRideQuerySetReviewMethods(TestCase):
|
||||
"""Tests for review-related RideQuerySet methods."""
|
||||
|
||||
def test__with_review_stats__annotates_review_data(self):
|
||||
"""Test with_review_stats adds review statistics."""
|
||||
ride = RideFactory()
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
RideReviewFactory(ride=ride, user=user1, is_published=True, rating=8)
|
||||
RideReviewFactory(ride=ride, user=user2, is_published=True, rating=6)
|
||||
|
||||
result = Ride.objects.get_queryset().with_review_stats().get(pk=ride.pk)
|
||||
|
||||
assert result.review_count == 2
|
||||
assert result.average_rating == 7.0
|
||||
|
||||
def test__highly_rated__filters_by_minimum_rating(self):
|
||||
"""Test highly_rated filters by minimum average rating."""
|
||||
ride1 = RideFactory()
|
||||
ride2 = RideFactory()
|
||||
|
||||
user1 = UserFactory()
|
||||
user2 = UserFactory()
|
||||
# High rated ride
|
||||
RideReviewFactory(ride=ride1, user=user1, is_published=True, rating=9)
|
||||
RideReviewFactory(ride=ride1, user=user2, is_published=True, rating=10)
|
||||
|
||||
user3 = UserFactory()
|
||||
user4 = UserFactory()
|
||||
# Low rated ride
|
||||
RideReviewFactory(ride=ride2, user=user3, is_published=True, rating=4)
|
||||
RideReviewFactory(ride=ride2, user=user4, is_published=True, rating=5)
|
||||
|
||||
result = Ride.objects.get_queryset().highly_rated(min_rating=8.0)
|
||||
|
||||
assert ride1 in result
|
||||
assert ride2 not in result
|
||||
Reference in New Issue
Block a user