Files
guilan-ace-backend/core/tests/integration/test_media.py
Amirhossein Khalili ff99130938 test: migrate backend suites to pytest with module coverage gate
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.
2026-09-26 16:50:29 +03:30

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)