test_avatars.py 9.6 KB

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