""" Tests for Park serializers. Following Django styleguide pattern: test______ """ import pytest from unittest.mock import Mock, MagicMock from django.test import TestCase from apps.api.v1.parks.serializers import ( ParkPhotoOutputSerializer, ParkPhotoCreateInputSerializer, ParkPhotoUpdateInputSerializer, ParkPhotoListOutputSerializer, ParkPhotoApprovalInputSerializer, ParkPhotoStatsOutputSerializer, ParkPhotoSerializer, HybridParkSerializer, ParkSerializer, ) from tests.factories import ( ParkFactory, ParkPhotoFactory, UserFactory, CloudflareImageFactory, ) @pytest.mark.django_db class TestParkPhotoOutputSerializer(TestCase): """Tests for ParkPhotoOutputSerializer.""" def test__serialize__valid_photo__returns_all_fields(self): """Test serializing a park photo returns all expected fields.""" user = UserFactory() park = ParkFactory() image = CloudflareImageFactory() photo = ParkPhotoFactory( park=park, uploaded_by=user, image=image, caption="Test caption", alt_text="Test alt text", is_primary=True, is_approved=True, ) serializer = ParkPhotoOutputSerializer(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["park_slug"] == park.slug assert data["park_name"] == park.name def test__serialize__photo_with_image__returns_image_url(self): """Test serializing a photo with image returns URL.""" photo = ParkPhotoFactory() serializer = ParkPhotoOutputSerializer(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 = ParkPhotoFactory() photo.image = None photo.save() serializer = ParkPhotoOutputSerializer(photo) data = serializer.data assert data["image_url"] is None assert data["image_variants"] == {} def test__get_file_size__photo_with_image__returns_file_size(self): """Test get_file_size method returns file size.""" photo = ParkPhotoFactory() serializer = ParkPhotoOutputSerializer(photo) # file_size comes from the model property assert "file_size" in serializer.data def test__get_dimensions__photo_with_image__returns_dimensions(self): """Test get_dimensions method returns [width, height].""" photo = ParkPhotoFactory() serializer = ParkPhotoOutputSerializer(photo) assert "dimensions" in serializer.data def test__get_image_variants__photo_with_image__returns_variant_urls(self): """Test get_image_variants returns all variant URLs.""" image = CloudflareImageFactory() photo = ParkPhotoFactory(image=image) serializer = ParkPhotoOutputSerializer(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 @pytest.mark.django_db class TestParkPhotoCreateInputSerializer(TestCase): """Tests for ParkPhotoCreateInputSerializer.""" 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", "is_primary": False, } serializer = ParkPhotoCreateInputSerializer(data=data) assert serializer.is_valid(), serializer.errors assert "caption" in serializer.validated_data assert "alt_text" 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 = ParkPhotoCreateInputSerializer(data=data) # image is required since it's not in read_only_fields assert not serializer.is_valid() def test__meta__fields__includes_expected_fields(self): """Test Meta.fields includes the expected input fields.""" expected_fields = ["image", "caption", "alt_text", "is_primary"] assert list(ParkPhotoCreateInputSerializer.Meta.fields) == expected_fields @pytest.mark.django_db class TestParkPhotoUpdateInputSerializer(TestCase): """Tests for ParkPhotoUpdateInputSerializer.""" def test__serialize__valid_data__returns_expected_fields(self): """Test serializing valid update data.""" data = { "caption": "Updated caption", "alt_text": "Updated alt text", "is_primary": True, } serializer = ParkPhotoUpdateInputSerializer(data=data) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["caption"] == "Updated caption" assert serializer.validated_data["alt_text"] == "Updated alt text" assert serializer.validated_data["is_primary"] is True def test__serialize__partial_update__validates_partial_data(self): """Test partial update with only some fields.""" photo = ParkPhotoFactory() data = {"caption": "Only caption updated"} serializer = ParkPhotoUpdateInputSerializer(photo, data=data, partial=True) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["caption"] == "Only caption updated" def test__meta__fields__excludes_image_field(self): """Test Meta.fields excludes image field for updates.""" expected_fields = ["caption", "alt_text", "is_primary"] assert list(ParkPhotoUpdateInputSerializer.Meta.fields) == expected_fields @pytest.mark.django_db class TestParkPhotoListOutputSerializer(TestCase): """Tests for ParkPhotoListOutputSerializer.""" def test__serialize__photo__returns_list_fields_only(self): """Test serializing returns only list-appropriate fields.""" user = UserFactory() photo = ParkPhotoFactory(uploaded_by=user) serializer = ParkPhotoListOutputSerializer(photo) data = serializer.data assert "id" in data assert "image" in data assert "caption" 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 = [ParkPhotoFactory() for _ in range(3)] serializer = ParkPhotoListOutputSerializer(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 ( ParkPhotoListOutputSerializer.Meta.read_only_fields == ParkPhotoListOutputSerializer.Meta.fields ) class TestParkPhotoApprovalInputSerializer(TestCase): """Tests for ParkPhotoApprovalInputSerializer.""" 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 = ParkPhotoApprovalInputSerializer(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 = ParkPhotoApprovalInputSerializer(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 = ParkPhotoApprovalInputSerializer(data=data) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["photo_ids"] == [] def test__validate__missing_photo_ids__returns_error(self): """Test validation fails without photo_ids.""" data = {"approve": True} serializer = ParkPhotoApprovalInputSerializer(data=data) assert not serializer.is_valid() assert "photo_ids" in serializer.errors def test__validate__invalid_photo_ids__returns_error(self): """Test validation fails with non-integer photo IDs.""" data = {"photo_ids": ["invalid", "ids"]} serializer = ParkPhotoApprovalInputSerializer(data=data) assert not serializer.is_valid() class TestParkPhotoStatsOutputSerializer(TestCase): """Tests for ParkPhotoStatsOutputSerializer.""" def test__serialize__stats_dict__returns_all_fields(self): """Test serializing stats dictionary.""" stats = { "total_photos": 100, "approved_photos": 80, "pending_photos": 20, "has_primary": True, "recent_uploads": 5, } serializer = ParkPhotoStatsOutputSerializer(data=stats) assert serializer.is_valid(), serializer.errors assert serializer.validated_data["total_photos"] == 100 assert serializer.validated_data["approved_photos"] == 80 assert serializer.validated_data["pending_photos"] == 20 assert serializer.validated_data["has_primary"] is True assert serializer.validated_data["recent_uploads"] == 5 def test__validate__missing_fields__returns_error(self): """Test validation fails with missing stats fields.""" stats = {"total_photos": 100} # Missing other required fields serializer = ParkPhotoStatsOutputSerializer(data=stats) assert not serializer.is_valid() @pytest.mark.django_db class TestHybridParkSerializer(TestCase): """Tests for HybridParkSerializer.""" def test__serialize__park_with_all_fields__returns_complete_data(self): """Test serializing park with all fields populated.""" park = ParkFactory() serializer = HybridParkSerializer(park) data = serializer.data assert "id" in data assert "name" in data assert "slug" in data assert "status" in data assert "operator_name" in data def test__serialize__park_without_location__returns_null_location_fields(self): """Test serializing park without location returns null for location fields.""" park = ParkFactory() # Remove location if it exists if hasattr(park, 'location') and park.location: park.location.delete() serializer = HybridParkSerializer(park) data = serializer.data # Location fields should be None when no location assert "city" in data assert "state" in data assert "country" in data def test__get_city__park_with_location__returns_city(self): """Test get_city returns city from location.""" park = ParkFactory() # Create a mock location mock_location = Mock() mock_location.city = "Orlando" mock_location.state = "FL" mock_location.country = "USA" mock_location.continent = "North America" mock_location.coordinates = [-81.3792, 28.5383] # [lon, lat] park.location = mock_location serializer = HybridParkSerializer(park) assert serializer.get_city(park) == "Orlando" def test__get_latitude__park_with_coordinates__returns_latitude(self): """Test get_latitude returns correct value from coordinates.""" park = ParkFactory() mock_location = Mock() mock_location.coordinates = [-81.3792, 28.5383] # [lon, lat] park.location = mock_location serializer = HybridParkSerializer(park) # Latitude is index 1 in PostGIS [lon, lat] format assert serializer.get_latitude(park) == 28.5383 def test__get_longitude__park_with_coordinates__returns_longitude(self): """Test get_longitude returns correct value from coordinates.""" park = ParkFactory() mock_location = Mock() mock_location.coordinates = [-81.3792, 28.5383] # [lon, lat] park.location = mock_location serializer = HybridParkSerializer(park) # Longitude is index 0 in PostGIS [lon, lat] format assert serializer.get_longitude(park) == -81.3792 def test__get_banner_image_url__park_with_banner__returns_url(self): """Test get_banner_image_url returns URL when banner exists.""" park = ParkFactory() mock_image = Mock() mock_image.url = "https://example.com/banner.jpg" mock_banner = Mock() mock_banner.image = mock_image park.banner_image = mock_banner serializer = HybridParkSerializer(park) assert serializer.get_banner_image_url(park) == "https://example.com/banner.jpg" def test__get_banner_image_url__park_without_banner__returns_none(self): """Test get_banner_image_url returns None when no banner.""" park = ParkFactory() park.banner_image = None serializer = HybridParkSerializer(park) assert serializer.get_banner_image_url(park) is None def test__meta__all_fields_read_only(self): """Test all fields in HybridParkSerializer are read-only.""" assert ( HybridParkSerializer.Meta.read_only_fields == HybridParkSerializer.Meta.fields ) @pytest.mark.django_db class TestParkSerializer(TestCase): """Tests for ParkSerializer (legacy).""" def test__serialize__park__returns_basic_fields(self): """Test serializing park returns basic fields.""" park = ParkFactory() serializer = ParkSerializer(park) data = serializer.data assert "id" in data assert "name" in data assert "slug" in data assert "status" in data assert "website" in data def test__serialize__multiple_parks__returns_list(self): """Test serializing multiple parks returns a list.""" parks = [ParkFactory() for _ in range(3)] serializer = ParkSerializer(parks, many=True) assert len(serializer.data) == 3 @pytest.mark.django_db class TestParkPhotoSerializer(TestCase): """Tests for legacy ParkPhotoSerializer.""" def test__serialize__photo__returns_legacy_fields(self): """Test serializing photo returns legacy field set.""" photo = ParkPhotoFactory() serializer = ParkPhotoSerializer(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 def test__meta__fields__matches_legacy_format(self): """Test Meta.fields matches legacy format.""" expected_fields = ( "id", "image", "caption", "alt_text", "is_primary", "uploaded_at", "uploaded_by", ) assert ParkPhotoSerializer.Meta.fields == expected_fields