test_avatars.py 8.6 KB

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