mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:51:08 -05:00
Refactor test utilities and enhance ASGI settings
- Cleaned up and standardized assertions in ApiTestMixin for API response validation. - Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE. - Removed unused imports and improved formatting in settings.py. - Refactored URL patterns in urls.py for better readability and organization. - Enhanced view functions in views.py for consistency and clarity. - Added .flake8 configuration for linting and style enforcement. - Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
119
media/tests.py
119
media/tests.py
@@ -4,11 +4,10 @@ from django.contrib.auth import get_user_model
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.test.utils import override_settings
|
||||
from django.db import models
|
||||
from datetime import datetime
|
||||
from PIL import Image
|
||||
import piexif # type: ignore
|
||||
import piexif # type: ignore
|
||||
import io
|
||||
import shutil
|
||||
import tempfile
|
||||
@@ -23,18 +22,19 @@ from parks.models import Park, Company as Operator
|
||||
User = get_user_model()
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@override_settings(MEDIA_ROOT=tempfile.mkdtemp())
|
||||
class PhotoModelTests(TestCase):
|
||||
test_media_root: str
|
||||
user: models.Model
|
||||
park: Park
|
||||
content_type: ContentType
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
super().setUpClass()
|
||||
cls.test_media_root = settings.MEDIA_ROOT
|
||||
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
try:
|
||||
@@ -48,7 +48,7 @@ class PhotoModelTests(TestCase):
|
||||
self.park = self._create_test_park()
|
||||
self.content_type = ContentType.objects.get_for_model(Park)
|
||||
self._setup_test_directory()
|
||||
|
||||
|
||||
def tearDown(self) -> None:
|
||||
self._cleanup_test_directory()
|
||||
Photo.objects.all().delete()
|
||||
@@ -57,31 +57,26 @@ class PhotoModelTests(TestCase):
|
||||
|
||||
def _create_test_user(self) -> models.Model:
|
||||
"""Create a test user for the tests"""
|
||||
return User.objects.create_user(
|
||||
username='testuser',
|
||||
password='testpass123'
|
||||
)
|
||||
return User.objects.create_user(username="testuser", password="testpass123")
|
||||
|
||||
def _create_test_park(self) -> Park:
|
||||
"""Create a test park for the tests"""
|
||||
operator = Operator.objects.create(name='Test Operator')
|
||||
operator = Operator.objects.create(name="Test Operator")
|
||||
return Park.objects.create(
|
||||
name='Test Park',
|
||||
slug='test-park',
|
||||
operator=operator
|
||||
name="Test Park", slug="test-park", operator=operator
|
||||
)
|
||||
|
||||
def _setup_test_directory(self) -> None:
|
||||
"""Set up test directory and clean any existing test files"""
|
||||
try:
|
||||
# Clean up any existing test park directory
|
||||
test_park_dir = os.path.join(settings.MEDIA_ROOT, 'park', 'test-park')
|
||||
test_park_dir = os.path.join(settings.MEDIA_ROOT, "park", "test-park")
|
||||
if os.path.exists(test_park_dir):
|
||||
shutil.rmtree(test_park_dir, ignore_errors=True)
|
||||
|
||||
|
||||
# Create necessary directories
|
||||
os.makedirs(test_park_dir, exist_ok=True)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to set up test directory: {e}")
|
||||
raise
|
||||
@@ -89,7 +84,7 @@ class PhotoModelTests(TestCase):
|
||||
def _cleanup_test_directory(self) -> None:
|
||||
"""Clean up test directories and files"""
|
||||
try:
|
||||
test_park_dir = os.path.join(settings.MEDIA_ROOT, 'park', 'test-park')
|
||||
test_park_dir = os.path.join(settings.MEDIA_ROOT, "park", "test-park")
|
||||
if os.path.exists(test_park_dir):
|
||||
shutil.rmtree(test_park_dir, ignore_errors=True)
|
||||
except Exception as e:
|
||||
@@ -104,25 +99,29 @@ class PhotoModelTests(TestCase):
|
||||
finally:
|
||||
MediaStorage.reset_counters()
|
||||
|
||||
def create_test_image_with_exif(self, date_taken: Optional[datetime] = None, filename: str = 'test.jpg') -> SimpleUploadedFile:
|
||||
def create_test_image_with_exif(
|
||||
self, date_taken: Optional[datetime] = None, filename: str = "test.jpg"
|
||||
) -> SimpleUploadedFile:
|
||||
"""Helper method to create a test image with EXIF data"""
|
||||
image = Image.new('RGB', (100, 100), color='red')
|
||||
image = Image.new("RGB", (100, 100), color="red")
|
||||
image_io = io.BytesIO()
|
||||
|
||||
|
||||
# Save image first without EXIF
|
||||
image.save(image_io, 'JPEG')
|
||||
image.save(image_io, "JPEG")
|
||||
image_io.seek(0)
|
||||
|
||||
|
||||
if date_taken:
|
||||
# Create EXIF data
|
||||
exif_dict = {
|
||||
"0th": {},
|
||||
"Exif": {
|
||||
piexif.ExifIFD.DateTimeOriginal: date_taken.strftime("%Y:%m:%d %H:%M:%S").encode()
|
||||
}
|
||||
piexif.ExifIFD.DateTimeOriginal: date_taken.strftime(
|
||||
"%Y:%m:%d %H:%M:%S"
|
||||
).encode()
|
||||
},
|
||||
}
|
||||
exif_bytes = piexif.dump(exif_dict)
|
||||
|
||||
|
||||
# Insert EXIF into image
|
||||
image_with_exif = io.BytesIO()
|
||||
piexif.insert(exif_bytes, image_io.getvalue(), image_with_exif)
|
||||
@@ -130,24 +129,20 @@ class PhotoModelTests(TestCase):
|
||||
image_data = image_with_exif.getvalue()
|
||||
else:
|
||||
image_data = image_io.getvalue()
|
||||
|
||||
return SimpleUploadedFile(
|
||||
filename,
|
||||
image_data,
|
||||
content_type='image/jpeg'
|
||||
)
|
||||
|
||||
return SimpleUploadedFile(filename, image_data, content_type="image/jpeg")
|
||||
|
||||
def test_filename_normalization(self) -> None:
|
||||
"""Test that filenames are properly normalized"""
|
||||
with self._reset_storage_state():
|
||||
# Test with various problematic filenames
|
||||
test_cases = [
|
||||
('test with spaces.jpg', 'test-park_1.jpg'),
|
||||
('TEST_UPPER.JPG', 'test-park_2.jpg'),
|
||||
('special@#chars.jpeg', 'test-park_3.jpg'),
|
||||
('no-extension', 'test-park_4.jpg'),
|
||||
('multiple...dots.jpg', 'test-park_5.jpg'),
|
||||
('très_açaí.jpg', 'test-park_6.jpg'), # Unicode characters
|
||||
("test with spaces.jpg", "test-park_1.jpg"),
|
||||
("TEST_UPPER.JPG", "test-park_2.jpg"),
|
||||
("special@#chars.jpeg", "test-park_3.jpg"),
|
||||
("no-extension", "test-park_4.jpg"),
|
||||
("multiple...dots.jpg", "test-park_5.jpg"),
|
||||
("très_açaí.jpg", "test-park_6.jpg"), # Unicode characters
|
||||
]
|
||||
|
||||
for input_name, expected_suffix in test_cases:
|
||||
@@ -155,20 +150,22 @@ class PhotoModelTests(TestCase):
|
||||
image=self.create_test_image_with_exif(filename=input_name),
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk
|
||||
object_id=self.park.pk,
|
||||
)
|
||||
|
||||
|
||||
# Check that the filename follows the normalized pattern
|
||||
self.assertTrue(
|
||||
photo.image.name.endswith(expected_suffix),
|
||||
f"Expected filename to end with {expected_suffix}, got {photo.image.name}"
|
||||
f"Expected filename to end with {expected_suffix}, got {
|
||||
photo.image.name}",
|
||||
)
|
||||
|
||||
|
||||
# Verify the path structure
|
||||
expected_path = f"park/{self.park.slug}/"
|
||||
self.assertTrue(
|
||||
photo.image.name.startswith(expected_path),
|
||||
f"Expected path to start with {expected_path}, got {photo.image.name}"
|
||||
f"Expected path to start with {expected_path}, got {
|
||||
photo.image.name}",
|
||||
)
|
||||
|
||||
def test_sequential_filename_numbering(self) -> None:
|
||||
@@ -180,32 +177,32 @@ class PhotoModelTests(TestCase):
|
||||
image=self.create_test_image_with_exif(),
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk
|
||||
object_id=self.park.pk,
|
||||
)
|
||||
|
||||
|
||||
expected_name = f"park/{self.park.slug}/test-park_{i}.jpg"
|
||||
self.assertEqual(
|
||||
photo.image.name,
|
||||
expected_name,
|
||||
f"Expected {expected_name}, got {photo.image.name}"
|
||||
f"Expected {expected_name}, got {photo.image.name}",
|
||||
)
|
||||
|
||||
def test_exif_date_extraction(self) -> None:
|
||||
"""Test EXIF date extraction from uploaded photos"""
|
||||
test_date = datetime(2024, 1, 1, 12, 0, 0)
|
||||
image_file = self.create_test_image_with_exif(test_date)
|
||||
|
||||
|
||||
photo = Photo.objects.create(
|
||||
image=image_file,
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk
|
||||
object_id=self.park.pk,
|
||||
)
|
||||
|
||||
|
||||
if photo.date_taken:
|
||||
self.assertEqual(
|
||||
photo.date_taken.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
test_date.strftime("%Y-%m-%d %H:%M:%S")
|
||||
test_date.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
)
|
||||
else:
|
||||
self.skipTest("EXIF data extraction not supported in test environment")
|
||||
@@ -213,14 +210,14 @@ class PhotoModelTests(TestCase):
|
||||
def test_photo_without_exif(self) -> None:
|
||||
"""Test photo upload without EXIF data"""
|
||||
image_file = self.create_test_image_with_exif()
|
||||
|
||||
|
||||
photo = Photo.objects.create(
|
||||
image=image_file,
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk
|
||||
object_id=self.park.pk,
|
||||
)
|
||||
|
||||
|
||||
self.assertIsNone(photo.date_taken)
|
||||
|
||||
def test_default_caption(self) -> None:
|
||||
@@ -229,9 +226,9 @@ class PhotoModelTests(TestCase):
|
||||
image=self.create_test_image_with_exif(),
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk
|
||||
object_id=self.park.pk,
|
||||
)
|
||||
|
||||
|
||||
expected_prefix = f"Uploaded by {cast(Any, self.user).username} on"
|
||||
self.assertTrue(photo.caption.startswith(expected_prefix))
|
||||
|
||||
@@ -242,20 +239,20 @@ class PhotoModelTests(TestCase):
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk,
|
||||
is_primary=True
|
||||
is_primary=True,
|
||||
)
|
||||
|
||||
|
||||
photo2 = Photo.objects.create(
|
||||
image=self.create_test_image_with_exif(),
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk,
|
||||
is_primary=True
|
||||
is_primary=True,
|
||||
)
|
||||
|
||||
|
||||
photo1.refresh_from_db()
|
||||
photo2.refresh_from_db()
|
||||
|
||||
|
||||
self.assertFalse(photo1.is_primary)
|
||||
self.assertTrue(photo2.is_primary)
|
||||
|
||||
@@ -267,7 +264,7 @@ class PhotoModelTests(TestCase):
|
||||
uploaded_by=self.user,
|
||||
content_type=self.content_type,
|
||||
object_id=self.park.pk,
|
||||
date_taken=test_date
|
||||
date_taken=test_date,
|
||||
)
|
||||
|
||||
|
||||
self.assertEqual(photo.date_taken, test_date)
|
||||
|
||||
Reference in New Issue
Block a user