import json import math import os.path from pathlib import Path from shutil import rmtree import pytest from django.contrib.admin.sites import AdminSite from django.core import management from django.core.cache import cache from django.test.utils import override_settings from PIL import Image, ImageChops from rest_framework import status from rest_framework.reverse import reverse from rest_framework.test import APITestCase, APIClient from avatar.admin import AvatarAdmin from avatar.api.views import AvatarViewSets from avatar.conf import settings from avatar.models import Avatar from avatar.signals import avatar_deleted from avatar.templatetags import avatar_tags from avatar.utils import ( get_cache_key, get_primary_avatar, get_user_model, invalidate_cache, ) # class AssertSignal: # def __init__(self): # self.signal_sent_count = 0 # self.avatar = None # self.user = None # self.sender = None # self.signal = None # # def __call__(self, user, avatar, sender, signal): # self.user = user # self.avatar = avatar # self.sender = sender # self.signal = signal # self.signal_sent_count += 1 def upload_helper(o, filename): f = open(os.path.join(o.testdatapath, filename), "rb") response = o.client.post( reverse("avatar-list"), { "avatar": f, "primary":True }, follow=True, ) f.close() return response def root_mean_square_difference(image1, image2): "Calculate the root-mean-square difference between two images" diff = ImageChops.difference(image1, image2).convert("L") h = diff.histogram() sq = (value * (idx**2) for idx, value in enumerate(h)) sum_of_squares = sum(sq) rms = math.sqrt(sum_of_squares / float(image1.size[0] * image1.size[1])) return rms pytestmark = [pytest.mark.urls('tests.urls'), pytest.mark.unit] class AvatarViewSetTests: client = APIClient() def api_client(self): user = get_user_model().objects.create_user( "test", "lennon@thebeatles.com", "testpassword" ) self.client.force_authenticate(user) return self.client def avatar_list(self,*args,**kwargs): print(args) print(kwargs) url = reverse('avatar-list') request = self.client.get(url) view = AvatarViewSets.as_view({'get':'list'}) response = view(request).render() assert response.status_code == 200 assert len(json.loads(response.content)) == 3 # @pytest.fixture # def test_user(db): # user = get_user_model().objects.create_user( # "test", "lennon@thebeatles.com", "testpassword" # ) # return user # # @pytest.fixture # def api_client(test_user): # client = APIClient() # client.force_authenticate(test_user) # return client # @pytest.fixture # def test_normal_image_upload(db,test_user): # avatar = Avatar.objects.create( # avatar='test.png', # user=test_user, # primary=True # ) # return avatar # class AvatarTests: # @classmethod # def setUpClass(cls): # cls.path = os.path.dirname(__file__) # cls.testdatapath = os.path.join(cls.path, "data") # cls.testmediapath = os.path.join(cls.path, "../test-media/") # return super(AvatarTests).setUpClass() # # # def setUp(self): # self.user = get_user_model().objects.create_user( # "test", "lennon@thebeatles.com", "testpassword" # ) # self.user.save() # self.client.login(username="test", password="testpassword") # Image.init() # # def tearDown(self): # if os.path.exists(self.testmediapath): # rmtree(self.testmediapath) # return super().tearDown() # # def assertMediaFileExists(self, path): # full_path = os.path.join(self.testmediapath, f".{path}") # if not Path(full_path).resolve().is_file(): # raise AssertionError(f"File does not exist: {full_path}") # # def test_non_image_upload(self): # response = upload_helper(self, "nonimagefile") # self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # # # def test_normal_image_upload(self): # response = upload_helper(self, "test.png") # self.assertEqual(response.status_code, status.HTTP_201_CREATED) # avatar = get_primary_avatar(self.user) # self.assertIsNotNone(avatar) # self.assertEqual(avatar.user, self.user) # self.assertTrue(avatar.primary) # # # def test_render_primary_different_size(self): # # upload_helper(self, "test.png") # # avatar = get_primary_avatar(self.user) # # width = 350 # # height = 250 # # url = avatar.avatar_url(width, height) # # response = self.client.get(f'{url}?width={width}&height={height}') # # self.assertEqual(response.status_code,status.HTTP_200_OK) # # # self.assertIsNone(url) # # # self.assertEqual(response.status_code,status.HTTP_200_OK) # # # # We allow the .tiff file extension but not the mime type # # @override_settings(AVATAR_ALLOWED_FILE_EXTS=(".png", ".gif", ".jpg", ".tiff")) # # @override_settings( # # AVATAR_ALLOWED_MIMETYPES=("image/png", "image/gif", "image/jpeg") # # ) # # def test_unsupported_image_format_upload(self): # # """Check with python-magic that we detect corrupted / unapprovd image files correctly""" # # response = upload_helper(self, "test.tiff") # # self.assertEqual(response.status_code, 200) # # self.assertEqual(len(response.redirect_chain), 0) # Redirect only if it worked # # self.assertNotEqual(response.context["upload_avatar_form"].errors, {}) # # # # # We allow the .tiff file extension and the mime type # # @override_settings(AVATAR_ALLOWED_FILE_EXTS=(".png", ".gif", ".jpg", ".tiff")) # # @override_settings( # # AVATAR_ALLOWED_MIMETYPES=("image/png", "image/gif", "image/jpeg", "image/tiff") # # ) # # def test_supported_image_format_upload(self): # # """Check with python-magic that we detect corrupted / unapprovd image files correctly""" # # response = upload_helper(self, "test.tiff") # # self.assertEqual(response.status_code, 200) # # self.assertEqual(len(response.redirect_chain), 1) # Redirect only if it worked # # self.assertEqual(response.context["upload_avatar_form"].errors, {}) # # # # @override_settings(AVATAR_ALLOWED_FILE_EXTS=(".jpg", ".png")) # # def test_image_without_wrong_extension(self): # # response = upload_helper(self, "imagefilewithoutext") # # self.assertEqual(response.status_code, 200) # # self.assertEqual(len(response.redirect_chain), 0) # Redirect only if it worked # # self.assertNotEqual(response.context["upload_avatar_form"].errors, {}) # # # # @override_settings(AVATAR_ALLOWED_FILE_EXTS=(".jpg", ".png")) # # def test_image_with_wrong_extension(self): # # response = upload_helper(self, "imagefilewithwrongext.ogg") # # self.assertEqual(response.status_code, 200) # # self.assertEqual(len(response.redirect_chain), 0) # Redirect only if it worked # # self.assertNotEqual(response.context["upload_avatar_form"].errors, {}) # # # # def test_image_too_big(self): # # # use with AVATAR_MAX_SIZE = 1024 * 1024 # # response = upload_helper(self, "testbig.png") # # self.assertEqual(response.status_code, 200) # # self.assertEqual(len(response.redirect_chain), 0) # Redirect only if it worked # # self.assertNotEqual(response.context["upload_avatar_form"].errors, {}) # # # # def test_default_url(self): # # response = self.client.get( # # reverse( # # "avatar_render_primary", # # kwargs={ # # "user": self.user.username, # # "width": 80, # # }, # # ) # # ) # # loc = response["Location"] # # base_url = getattr(settings, "STATIC_URL", None) # # if not base_url: # # base_url = settings.MEDIA_URL # # self.assertTrue(base_url in loc) # # self.assertTrue(loc.endswith(settings.AVATAR_DEFAULT_URL)) # # # # def test_non_existing_user(self): # # a = get_primary_avatar("nonexistinguser") # # self.assertEqual(a, None) # # # # def test_there_can_be_only_one_primary_avatar(self): # # for _ in range(1, 10): # # self.test_normal_image_upload() # # count = Avatar.objects.filter(user=self.user, primary=True).count() # # self.assertEqual(count, 1) # # # # def test_delete_avatar(self): # # self.test_normal_image_upload() # # avatar = Avatar.objects.filter(user=self.user) # # self.assertEqual(len(avatar), 1) # # receiver = AssertSignal() # # avatar_deleted.connect(receiver) # # response = self.client.post( # # reverse("avatar_delete"), # # { # # "choices": [avatar[0].id], # # }, # # follow=True, # # ) # # self.assertEqual(response.status_code, 200) # # self.assertEqual(len(response.redirect_chain), 1) # # count = Avatar.objects.filter(user=self.user).count() # # self.assertEqual(count, 0) # # self.assertEqual(receiver.user, self.user) # # self.assertEqual(receiver.avatar, avatar[0]) # # self.assertEqual(receiver.sender, Avatar) # # self.assertEqual(receiver.signal_sent_count, 1) # # # # def test_delete_primary_avatar_and_new_primary(self): # # self.test_there_can_be_only_one_primary_avatar() # # primary = get_primary_avatar(self.user) # # oid = primary.id # # self.client.post( # # reverse("avatar_delete"), # # { # # "choices": [oid], # # }, # # ) # # primaries = Avatar.objects.filter(user=self.user, primary=True) # # self.assertEqual(len(primaries), 1) # # self.assertNotEqual(oid, primaries[0].id) # # avatars = Avatar.objects.filter(user=self.user) # # self.assertEqual(avatars[0].id, primaries[0].id) # # # # def test_change_avatar_get(self): # # self.test_normal_image_upload() # # response = self.client.get(reverse("avatar_change")) # # # # self.assertEqual(response.status_code, 200) # # self.assertIsNotNone(response.context["avatar"]) # # # # def test_change_avatar_post_updates_primary_avatar(self): # # self.test_there_can_be_only_one_primary_avatar() # # old_primary = Avatar.objects.get(user=self.user, primary=True) # # choice = Avatar.objects.filter(user=self.user, primary=False)[0] # # response = self.client.post( # # reverse("avatar_change"), # # { # # "choice": choice.pk, # # }, # # ) # # # # self.assertEqual(response.status_code, 302) # # new_primary = Avatar.objects.get(user=self.user, primary=True) # # self.assertEqual(new_primary.pk, choice.pk) # # # Avatar with old primary pk exists but it is not primary anymore # # self.assertTrue( # # Avatar.objects.filter( # # user=self.user, pk=old_primary.pk, primary=False # # ).exists() # # ) # # # # def test_too_many_avatars(self): # # for _ in range(0, settings.AVATAR_MAX_AVATARS_PER_USER): # # self.test_normal_image_upload() # # count_before = Avatar.objects.filter(user=self.user).count() # # response = upload_helper(self, "test.png") # # count_after = Avatar.objects.filter(user=self.user).count() # # self.assertEqual(response.status_code, 200) # # self.assertEqual(len(response.redirect_chain), 0) # Redirect only if it worked # # self.assertNotEqual(response.context["upload_avatar_form"].errors, {}) # # self.assertEqual(count_before, count_after) # # # # def test_automatic_thumbnail_creation_RGBA(self): # # upload_helper(self, "django.png") # # avatar = get_primary_avatar(self.user) # # image = Image.open( # # avatar.avatar.storage.open( # # avatar.avatar_name(settings.AVATAR_DEFAULT_SIZE), "rb" # # ) # # ) # # self.assertEqual(image.mode, "RGBA") # # # # @override_settings(AVATAR_THUMB_FORMAT="JPEG") # # def test_automatic_thumbnail_creation_CMYK(self): # # upload_helper(self, "django_pony_cmyk.jpg") # # avatar = get_primary_avatar(self.user) # # image = Image.open( # # avatar.avatar.storage.open( # # avatar.avatar_name(settings.AVATAR_DEFAULT_SIZE), "rb" # # ) # # ) # # self.assertEqual(image.mode, "RGB") # # # # def test_automatic_thumbnail_creation_image_type_conversion(self): # # upload_helper(self, "django_pony_cmyk.jpg") # # self.assertMediaFileExists( # # f"/avatars/{self.user.id}/resized/80/80/django_pony_cmyk.png" # # ) # # # # def test_thumbnail_transpose_based_on_exif(self): # # upload_helper(self, "image_no_exif.jpg") # # avatar = get_primary_avatar(self.user) # # image_no_exif = Image.open( # # avatar.avatar.storage.open( # # avatar.avatar_name(settings.AVATAR_DEFAULT_SIZE), "rb" # # ) # # ) # # # # upload_helper(self, "image_exif_orientation.jpg") # # avatar = get_primary_avatar(self.user) # # image_with_exif = Image.open( # # avatar.avatar.storage.open( # # avatar.avatar_name(settings.AVATAR_DEFAULT_SIZE), "rb" # # ) # # ) # # # # self.assertLess(root_mean_square_difference(image_with_exif, image_no_exif), 1) # # # # def test_automatic_thumbnail_creation_nondefault_filename(self): # # upload_helper(self, "django #3.png") # # self.assertMediaFileExists( # # f"/avatars/{self.user.id}/resized/80/80/django_3.png" # # ) # # # # def test_has_avatar_False_if_no_avatar(self): # # self.assertFalse(avatar_tags.has_avatar(self.user)) # # # # def test_has_avatar_False_if_not_user_model(self): # # self.assertFalse(avatar_tags.has_avatar("Look, I'm a string")) # # # # def test_has_avatar_True(self): # # upload_helper(self, "test.png") # # # # self.assertTrue(avatar_tags.has_avatar(self.user)) # # # # def test_avatar_tag_works_with_username(self): # # upload_helper(self, "test.png") # # avatar = get_primary_avatar(self.user) # # # # result = avatar_tags.avatar(self.user.username) # # # # self.assertIn('User Avatar', result) # # # # @override_settings(AVATAR_EXPOSE_USERNAMES=True) # # def test_avatar_tag_works_with_exposed_username(self): # # upload_helper(self, "test.png") # # avatar = get_primary_avatar(self.user) # # # # result = avatar_tags.avatar(self.user.username) # # # # self.assertIn('test', result) # # # # def test_avatar_tag_works_with_user(self): # # upload_helper(self, "test.png") # # avatar = get_primary_avatar(self.user) # # # # result = avatar_tags.avatar(self.user) # # # # self.assertIn('User Avatar', result) # # # # def test_avatar_tag_works_with_custom_size(self): # # upload_helper(self, "test.png") # # avatar = get_primary_avatar(self.user) # # # # result = avatar_tags.avatar(self.user, 100) # # # # self.assertIn('User Avatar', result) # # # # def test_avatar_tag_works_with_rectangle(self): # # upload_helper(self, "test.png") # # avatar = get_primary_avatar(self.user) # # # # result = avatar_tags.avatar(self.user, 100, 150) # # # # self.assertIn('User Avatar', result) # # # # def test_avatar_tag_works_with_kwargs(self): # # upload_helper(self, "test.png") # # avatar = get_primary_avatar(self.user) # # # # result = avatar_tags.avatar(self.user, title="Avatar") # # html = 'User Avatar'.format( # # avatar.avatar_url(80) # # ) # # self.assertInHTML(html, result) # # # # def test_primary_avatar_tag_works(self): # # upload_helper(self, "test.png") # # # # result = avatar_tags.primary_avatar(self.user) # # # # self.assertIn(f'User Avatar', result) # # # # response = self.client.get(f"/avatar/render_primary/{self.user.id}/80/") # # self.assertEqual(response.status_code, 302) # # self.assertMediaFileExists(response.url) # # # # def test_primary_avatar_tag_works_with_custom_size(self): # # upload_helper(self, "test.png") # # # # result = avatar_tags.primary_avatar(self.user, 90) # # # # self.assertIn(f'User Avatar', result) # # # # response = self.client.get(f"/avatar/render_primary/{self.user.id}/90/") # # self.assertEqual(response.status_code, 302) # # self.assertMediaFileExists(response.url) # # # # def test_primary_avatar_tag_works_with_rectangle(self): # # upload_helper(self, "test.png") # # # # result = avatar_tags.primary_avatar(self.user, 60, 110) # # # # self.assertIn( # # f'User Avatar', result) # # # # response = self.client.get(f"/avatar/render_primary/{self.user.id}/60/110/") # # self.assertEqual(response.status_code, 302) # # self.assertMediaFileExists(response.url) # # # # @override_settings(AVATAR_EXPOSE_USERNAMES=True) # # def test_primary_avatar_tag_works_with_exposed_user(self): # # upload_helper(self, "test.png") # # # # result = avatar_tags.primary_avatar(self.user) # # # # self.assertIn( # # f'test', result) # # # # response = self.client.get(f"/avatar/render_primary/{self.user.username}/80/") # # self.assertEqual(response.status_code, 302) # # self.assertMediaFileExists(response.url) # # # # def test_default_add_template(self): # # response = self.client.get("/avatar/add/") # # self.assertContains(response, "Upload New Image") # # self.assertNotContains(response, "ALTERNATE ADD TEMPLATE") # # # # @override_settings(AVATAR_ADD_TEMPLATE="alt/add.html") # # def test_custom_add_template(self): # # response = self.client.get("/avatar/add/") # # self.assertNotContains(response, "Upload New Image") # # self.assertContains(response, "ALTERNATE ADD TEMPLATE") # # # # def test_default_change_template(self): # # response = self.client.get("/avatar/change/") # # self.assertContains(response, "Upload New Image") # # self.assertNotContains(response, "ALTERNATE CHANGE TEMPLATE") # # # # @override_settings(AVATAR_CHANGE_TEMPLATE="alt/change.html") # # def test_custom_change_template(self): # # response = self.client.get("/avatar/change/") # # self.assertNotContains(response, "Upload New Image") # # self.assertContains(response, "ALTERNATE CHANGE TEMPLATE") # # # # def test_default_delete_template(self): # # upload_helper(self, "test.png") # # response = self.client.get("/avatar/delete/") # # self.assertContains(response, "like to delete.") # # self.assertNotContains(response, "ALTERNATE DELETE TEMPLATE") # # # # @override_settings(AVATAR_DELETE_TEMPLATE="alt/delete.html") # # def test_custom_delete_template(self): # # response = self.client.get("/avatar/delete/") # # self.assertNotContains(response, "like to delete.") # # self.assertContains(response, "ALTERNATE DELETE TEMPLATE") # # # # def get_media_file_mtime(self, path): # # full_path = os.path.join(self.testmediapath, f".{path}") # # return os.path.getmtime(full_path) # # # # def test_rebuild_avatars(self): # # upload_helper(self, "test.png") # # avatar_51_url = get_primary_avatar(self.user).avatar_url(51) # # self.assertMediaFileExists(avatar_51_url) # # avatar_51_mtime = self.get_media_file_mtime(avatar_51_url) # # # # avatar_62_url = get_primary_avatar(self.user).avatar_url(62) # # self.assertMediaFileExists(avatar_62_url) # # avatar_62_mtime = self.get_media_file_mtime(avatar_62_url) # # # # avatar_33_22_url = get_primary_avatar(self.user).avatar_url(33, 22) # # self.assertMediaFileExists(avatar_33_22_url) # # avatar_33_22_mtime = self.get_media_file_mtime(avatar_33_22_url) # # # # avatar_80_url = get_primary_avatar(self.user).avatar_url(80) # # self.assertMediaFileExists(avatar_80_url) # # avatar_80_mtime = self.get_media_file_mtime(avatar_80_url) # # # Rebuild all avatars # # management.call_command("rebuild_avatars", verbosity=0) # # # Make sure the media files all exist, but that their modification times differ # # self.assertMediaFileExists(avatar_51_url) # # self.assertNotEqual(avatar_51_mtime, self.get_media_file_mtime(avatar_51_url)) # # self.assertMediaFileExists(avatar_62_url) # # self.assertNotEqual(avatar_62_mtime, self.get_media_file_mtime(avatar_62_url)) # # self.assertMediaFileExists(avatar_33_22_url) # # self.assertNotEqual( # # avatar_33_22_mtime, self.get_media_file_mtime(avatar_33_22_url) # # ) # # self.assertMediaFileExists(avatar_80_url) # # self.assertNotEqual(avatar_80_mtime, self.get_media_file_mtime(avatar_80_url)) # # # # def test_invalidate_cache(self): # # upload_helper(self, "test.png") # # sizes_key = get_cache_key(self.user, "cached_sizes") # # sizes = cache.get(sizes_key, set()) # # # Only default 80x80 thumbnail is cached # # self.assertEqual(len(sizes), 1) # # # Invalidate cache # # invalidate_cache(self.user) # # sizes = cache.get(sizes_key, set()) # # # No thumbnail is cached. # # self.assertEqual(len(sizes), 0) # # # Create a custom 25x25 thumbnail and check that it is cached # # avatar_tags.avatar(self.user, 25) # # sizes = cache.get(sizes_key, set()) # # self.assertEqual(len(sizes), 1) # # # Invalidate cache again. # # invalidate_cache(self.user) # # sizes = cache.get(sizes_key, set()) # # # It should now be empty again # # self.assertEqual(len(sizes), 0)