test_avatars.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. from pathlib import Path
  2. from unittest.mock import Mock
  3. from PIL import Image
  4. from django.core.exceptions import ValidationError
  5. from django.test import TestCase
  6. from django.utils.crypto import get_random_string
  7. from misago.conf import settings
  8. from misago.users.avatars import (
  9. dynamic,
  10. gallery,
  11. gravatar,
  12. set_default_avatar,
  13. store,
  14. uploaded,
  15. )
  16. from misago.users.models import Avatar, AvatarGallery
  17. from misago.users.testutils import create_test_user
  18. class AvatarsStoreTests(TestCase):
  19. def test_store(self):
  20. """store successfully stores and deletes avatar"""
  21. user = create_test_user("User", "user@example.com")
  22. test_image = Image.new("RGBA", (100, 100), 0)
  23. store.store_new_avatar(user, test_image)
  24. # reload user
  25. user.refresh_from_db()
  26. # assert that avatars were stored in media
  27. avatars_dict = {}
  28. for avatar in user.avatar_set.all():
  29. self.assertTrue(avatar.image.url)
  30. self.assertEqual(avatar.url, avatar.image.url)
  31. avatars_dict[avatar.size] = avatar
  32. # asserts that user.avatars cache was set
  33. self.assertEqual(len(avatars_dict), len(settings.MISAGO_AVATARS_SIZES))
  34. self.assertEqual(len(user.avatars), len(settings.MISAGO_AVATARS_SIZES))
  35. self.assertEqual(len(user.avatars), len(avatars_dict))
  36. for avatar in user.avatars:
  37. self.assertIn(avatar["size"], settings.MISAGO_AVATARS_SIZES)
  38. self.assertEqual(avatar["url"], avatars_dict[avatar["size"]].url)
  39. # another avatar change deleted old avatars
  40. store.store_new_avatar(user, test_image)
  41. for old_avatar in avatars_dict.values():
  42. avatar_path = Path(old_avatar.image.path)
  43. self.assertFalse(avatar_path.exists())
  44. self.assertFalse(avatar_path.is_file())
  45. with self.assertRaises(Avatar.DoesNotExist):
  46. Avatar.objects.get(pk=old_avatar.pk)
  47. # and updated user avatars again
  48. new_avatars_dict = {}
  49. for size in settings.MISAGO_AVATARS_SIZES:
  50. avatar = user.avatar_set.get(size=size)
  51. self.assertTrue(avatar.image.url)
  52. self.assertEqual(avatar.url, avatar.image.url)
  53. new_avatars_dict[size] = avatar
  54. self.assertTrue(avatars_dict != new_avatars_dict)
  55. # asserts that user.avatars cache was updated
  56. self.assertEqual(len(user.avatars), len(settings.MISAGO_AVATARS_SIZES))
  57. for avatar in user.avatars:
  58. self.assertIn(avatar["size"], settings.MISAGO_AVATARS_SIZES)
  59. self.assertEqual(avatar["url"], new_avatars_dict[avatar["size"]].url)
  60. # delete avatar
  61. store.delete_avatar(user)
  62. for removed_avatar in new_avatars_dict.values():
  63. avatar_path = Path(removed_avatar.image.path)
  64. self.assertFalse(avatar_path.exists())
  65. self.assertFalse(avatar_path.is_file())
  66. with self.assertRaises(Avatar.DoesNotExist):
  67. Avatar.objects.get(pk=removed_avatar.pk)
  68. class AvatarSetterTests(TestCase):
  69. def setUp(self):
  70. self.user = create_test_user("User", "user@example.com", avatars=None)
  71. def tearDown(self):
  72. store.delete_avatar(self.user)
  73. def get_current_user(self):
  74. self.user.refresh_from_db()
  75. return self.user
  76. def assertNoAvatarIsSet(self):
  77. user = self.get_current_user()
  78. self.assertFalse(user.avatars)
  79. def assertAvatarWasSet(self):
  80. user = self.get_current_user()
  81. avatars_dict = {}
  82. for avatar in user.avatar_set.all():
  83. avatar_path = Path(avatar.image.path)
  84. self.assertTrue(avatar_path.exists())
  85. self.assertTrue(avatar_path.is_file())
  86. avatars_dict[avatar.size] = avatar
  87. self.assertEqual(len(user.avatars), len(avatars_dict))
  88. self.assertEqual(len(user.avatars), len(settings.MISAGO_AVATARS_SIZES))
  89. def test_dynamic_avatar(self):
  90. """dynamic avatar gets created"""
  91. self.assertNoAvatarIsSet()
  92. dynamic.set_avatar(self.user)
  93. self.assertAvatarWasSet()
  94. def test_random_gallery_avatar_no_gallery(self):
  95. """runtime error is raised when no gallery exists"""
  96. with self.assertRaises(RuntimeError):
  97. gallery.set_random_avatar(self.user)
  98. def test_random_gallery_avatar(self):
  99. """dynamic avatar gets created"""
  100. gallery.load_avatar_galleries()
  101. self.assertNoAvatarIsSet()
  102. gallery.set_random_avatar(self.user)
  103. self.assertAvatarWasSet()
  104. def test_selected_gallery_avatar(self):
  105. """dynamic avatar gets created"""
  106. gallery.load_avatar_galleries()
  107. self.assertNoAvatarIsSet()
  108. test_avatar = AvatarGallery.objects.order_by("id").last()
  109. gallery.set_avatar(self.user, test_avatar)
  110. self.assertAvatarWasSet()
  111. def test_gravatar(self):
  112. """dynamic avatar gets created"""
  113. self.assertNoAvatarIsSet()
  114. self.user.set_email("rafio.xudb@gmail.com")
  115. gravatar.set_avatar(self.user)
  116. self.assertAvatarWasSet()
  117. def test_default_avatar_gravatar(self):
  118. """default gravatar gets set"""
  119. self.assertNoAvatarIsSet()
  120. set_default_avatar(self.user, "gravatar", "dynamic")
  121. self.assertAvatarWasSet()
  122. def test_default_avatar_gravatar_fallback_dynamic(self):
  123. """default gravatar fails but fallback dynamic works"""
  124. gibberish_email = "%s@%s.%s" % (
  125. get_random_string(6),
  126. get_random_string(6),
  127. get_random_string(3),
  128. )
  129. self.user.set_email(gibberish_email)
  130. self.user.save()
  131. self.assertNoAvatarIsSet()
  132. set_default_avatar(self.user, "gravatar", "dynamic")
  133. self.assertAvatarWasSet()
  134. def test_default_avatar_gravatar_fallback_empty_gallery(self):
  135. """default both gravatar and fallback fail set"""
  136. gibberish_email = "%s@%s.%s" % (
  137. get_random_string(6),
  138. get_random_string(6),
  139. get_random_string(3),
  140. )
  141. self.user.set_email(gibberish_email)
  142. self.user.save()
  143. self.assertNoAvatarIsSet()
  144. self.user.save()
  145. set_default_avatar(self.user, "gravatar", "gallery")
  146. self.assertAvatarWasSet()
  147. class MockAvatarFile(object):
  148. def __init__(self, size=None, name=None, mime=None):
  149. self.size = size
  150. self.name = name
  151. self.content_type = mime
  152. class UploadedAvatarTests(TestCase):
  153. def test_clean_crop(self):
  154. """crop validation and cleaning"""
  155. image = Image.new("RGBA", (200, 200), 0)
  156. with self.assertRaises(ValidationError):
  157. uploaded.clean_crop(image, "abc")
  158. with self.assertRaises(ValidationError):
  159. uploaded.clean_crop(image, {})
  160. with self.assertRaises(ValidationError):
  161. uploaded.clean_crop(image, {"offset": {"x": "ugabuga"}})
  162. with self.assertRaises(ValidationError):
  163. uploaded.clean_crop(image, {"offset": {"x": 0, "y": 0}, "zoom": -2})
  164. with self.assertRaises(ValidationError):
  165. uploaded.clean_crop(image, {"offset": {"x": 0, "y": 0}, "zoom": 2})
  166. def test_uploaded_image_size_validation(self):
  167. """uploaded image size is validated"""
  168. settings = Mock(avatar_upload_limit=1) # no. of MBs
  169. image = MockAvatarFile(size=1025)
  170. with self.assertRaises(ValidationError):
  171. uploaded.validate_file_size(settings, image)
  172. image = MockAvatarFile(size=1024)
  173. uploaded.validate_file_size(settings, image)
  174. def test_uploaded_image_extension_validation(self):
  175. """uploaded image extension is validated"""
  176. for invalid_extension in (".txt", ".zip", ".py", ".tiff"):
  177. with self.assertRaises(ValidationError):
  178. image = MockAvatarFile(name="test%s" % invalid_extension)
  179. uploaded.validate_extension(image)
  180. for valid_extension in uploaded.ALLOWED_EXTENSIONS:
  181. image = MockAvatarFile(name="test%s" % valid_extension)
  182. uploaded.validate_extension(image)
  183. def test_uploaded_image_mime_validation(self):
  184. """uploaded image mime type is validated"""
  185. image = MockAvatarFile(mime="fake/mime")
  186. with self.assertRaises(ValidationError):
  187. uploaded.validate_mime(image)
  188. for valid_mime in uploaded.ALLOWED_MIME_TYPES:
  189. image = MockAvatarFile(mime=valid_mime)
  190. uploaded.validate_mime(image)