""" Tests for Ride serializers. Following Django styleguide pattern: test______ """ import pytest from unittest.mock import Mock, MagicMock from django.test import TestCase from apps.api.v1.rides.serializers import ( RidePhotoOutputSerializer, RidePhotoCreateInputSerializer, RidePhotoUpdateInputSerializer, RidePhotoListOutputSerializer, RidePhotoApprovalInputSerializer, RidePhotoStatsOutputSerializer, RidePhotoTypeFilterSerializer, RidePhotoSerializer, HybridRideSerializer, RideSerializer, ) from tests.factories import ( RideFactory, RidePhotoFactory, ParkFactory, UserFactory, CloudflareImageFactory, ManufacturerCompanyFactory, DesignerCompanyFactory, ) @pytest.mark.django_db class TestRidePhotoOutputSerializer(TestCase): """Tests for RidePhotoOutputSerializer.""" def test__serialize__valid_photo__returns_all_fields(self): """Test serializing a ride photo returns all expected fields.""" user = UserFactory() ride = RideFactory() image = CloudflareImageFactory() photo = RidePhotoFactory( ride=ride, uploaded_by=user, image=image, caption="Test caption", alt_text="Test alt text", is_primary=True, is_approved=True, ) serializer = RidePhotoOutputSerializer(photo) data = serializer.data assert "id" in data assert data["caption"] == "Test caption" assert data["alt_text"] == "Test alt text" assert data["is_primary"] is True assert data["is_approved"] is True assert data["uploaded_by_username"] == user.username assert data["ride_slug"] == ride.slug assert data["ride_name"] == ride.name assert data["park_slug"] == ride.park.slug assert data["park_name"] == ride.park.name def test__serialize__photo_with_image__returns_image_url(self): """Test serializing a photo with image returns URL.""" photo = RidePhotoFactory() serializer = RidePhotoOutputSerializer(photo) data = serializer.data assert "image_url" in data assert "image_variants" in data def test__serialize__photo_without_image__returns_none_for_image_fields(self): """Test serializing photo without image returns None for image fields.""" photo = RidePhotoFactory() photo.image = None photo.save() serializer = RidePhotoOutputSerializer(photo) data = serializer.data assert data["image_url"] is None assert data["image_variants"] == {} def test__get_image_variants__photo_with_image__returns_variant_urls(self): """Test get_image_variants returns all variant URLs.""" image = CloudflareImageFactory() photo = RidePhotoFactory(image=image) serializer = RidePhotoOutputSerializer(photo) data = serializer.data if photo.image: variants = data["image_variants"] assert "thumbnail" in variants assert "medium" in variants assert "large" in variants assert "public" in variants def test__serialize__includes_photo_type(self): """Test serializing includes photo_type field.""" photo = RidePhotoFactory() serializer = RidePhotoOutputSerializer(photo) data = serializer.data assert "photo_type" in data @pytest.mark.django_db class TestRidePhotoCreateInputSerializer(TestCase): """Tests for RidePhotoCreateInputSerializer.""" def test__serialize__valid_data__returns_expected_fields(self): """Test serializing valid create data.""" image = CloudflareImageFactory() data = { "image": image.pk, "caption": "New photo caption", "alt_text": "Description of the image", "photo_type": "exterior", "is_primary": False, } serializer = RidePhotoCreateInputSerializer(data=data) assert serializer.is_valid(), serializer.errors assert "caption" in serializer.validated_data assert "alt_text" in serializer.validated_data assert "photo_type" in serializer.validated_data assert "is_primary" in serializer.validated_data def test__validate__missing_required_fields__returns_error(self): """Test validation fails with missing required fields.""" data = {} serializer = RidePhotoCreateInputSerializer(data=data) assert not serializer.is_valid() def test__meta__fields__includes_photo_type(self): """Test Meta.fields includes photo_type for ride photos.""" expected_fields = ["image", "caption", "alt_text", "photo_type", "is_primary"] assert list(RidePhotoCreateInputSerializer.Meta.fields) == expected_fields @pytest.mark.django_db class TestRidePhotoUpdateInputSerializer(TestCase): """Tests for RidePhotoUpdateInputSerializer.""" def test__serialize__valid_data__returns_expected_fields(self): """Test serializing valid update data.""" data = { "caption": "Updated caption", "alt_text": "Updated alt text", "photo_type": "queue", "is_primary": True, } serializer = RidePhotoUpdateInputSerializer(data=data) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["caption"] == "Updated caption" assert serializer.validated_data["photo_type"] == "queue" def test__serialize__partial_update__validates_partial_data(self): """Test partial update with only some fields.""" photo = RidePhotoFactory() data = {"caption": "Only caption updated"} serializer = RidePhotoUpdateInputSerializer(photo, data=data, partial=True) assert serializer.is_valid(), serializer.errors def test__meta__fields__includes_photo_type(self): """Test Meta.fields includes photo_type for updates.""" expected_fields = ["caption", "alt_text", "photo_type", "is_primary"] assert list(RidePhotoUpdateInputSerializer.Meta.fields) == expected_fields @pytest.mark.django_db class TestRidePhotoListOutputSerializer(TestCase): """Tests for RidePhotoListOutputSerializer.""" def test__serialize__photo__returns_list_fields_only(self): """Test serializing returns only list-appropriate fields.""" user = UserFactory() photo = RidePhotoFactory(uploaded_by=user) serializer = RidePhotoListOutputSerializer(photo) data = serializer.data assert "id" in data assert "image" in data assert "caption" in data assert "photo_type" in data assert "is_primary" in data assert "is_approved" in data assert "created_at" in data assert "uploaded_by_username" in data # Should NOT include detailed fields assert "image_variants" not in data assert "file_size" not in data assert "dimensions" not in data def test__serialize__multiple_photos__returns_list(self): """Test serializing multiple photos returns a list.""" photos = [RidePhotoFactory() for _ in range(3)] serializer = RidePhotoListOutputSerializer(photos, many=True) assert len(serializer.data) == 3 def test__meta__all_fields_read_only(self): """Test all fields are read-only for list serializer.""" assert ( RidePhotoListOutputSerializer.Meta.read_only_fields == RidePhotoListOutputSerializer.Meta.fields ) class TestRidePhotoApprovalInputSerializer(TestCase): """Tests for RidePhotoApprovalInputSerializer.""" def test__validate__valid_photo_ids__returns_validated_data(self): """Test validation with valid photo IDs.""" data = { "photo_ids": [1, 2, 3], "approve": True, } serializer = RidePhotoApprovalInputSerializer(data=data) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["photo_ids"] == [1, 2, 3] assert serializer.validated_data["approve"] is True def test__validate__approve_default__defaults_to_true(self): """Test approve field defaults to True.""" data = {"photo_ids": [1, 2]} serializer = RidePhotoApprovalInputSerializer(data=data) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["approve"] is True def test__validate__empty_photo_ids__is_valid(self): """Test empty photo_ids list is valid.""" data = {"photo_ids": [], "approve": False} serializer = RidePhotoApprovalInputSerializer(data=data) assert serializer.is_valid(), serializer.errors def test__validate__missing_photo_ids__returns_error(self): """Test validation fails without photo_ids.""" data = {"approve": True} serializer = RidePhotoApprovalInputSerializer(data=data) assert not serializer.is_valid() assert "photo_ids" in serializer.errors class TestRidePhotoStatsOutputSerializer(TestCase): """Tests for RidePhotoStatsOutputSerializer.""" def test__serialize__stats_dict__returns_all_fields(self): """Test serializing stats dictionary.""" stats = { "total_photos": 50, "approved_photos": 40, "pending_photos": 10, "has_primary": True, "recent_uploads": 3, "by_type": {"exterior": 20, "queue": 10, "onride": 10, "other": 10}, } serializer = RidePhotoStatsOutputSerializer(data=stats) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["total_photos"] == 50 assert serializer.validated_data["by_type"]["exterior"] == 20 def test__validate__includes_by_type_field(self): """Test stats include by_type breakdown.""" stats = { "total_photos": 10, "approved_photos": 8, "pending_photos": 2, "has_primary": False, "recent_uploads": 1, "by_type": {"exterior": 10}, } serializer = RidePhotoStatsOutputSerializer(data=stats) assert serializer.is_valid(), serializer.errors assert "by_type" in serializer.validated_data class TestRidePhotoTypeFilterSerializer(TestCase): """Tests for RidePhotoTypeFilterSerializer.""" def test__validate__valid_photo_type__returns_validated_data(self): """Test validation with valid photo type.""" data = {"photo_type": "exterior"} serializer = RidePhotoTypeFilterSerializer(data=data) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["photo_type"] == "exterior" def test__validate__all_photo_types__are_valid(self): """Test all defined photo types are valid.""" valid_types = ["exterior", "queue", "station", "onride", "construction", "other"] for photo_type in valid_types: serializer = RidePhotoTypeFilterSerializer(data={"photo_type": photo_type}) assert serializer.is_valid(), f"Photo type {photo_type} should be valid" def test__validate__invalid_photo_type__returns_error(self): """Test invalid photo type returns error.""" data = {"photo_type": "invalid_type"} serializer = RidePhotoTypeFilterSerializer(data=data) assert not serializer.is_valid() assert "photo_type" in serializer.errors def test__validate__empty_photo_type__is_valid(self): """Test empty/missing photo_type is valid (optional field).""" data = {} serializer = RidePhotoTypeFilterSerializer(data=data) assert serializer.is_valid(), serializer.errors @pytest.mark.django_db class TestHybridRideSerializer(TestCase): """Tests for HybridRideSerializer.""" def test__serialize__ride_with_all_fields__returns_complete_data(self): """Test serializing ride with all fields populated.""" ride = RideFactory() serializer = HybridRideSerializer(ride) data = serializer.data assert "id" in data assert "name" in data assert "slug" in data assert "category" in data assert "status" in data assert "park_name" in data assert "park_slug" in data assert "manufacturer_name" in data def test__serialize__ride_with_manufacturer__returns_manufacturer_fields(self): """Test serializing includes manufacturer information.""" manufacturer = ManufacturerCompanyFactory(name="Test Manufacturer") ride = RideFactory(manufacturer=manufacturer) serializer = HybridRideSerializer(ride) data = serializer.data assert data["manufacturer_name"] == "Test Manufacturer" assert "manufacturer_slug" in data def test__serialize__ride_with_designer__returns_designer_fields(self): """Test serializing includes designer information.""" designer = DesignerCompanyFactory(name="Test Designer") ride = RideFactory(designer=designer) serializer = HybridRideSerializer(ride) data = serializer.data assert data["designer_name"] == "Test Designer" assert "designer_slug" in data def test__get_park_city__ride_with_park_location__returns_city(self): """Test get_park_city returns city from park location.""" ride = RideFactory() mock_location = Mock() mock_location.city = "Orlando" mock_location.state = "FL" mock_location.country = "USA" ride.park.location = mock_location serializer = HybridRideSerializer(ride) assert serializer.get_park_city(ride) == "Orlando" def test__get_park_city__ride_without_park_location__returns_none(self): """Test get_park_city returns None when no location.""" ride = RideFactory() ride.park.location = None serializer = HybridRideSerializer(ride) assert serializer.get_park_city(ride) is None def test__get_coaster_height_ft__ride_with_stats__returns_height(self): """Test get_coaster_height_ft returns height from coaster stats.""" ride = RideFactory() mock_stats = Mock() mock_stats.height_ft = 205.5 mock_stats.length_ft = 5000 mock_stats.speed_mph = 70 mock_stats.inversions = 4 ride.coaster_stats = mock_stats serializer = HybridRideSerializer(ride) assert serializer.get_coaster_height_ft(ride) == 205.5 def test__get_coaster_inversions__ride_with_stats__returns_inversions(self): """Test get_coaster_inversions returns inversions count.""" ride = RideFactory() mock_stats = Mock() mock_stats.inversions = 7 ride.coaster_stats = mock_stats serializer = HybridRideSerializer(ride) assert serializer.get_coaster_inversions(ride) == 7 def test__get_coaster_height_ft__ride_without_stats__returns_none(self): """Test coaster stat methods return None when no stats.""" ride = RideFactory() ride.coaster_stats = None serializer = HybridRideSerializer(ride) assert serializer.get_coaster_height_ft(ride) is None assert serializer.get_coaster_length_ft(ride) is None assert serializer.get_coaster_speed_mph(ride) is None assert serializer.get_coaster_inversions(ride) is None def test__get_banner_image_url__ride_with_banner__returns_url(self): """Test get_banner_image_url returns URL when banner exists.""" ride = RideFactory() mock_image = Mock() mock_image.url = "https://example.com/ride-banner.jpg" mock_banner = Mock() mock_banner.image = mock_image ride.banner_image = mock_banner serializer = HybridRideSerializer(ride) assert serializer.get_banner_image_url(ride) == "https://example.com/ride-banner.jpg" def test__get_banner_image_url__ride_without_banner__returns_none(self): """Test get_banner_image_url returns None when no banner.""" ride = RideFactory() ride.banner_image = None serializer = HybridRideSerializer(ride) assert serializer.get_banner_image_url(ride) is None def test__meta__all_fields_read_only(self): """Test all fields in HybridRideSerializer are read-only.""" assert ( HybridRideSerializer.Meta.read_only_fields == HybridRideSerializer.Meta.fields ) def test__serialize__includes_ride_model_fields(self): """Test serializing includes ride model information.""" ride = RideFactory() serializer = HybridRideSerializer(ride) data = serializer.data assert "ride_model_name" in data assert "ride_model_slug" in data assert "ride_model_category" in data @pytest.mark.django_db class TestRideSerializer(TestCase): """Tests for RideSerializer (legacy).""" def test__serialize__ride__returns_basic_fields(self): """Test serializing ride returns basic fields.""" ride = RideFactory() serializer = RideSerializer(ride) data = serializer.data assert "id" in data assert "name" in data assert "slug" in data assert "category" in data assert "status" in data assert "opening_date" in data def test__serialize__multiple_rides__returns_list(self): """Test serializing multiple rides returns a list.""" rides = [RideFactory() for _ in range(3)] serializer = RideSerializer(rides, many=True) assert len(serializer.data) == 3 def test__meta__fields__matches_expected(self): """Test Meta.fields matches expected field list.""" expected_fields = [ "id", "name", "slug", "park", "manufacturer", "designer", "category", "status", "opening_date", "closing_date", ] assert list(RideSerializer.Meta.fields) == expected_fields @pytest.mark.django_db class TestRidePhotoSerializer(TestCase): """Tests for legacy RidePhotoSerializer.""" def test__serialize__photo__returns_legacy_fields(self): """Test serializing photo returns legacy field set.""" photo = RidePhotoFactory() serializer = RidePhotoSerializer(photo) data = serializer.data assert "id" in data assert "image" in data assert "caption" in data assert "alt_text" in data assert "is_primary" in data assert "photo_type" in data def test__meta__fields__matches_legacy_format(self): """Test Meta.fields matches legacy format.""" expected_fields = [ "id", "image", "caption", "alt_text", "is_primary", "photo_type", "uploaded_at", "uploaded_by", ] assert list(RidePhotoSerializer.Meta.fields) == expected_fields