Split backend tests into unit and integration suites, add isolated fixtures and coverage enforcement, and update CI and test documentation. Cover current authentication, content, notification, payment, media, and admin flows; fix behaviors revealed by the new tests. Validate both suites and the per-module gate against local PostgreSQL.
96 lines
6.1 KiB
Python
96 lines
6.1 KiB
Python
import pytest
|
|
import io
|
|
from datetime import timedelta
|
|
from PIL import Image
|
|
from django.core.management import call_command
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
from django.test import RequestFactory
|
|
from django.utils import timezone
|
|
from apps.blog.api.schemas import AuthorSchema, PostListSchema
|
|
from apps.blog.models import Post
|
|
from apps.events.api.schemas import EventGallerySchema, EventListSchema
|
|
from apps.events.models import Event
|
|
from apps.gallery.api.schemas import GallerySchema
|
|
from apps.gallery.models import Gallery
|
|
from apps.users.api.schemas import UserProfileSchema
|
|
from apps.users.models import User
|
|
from core.media import BLUR_VARIANT, PREVIEW_VARIANT, THUMBNAIL_VARIANT, delete_image_derivatives, derivative_name, process_public_image
|
|
|
|
@pytest.mark.django_db
|
|
@pytest.mark.integration
|
|
class TestMediaProcessingTests:
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _media_root(self, settings, tmp_path):
|
|
self.media_root = str(tmp_path)
|
|
settings.MEDIA_ROOT = self.media_root
|
|
settings.MEDIA_URL = '/media/'
|
|
|
|
def setup_method(self):
|
|
self.user = User.objects.create_user(username='media-user', email='media@example.com', mobile='09123456789', password='TestPass123!')
|
|
self.factory = RequestFactory()
|
|
|
|
def _upload(self, name: str, *, mode: str='RGB', fmt: str='JPEG') -> SimpleUploadedFile:
|
|
buffer = io.BytesIO()
|
|
Image.new(mode, (2400, 1800), color=(20, 80, 200)).save(buffer, format=fmt)
|
|
buffer.seek(0)
|
|
return SimpleUploadedFile(name, buffer.read(), content_type=f'image/{fmt.lower()}')
|
|
|
|
def test_process_public_image_generates_derivatives_for_jpeg(self):
|
|
gallery = Gallery.objects.create(title='JPEG image', uploaded_by=self.user, image=self._upload('photo.jpg', fmt='JPEG'))
|
|
result = process_public_image(gallery.image, 'gallery', force=True)
|
|
assert result.processed
|
|
assert gallery.image.storage.exists(derivative_name(gallery.image.name, THUMBNAIL_VARIANT))
|
|
assert gallery.image.storage.exists(derivative_name(gallery.image.name, PREVIEW_VARIANT))
|
|
assert gallery.image.storage.exists(derivative_name(gallery.image.name, BLUR_VARIANT))
|
|
|
|
def test_process_public_image_generates_derivatives_for_png(self):
|
|
gallery = Gallery.objects.create(title='PNG image', uploaded_by=self.user, image=self._upload('photo.png', mode='RGBA', fmt='PNG'))
|
|
result = process_public_image(gallery.image, 'gallery', force=True)
|
|
assert result.width == 2400
|
|
assert result.height == 1800
|
|
assert gallery.image.storage.exists(derivative_name(gallery.image.name, PREVIEW_VARIANT))
|
|
|
|
def test_delete_image_derivatives_removes_sidecars(self):
|
|
gallery = Gallery.objects.create(title='Delete derivatives', uploaded_by=self.user, image=self._upload('cleanup.jpg', fmt='JPEG'))
|
|
process_public_image(gallery.image, 'gallery', force=True)
|
|
delete_image_derivatives(gallery.image, 'gallery')
|
|
assert not gallery.image.storage.exists(derivative_name(gallery.image.name, THUMBNAIL_VARIANT))
|
|
assert not gallery.image.storage.exists(derivative_name(gallery.image.name, PREVIEW_VARIANT))
|
|
assert not gallery.image.storage.exists(derivative_name(gallery.image.name, BLUR_VARIANT))
|
|
|
|
def test_replacing_image_removes_previous_sidecars(self):
|
|
gallery = Gallery.objects.create(title='Replace derivatives', uploaded_by=self.user, image=self._upload('replace-first.jpg', fmt='JPEG'))
|
|
first_name = gallery.image.name
|
|
gallery.image = self._upload('replace-second.jpg', fmt='JPEG')
|
|
gallery.save()
|
|
assert not gallery.image.storage.exists(derivative_name(first_name, THUMBNAIL_VARIANT))
|
|
assert not gallery.image.storage.exists(derivative_name(first_name, PREVIEW_VARIANT))
|
|
assert not gallery.image.storage.exists(derivative_name(first_name, BLUR_VARIANT))
|
|
|
|
def test_backfill_command_is_idempotent(self):
|
|
gallery = Gallery(title='Backfill image', uploaded_by=self.user)
|
|
gallery._defer_image_processing = True
|
|
gallery.image = self._upload('backfill.jpg', fmt='JPEG')
|
|
gallery.save()
|
|
first = io.StringIO()
|
|
second = io.StringIO()
|
|
call_command('backfill_media_derivatives', stdout=first)
|
|
call_command('backfill_media_derivatives', stdout=second)
|
|
assert 'processed=1' in first.getvalue()
|
|
assert 'skipped=1' in second.getvalue()
|
|
|
|
def test_schema_resolvers_expose_derivative_urls(self):
|
|
self.user.profile_picture.save('profile.jpg', self._upload('profile.jpg', fmt='JPEG'))
|
|
gallery = Gallery.objects.create(title='Schema image', uploaded_by=self.user, image=self._upload('schema-gallery.jpg', fmt='JPEG'))
|
|
event = Event.objects.create(title='Schema event', description='Event description', start_time=timezone.now(), end_time=timezone.now() + timedelta(hours=2), featured_image=self._upload('schema-event.jpg', fmt='JPEG'))
|
|
post = Post.objects.create(title='Schema post', content='Post body', author=self.user, featured_image=self._upload('schema-post.jpg', fmt='JPEG'), status=Post.StatusChoices.PUBLISHED)
|
|
context = {'request': self.factory.get('/')}
|
|
assert derivative_name(event.featured_image.name, THUMBNAIL_VARIANT) in EventListSchema.resolve_absolute_featured_image_thumbnail_url(event, context)
|
|
assert derivative_name(event.featured_image.name, PREVIEW_VARIANT) in EventListSchema.resolve_absolute_featured_image_preview_url(event, context)
|
|
assert derivative_name(gallery.image.name, PREVIEW_VARIANT) in EventGallerySchema.resolve_absolute_image_preview_url(gallery, context)
|
|
assert derivative_name(gallery.image.name, BLUR_VARIANT) in GallerySchema.resolve_absolute_image_blur_url(gallery, context)
|
|
assert derivative_name(post.featured_image.name, THUMBNAIL_VARIANT) in PostListSchema.resolve_absolute_featured_image_thumbnail_url(post, context)
|
|
assert derivative_name(self.user.profile_picture.name, THUMBNAIL_VARIANT) in AuthorSchema.resolve_profile_picture_thumbnail_url(self.user, context)
|
|
assert derivative_name(self.user.profile_picture.name, PREVIEW_VARIANT) in UserProfileSchema.resolve_profile_picture_preview_url(self.user, context)
|