mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 07:11:09 -05:00
478 lines
16 KiB
Python
478 lines
16 KiB
Python
"""
|
|
Tests for Park serializers.
|
|
|
|
Following Django styleguide pattern: test__<context>__<action>__<expected_outcome>
|
|
"""
|
|
|
|
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
|