test_thread_postmove_api.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import json
  4. from django.core.urlresolvers import reverse
  5. from django.utils.six.moves import range
  6. from misago.acl.testutils import override_acl
  7. from misago.categories.models import Category
  8. from misago.users.testutils import AuthenticatedUserTestCase
  9. from .. import testutils
  10. from ..api.postendpoints.move import MOVE_LIMIT
  11. from ..models import Thread
  12. class ThreadPostMoveApiTestCase(AuthenticatedUserTestCase):
  13. def setUp(self):
  14. super(ThreadPostMoveApiTestCase, self).setUp()
  15. self.category = Category.objects.get(slug='first-category')
  16. self.thread = testutils.post_thread(category=self.category)
  17. self.api_link = reverse('misago:api:thread-post-move', kwargs={
  18. 'thread_pk': self.thread.pk
  19. })
  20. Category(
  21. name='Category B',
  22. slug='category-b',
  23. ).insert_at(self.category, position='last-child', save=True)
  24. self.category_b = Category.objects.get(slug='category-b')
  25. self.override_acl()
  26. self.override_other_acl()
  27. def refresh_thread(self):
  28. self.thread = Thread.objects.get(pk=self.thread.pk)
  29. def override_acl(self, extra_acl=None):
  30. new_acl = self.user.acl
  31. new_acl['categories'][self.category.pk].update({
  32. 'can_see': 1,
  33. 'can_browse': 1,
  34. 'can_start_threads': 1,
  35. 'can_reply_threads': 1,
  36. 'can_edit_posts': 1,
  37. 'can_approve_content': 0,
  38. 'can_move_posts': 1
  39. })
  40. if extra_acl:
  41. new_acl['categories'][self.category.pk].update(extra_acl)
  42. override_acl(self.user, new_acl)
  43. def override_other_acl(self, acl=None):
  44. other_category_acl = self.user.acl['categories'][self.category.pk].copy()
  45. other_category_acl.update({
  46. 'can_see': 1,
  47. 'can_browse': 1,
  48. 'can_start_threads': 0,
  49. 'can_reply_threads': 0,
  50. 'can_edit_posts': 1,
  51. 'can_approve_content': 0,
  52. 'can_move_posts': 1
  53. })
  54. if acl:
  55. other_category_acl.update(acl)
  56. categories_acl = self.user.acl['categories']
  57. categories_acl[self.category_b.pk] = other_category_acl
  58. visible_categories = [self.category.pk]
  59. if other_category_acl['can_see']:
  60. visible_categories.append(self.category_b.pk)
  61. override_acl(self.user, {
  62. 'visible_categories': visible_categories,
  63. 'categories': categories_acl,
  64. })
  65. def test_anonymous_user(self):
  66. """you need to authenticate to merge posts"""
  67. self.logout_user()
  68. response = self.client.post(self.api_link, json.dumps({}), content_type="application/json")
  69. self.assertEqual(response.status_code, 403)
  70. def test_no_permission(self):
  71. """api validates permission to merge"""
  72. self.override_acl({
  73. 'can_move_posts': 0
  74. })
  75. response = self.client.post(self.api_link, json.dumps({}), content_type="application/json")
  76. self.assertContains(response, "You can't move posts in this thread.", status_code=403)
  77. def test_move_no_url(self):
  78. """api validates if thread url was given"""
  79. response = self.client.post(self.api_link)
  80. self.assertContains(response, "This is not a valid thread link.", status_code=400)
  81. def test_invalid_url(self):
  82. """api validates thread url"""
  83. response = self.client.post(self.api_link, {
  84. 'thread_url': self.user.get_absolute_url()
  85. })
  86. self.assertContains(response, "This is not a valid thread link.", status_code=400)
  87. def test_current_thread_url(self):
  88. """api validates if thread url given is to current thread"""
  89. response = self.client.post(self.api_link, {
  90. 'thread_url': self.thread.get_absolute_url()
  91. })
  92. self.assertContains(response, "Thread to move posts to is same as current one.", status_code=400)
  93. def test_other_thread_exists(self):
  94. """api validates if other thread exists"""
  95. self.override_other_acl()
  96. other_thread = testutils.post_thread(self.category_b)
  97. other_thread_url = other_thread.get_absolute_url()
  98. other_thread.delete()
  99. response = self.client.post(self.api_link, {
  100. 'thread_url': other_thread_url
  101. })
  102. self.assertContains(response, "The thread you have entered link to doesn't exist", status_code=400)
  103. def test_other_thread_is_invisible(self):
  104. """api validates if other thread is visible"""
  105. self.override_other_acl({
  106. 'can_see': 0
  107. })
  108. other_thread = testutils.post_thread(self.category_b)
  109. response = self.client.post(self.api_link, {
  110. 'thread_url': other_thread.get_absolute_url()
  111. })
  112. self.assertContains(response, "The thread you have entered link to doesn't exist", status_code=400)
  113. def test_other_thread_isnt_replyable(self):
  114. """api validates if other thread can be replied"""
  115. self.override_other_acl({
  116. 'can_reply_threads': 0
  117. })
  118. other_thread = testutils.post_thread(self.category_b)
  119. response = self.client.post(self.api_link, {
  120. 'thread_url': other_thread.get_absolute_url()
  121. })
  122. self.assertContains(response, "You can't move posts to threads you can't reply.", status_code=400)
  123. def test_empty_data(self):
  124. """api handles empty data"""
  125. other_thread = testutils.post_thread(self.category)
  126. response = self.client.post(self.api_link, {
  127. 'thread_url': other_thread.get_absolute_url()
  128. })
  129. self.assertContains(response, "You have to specify at least one post to move.", status_code=400)
  130. def test_no_posts_ids(self):
  131. """api rejects no posts ids"""
  132. other_thread = testutils.post_thread(self.category)
  133. response = self.client.post(self.api_link, json.dumps({
  134. 'thread_url': other_thread.get_absolute_url(),
  135. 'posts': []
  136. }), content_type="application/json")
  137. self.assertContains(response, "You have to specify at least one post to move.", status_code=400)
  138. def test_invalid_posts_data(self):
  139. """api handles invalid data"""
  140. other_thread = testutils.post_thread(self.category)
  141. response = self.client.post(self.api_link, json.dumps({
  142. 'thread_url': other_thread.get_absolute_url(),
  143. 'posts': 'string'
  144. }), content_type="application/json")
  145. self.assertContains(response, "One or more post ids received were invalid.", status_code=400)
  146. def test_invalid_posts_ids(self):
  147. """api handles invalid post id"""
  148. other_thread = testutils.post_thread(self.category)
  149. response = self.client.post(self.api_link, json.dumps({
  150. 'thread_url': other_thread.get_absolute_url(),
  151. 'posts': [1, 2, 'string']
  152. }), content_type="application/json")
  153. self.assertContains(response, "One or more post ids received were invalid.", status_code=400)
  154. def test_move_limit(self):
  155. """api rejects more posts than move limit"""
  156. other_thread = testutils.post_thread(self.category)
  157. response = self.client.post(self.api_link, json.dumps({
  158. 'thread_url': other_thread.get_absolute_url(),
  159. 'posts': list(range(MOVE_LIMIT + 1))
  160. }), content_type="application/json")
  161. self.assertContains(response, "No more than {} posts can be moved".format(MOVE_LIMIT), status_code=400)
  162. def test_move_invisible(self):
  163. """api validates posts visibility"""
  164. other_thread = testutils.post_thread(self.category)
  165. response = self.client.post(self.api_link, json.dumps({
  166. 'thread_url': other_thread.get_absolute_url(),
  167. 'posts': [
  168. testutils.reply_thread(self.thread, is_unapproved=True).pk
  169. ]
  170. }), content_type="application/json")
  171. self.assertContains(response, "One or more posts to move could not be found.", status_code=400)
  172. def test_move_event(self):
  173. """api rejects events move"""
  174. other_thread = testutils.post_thread(self.category)
  175. response = self.client.post(self.api_link, json.dumps({
  176. 'thread_url': other_thread.get_absolute_url(),
  177. 'posts': [
  178. testutils.reply_thread(self.thread, is_event=True).pk
  179. ]
  180. }), content_type="application/json")
  181. self.assertContains(response, "Events can't be moved.", status_code=400)
  182. def test_move_first_post(self):
  183. """api rejects first post move"""
  184. other_thread = testutils.post_thread(self.category)
  185. response = self.client.post(self.api_link, json.dumps({
  186. 'thread_url': other_thread.get_absolute_url(),
  187. 'posts': [
  188. self.thread.first_post_id
  189. ]
  190. }), content_type="application/json")
  191. self.assertContains(response, "You can't move thread's first post.", status_code=400)
  192. def test_move_hidden_posts(self):
  193. """api recjects attempt to move urneadable hidden post"""
  194. other_thread = testutils.post_thread(self.category)
  195. response = self.client.post(self.api_link, json.dumps({
  196. 'thread_url': other_thread.get_absolute_url(),
  197. 'posts': [
  198. testutils.reply_thread(self.thread, is_hidden=True).pk
  199. ]
  200. }), content_type="application/json")
  201. self.assertContains(response, "You can't move posts the content you can't see.", status_code=400)
  202. def test_move_posts(self):
  203. """api moves posts to other thread"""
  204. self.override_other_acl({
  205. 'can_reply_threads': 1
  206. })
  207. other_thread = testutils.post_thread(self.category_b)
  208. posts = (
  209. testutils.reply_thread(self.thread).pk,
  210. testutils.reply_thread(self.thread).pk,
  211. testutils.reply_thread(self.thread).pk,
  212. testutils.reply_thread(self.thread).pk,
  213. )
  214. self.refresh_thread()
  215. self.assertEqual(self.thread.replies, 4)
  216. response = self.client.post(self.api_link, json.dumps({
  217. 'thread_url': other_thread.get_absolute_url(),
  218. 'posts': posts
  219. }), content_type="application/json")
  220. self.assertEqual(response.status_code, 200)
  221. # replies were moved
  222. self.refresh_thread()
  223. self.assertEqual(self.thread.replies, 0)
  224. other_thread = Thread.objects.get(pk=other_thread.pk)
  225. self.assertEqual(other_thread.post_set.filter(pk__in=posts).count(), 4)
  226. self.assertEqual(other_thread.replies, 4)