test_attachments_middleware.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. from django.conf import settings
  2. from rest_framework import serializers
  3. from misago.acl.testutils import override_acl
  4. from misago.categories.models import Category
  5. from misago.users.testutils import AuthenticatedUserTestCase
  6. from .. import testutils
  7. from ..api.postingendpoint import PostingEndpoint
  8. from ..api.postingendpoint.attachments import AttachmentsMiddleware, validate_attachments_count
  9. from ..models import Attachment, AttachmentType
  10. class RequestMock(object):
  11. def __init__(self, data=None):
  12. self.data = data or {}
  13. class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
  14. def setUp(self):
  15. super(AttachmentsMiddlewareTests, self).setUp()
  16. self.category = Category.objects.get(slug='first-category')
  17. self.thread = testutils.post_thread(category=self.category)
  18. self.post = self.thread.first_post
  19. self.post.update_fields = []
  20. self.override_acl()
  21. self.filetype = AttachmentType.objects.order_by('id').last()
  22. def override_acl(self, new_acl=None):
  23. override_acl(self.user, new_acl or {
  24. 'max_attachment_size': 1024
  25. })
  26. def mock_attachment(self, user=True, post=None):
  27. return Attachment.objects.create(
  28. secret=Attachment.generate_new_secret(),
  29. filetype=self.filetype,
  30. post=post,
  31. size=1000,
  32. uploader=self.user if user else None,
  33. uploader_name=self.user.username,
  34. uploader_slug=self.user.slug,
  35. uploader_ip='127.0.0.1',
  36. filename='testfile_{}.zip'.format(Attachment.objects.count() + 1),
  37. )
  38. def test_use_this_middleware(self):
  39. """use_this_middleware returns False if we can't upload attachments"""
  40. middleware = AttachmentsMiddleware(user=self.user)
  41. self.override_acl({
  42. 'max_attachment_size': 0
  43. })
  44. self.assertFalse(middleware.use_this_middleware())
  45. self.override_acl({
  46. 'max_attachment_size': 1024
  47. })
  48. self.assertTrue(middleware.use_this_middleware())
  49. def test_middleware_is_optional(self):
  50. """middleware is optional"""
  51. INPUTS = (
  52. {},
  53. {'attachments': []}
  54. )
  55. for test_input in INPUTS:
  56. middleware = AttachmentsMiddleware(
  57. request=RequestMock(test_input),
  58. mode=PostingEndpoint.START,
  59. user=self.user,
  60. post=self.post
  61. )
  62. serializer = middleware.get_serializer()
  63. self.assertTrue(serializer.is_valid())
  64. def test_middleware_validates_ids(self):
  65. """middleware validates attachments ids"""
  66. INPUTS = (
  67. 'none',
  68. ['a', 'b', 123],
  69. range(settings.MISAGO_POST_ATTACHMENTS_LIMIT + 1)
  70. )
  71. for test_input in INPUTS:
  72. middleware = AttachmentsMiddleware(
  73. request=RequestMock({
  74. 'attachments': test_input
  75. }),
  76. mode=PostingEndpoint.START,
  77. user=self.user,
  78. post=self.post
  79. )
  80. serializer = middleware.get_serializer()
  81. self.assertFalse(serializer.is_valid(), "%r shouldn't validate" % test_input)
  82. def test_get_initial_attachments(self):
  83. """get_initial_attachments returns list of attachments already existing on post"""
  84. middleware = AttachmentsMiddleware(
  85. request=RequestMock(),
  86. mode=PostingEndpoint.EDIT,
  87. user=self.user,
  88. post=self.post
  89. )
  90. serializer = middleware.get_serializer()
  91. attachments = serializer.get_initial_attachments(
  92. middleware.mode, middleware.user, middleware.post)
  93. self.assertEqual(attachments, [])
  94. attachment = self.mock_attachment(post=self.post)
  95. attachments = serializer.get_initial_attachments(
  96. middleware.mode, middleware.user, middleware.post)
  97. self.assertEqual(attachments, [attachment])
  98. def test_get_new_attachments(self):
  99. """get_initial_attachments returns list of attachments already existing on post"""
  100. middleware = AttachmentsMiddleware(
  101. request=RequestMock(),
  102. mode=PostingEndpoint.EDIT,
  103. user=self.user,
  104. post=self.post
  105. )
  106. serializer = middleware.get_serializer()
  107. attachments = serializer.get_new_attachments(middleware.user, [1, 2, 3])
  108. self.assertEqual(attachments, [])
  109. attachment = self.mock_attachment()
  110. attachments = serializer.get_new_attachments(middleware.user, [attachment.pk])
  111. self.assertEqual(attachments, [attachment])
  112. # only own orphaned attachments may be assigned to posts
  113. other_user_attachment = self.mock_attachment(user=False)
  114. attachments = serializer.get_new_attachments(middleware.user, [other_user_attachment.pk])
  115. self.assertEqual(attachments, [])
  116. def test_cant_delete_attachment(self):
  117. """middleware validates if we have permission to delete other users attachments"""
  118. self.override_acl({
  119. 'max_attachment_size': 1024,
  120. 'can_delete_other_users_attachments': False
  121. })
  122. attachment = self.mock_attachment(user=False, post=self.post)
  123. self.assertIsNone(attachment.uploader)
  124. serializer = AttachmentsMiddleware(
  125. request=RequestMock({'attachments': []}),
  126. mode=PostingEndpoint.EDIT,
  127. user=self.user,
  128. post=self.post
  129. ).get_serializer()
  130. self.assertFalse(serializer.is_valid())
  131. def test_add_attachments(self):
  132. """middleware adds attachments to post"""
  133. attachments = [
  134. self.mock_attachment(),
  135. self.mock_attachment(),
  136. ]
  137. middleware = AttachmentsMiddleware(
  138. request=RequestMock({
  139. 'attachments': [a.pk for a in attachments]
  140. }),
  141. mode=PostingEndpoint.EDIT,
  142. user=self.user,
  143. post=self.post
  144. )
  145. serializer = middleware.get_serializer()
  146. self.assertTrue(serializer.is_valid())
  147. middleware.save(serializer)
  148. # attachments were associated with post
  149. self.assertEqual(self.post.update_fields, ['attachments_cache'])
  150. self.assertEqual(self.post.attachment_set.count(), 2)
  151. attachments_filenames = list(reversed([a.filename for a in attachments]))
  152. self.assertEqual([a['filename'] for a in self.post.attachments_cache], attachments_filenames)
  153. def test_remove_attachments(self):
  154. """middleware removes attachment from post and db"""
  155. attachments = [
  156. self.mock_attachment(post=self.post),
  157. self.mock_attachment(post=self.post),
  158. ]
  159. middleware = AttachmentsMiddleware(
  160. request=RequestMock({
  161. 'attachments': [attachments[0].pk]
  162. }),
  163. mode=PostingEndpoint.EDIT,
  164. user=self.user,
  165. post=self.post
  166. )
  167. serializer = middleware.get_serializer()
  168. self.assertTrue(serializer.is_valid())
  169. middleware.save(serializer)
  170. # attachments were associated with post
  171. self.assertEqual(self.post.update_fields, ['attachments_cache'])
  172. self.assertEqual(self.post.attachment_set.count(), 1)
  173. self.assertEqual(Attachment.objects.count(), 1)
  174. attachments_filenames = [attachments[0].filename]
  175. self.assertEqual([a['filename'] for a in self.post.attachments_cache], attachments_filenames)
  176. def test_steal_attachments(self):
  177. """middleware validates if attachments are already assigned to other posts"""
  178. other_post = testutils.reply_thread(self.thread)
  179. attachments = [
  180. self.mock_attachment(post=other_post),
  181. self.mock_attachment(),
  182. ]
  183. middleware = AttachmentsMiddleware(
  184. request=RequestMock({
  185. 'attachments': [attachments[0].pk, attachments[1].pk]
  186. }),
  187. mode=PostingEndpoint.EDIT,
  188. user=self.user,
  189. post=self.post
  190. )
  191. serializer = middleware.get_serializer()
  192. self.assertTrue(serializer.is_valid())
  193. middleware.save(serializer)
  194. # only unassociated attachment was associated with post
  195. self.assertEqual(self.post.update_fields, ['attachments_cache'])
  196. self.assertEqual(self.post.attachment_set.count(), 1)
  197. self.assertEqual(Attachment.objects.get(pk=attachments[0].pk).post, other_post)
  198. self.assertEqual(Attachment.objects.get(pk=attachments[1].pk).post, self.post)
  199. def test_edit_attachments(self):
  200. """middleware removes and adds attachments to post"""
  201. attachments = [
  202. self.mock_attachment(post=self.post),
  203. self.mock_attachment(post=self.post),
  204. self.mock_attachment(),
  205. ]
  206. middleware = AttachmentsMiddleware(
  207. request=RequestMock({
  208. 'attachments': [attachments[0].pk, attachments[2].pk]
  209. }),
  210. mode=PostingEndpoint.EDIT,
  211. user=self.user,
  212. post=self.post
  213. )
  214. serializer = middleware.get_serializer()
  215. self.assertTrue(serializer.is_valid())
  216. middleware.save(serializer)
  217. # attachments were associated with post
  218. self.assertEqual(self.post.update_fields, ['attachments_cache'])
  219. self.assertEqual(self.post.attachment_set.count(), 2)
  220. attachments_filenames = [attachments[2].filename, attachments[0].filename]
  221. self.assertEqual([a['filename'] for a in self.post.attachments_cache], attachments_filenames)
  222. class ValidateAttachmentsCountTests(AuthenticatedUserTestCase):
  223. def test_validate_attachments_count(self):
  224. """too large count of attachments is rejected"""
  225. validate_attachments_count(range(settings.MISAGO_POST_ATTACHMENTS_LIMIT))
  226. with self.assertRaises(serializers.ValidationError):
  227. validate_attachments_count(range(settings.MISAGO_POST_ATTACHMENTS_LIMIT + 1))