test_attachments_middleware.py 10 KB

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