test_thread_merge_api.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. from django.urls import reverse
  2. from misago.acl.testutils import override_acl
  3. from misago.categories.models import Category
  4. from misago.threads import testutils
  5. from misago.threads.models import Poll, PollVote, Thread
  6. from .test_threads_api import ThreadsApiTestCase
  7. class ThreadMergeApiTests(ThreadsApiTestCase):
  8. def setUp(self):
  9. super(ThreadMergeApiTests, self).setUp()
  10. Category(
  11. name='Category B',
  12. slug='category-b',
  13. ).insert_at(self.category, position='last-child', save=True)
  14. self.category_b = Category.objects.get(slug='category-b')
  15. self.api_link = reverse('misago:api:thread-merge', kwargs={'pk': self.thread.pk})
  16. def override_other_acl(self, acl=None):
  17. other_category_acl = self.user.acl_cache['categories'][self.category.pk].copy()
  18. other_category_acl.update({
  19. 'can_see': 1,
  20. 'can_browse': 1,
  21. 'can_see_all_threads': 1,
  22. 'can_see_own_threads': 0,
  23. 'can_hide_threads': 0,
  24. 'can_approve_content': 0,
  25. 'can_edit_posts': 0,
  26. 'can_hide_posts': 0,
  27. 'can_hide_own_posts': 0,
  28. 'can_merge_threads': 0
  29. })
  30. if acl:
  31. other_category_acl.update(acl)
  32. categories_acl = self.user.acl_cache['categories']
  33. categories_acl[self.category_b.pk] = other_category_acl
  34. visible_categories = [self.category.pk]
  35. if other_category_acl['can_see']:
  36. visible_categories.append(self.category_b.pk)
  37. override_acl(self.user, {
  38. 'visible_categories': visible_categories,
  39. 'categories': categories_acl,
  40. })
  41. def test_merge_no_permission(self):
  42. """api validates if thread can be merged with other one"""
  43. self.override_acl({
  44. 'can_merge_threads': 0
  45. })
  46. response = self.client.post(self.api_link)
  47. self.assertContains(response, "You don't have permission to merge this thread with others.", status_code=403)
  48. def test_merge_no_url(self):
  49. """api validates if thread url was given"""
  50. self.override_acl({
  51. 'can_merge_threads': 1
  52. })
  53. response = self.client.post(self.api_link)
  54. self.assertContains(response, "This is not a valid thread link.", status_code=400)
  55. def test_invalid_url(self):
  56. """api validates thread url"""
  57. self.override_acl({
  58. 'can_merge_threads': 1
  59. })
  60. response = self.client.post(self.api_link, {
  61. 'thread_url': self.user.get_absolute_url()
  62. })
  63. self.assertContains(response, "This is not a valid thread link.", status_code=400)
  64. def test_current_thread_url(self):
  65. """api validates if thread url given is to current thread"""
  66. self.override_acl({
  67. 'can_merge_threads': 1
  68. })
  69. response = self.client.post(self.api_link, {
  70. 'thread_url': self.thread.get_absolute_url()
  71. })
  72. self.assertContains(response, "You can't merge thread with itself.", status_code=400)
  73. def test_other_thread_exists(self):
  74. """api validates if other thread exists"""
  75. self.override_acl({
  76. 'can_merge_threads': 1
  77. })
  78. self.override_other_acl()
  79. other_thread = testutils.post_thread(self.category_b)
  80. other_thread_url = other_thread.get_absolute_url()
  81. other_thread.delete()
  82. response = self.client.post(self.api_link, {
  83. 'thread_url': other_thread_url
  84. })
  85. self.assertContains(response, "The thread you have entered link to doesn't exist", status_code=400)
  86. def test_other_thread_is_invisible(self):
  87. """api validates if other thread is visible"""
  88. self.override_acl({
  89. 'can_merge_threads': 1
  90. })
  91. self.override_other_acl({
  92. 'can_see': 0
  93. })
  94. other_thread = testutils.post_thread(self.category_b)
  95. response = self.client.post(self.api_link, {
  96. 'thread_url': other_thread.get_absolute_url()
  97. })
  98. self.assertContains(response, "The thread you have entered link to doesn't exist", status_code=400)
  99. def test_other_thread_isnt_mergeable(self):
  100. """api validates if other thread can be merged"""
  101. self.override_acl({
  102. 'can_merge_threads': 1
  103. })
  104. self.override_other_acl({
  105. 'can_merge_threads': 0
  106. })
  107. other_thread = testutils.post_thread(self.category_b)
  108. response = self.client.post(self.api_link, {
  109. 'thread_url': other_thread.get_absolute_url()
  110. })
  111. self.assertContains(response, "You don't have permission to merge this thread", status_code=400)
  112. def test_other_thread_isnt_replyable(self):
  113. """api validates if other thread can be replied, which is condition for merg"""
  114. self.override_acl({
  115. 'can_merge_threads': 1
  116. })
  117. self.override_other_acl({
  118. 'can_reply_threads': 0
  119. })
  120. other_thread = testutils.post_thread(self.category_b)
  121. response = self.client.post(self.api_link, {
  122. 'thread_url': other_thread.get_absolute_url()
  123. })
  124. self.assertContains(response, "You can't merge this thread into thread you can't reply.", status_code=400)
  125. def test_merge_threads(self):
  126. """api merges two threads successfully"""
  127. self.override_acl({
  128. 'can_merge_threads': 1
  129. })
  130. self.override_other_acl({
  131. 'can_merge_threads': 1
  132. })
  133. other_thread = testutils.post_thread(self.category_b)
  134. response = self.client.post(self.api_link, {
  135. 'thread_url': other_thread.get_absolute_url()
  136. })
  137. self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
  138. # other thread has two posts now
  139. self.assertEqual(other_thread.post_set.count(), 3)
  140. # first thread is gone
  141. with self.assertRaises(Thread.DoesNotExist):
  142. Thread.objects.get(pk=self.thread.pk)
  143. def test_merge_threads_kept_poll(self):
  144. """api merges two threads successfully, keeping poll from old thread"""
  145. self.override_acl({
  146. 'can_merge_threads': 1
  147. })
  148. self.override_other_acl({
  149. 'can_merge_threads': 1
  150. })
  151. other_thread = testutils.post_thread(self.category_b)
  152. poll = testutils.post_poll(other_thread, self.user)
  153. response = self.client.post(self.api_link, {
  154. 'thread_url': other_thread.get_absolute_url()
  155. })
  156. self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
  157. # other thread has two posts now
  158. self.assertEqual(other_thread.post_set.count(), 3)
  159. # first thread is gone
  160. with self.assertRaises(Thread.DoesNotExist):
  161. Thread.objects.get(pk=self.thread.pk)
  162. # poll and its votes were kept
  163. self.assertEqual(Poll.objects.filter(pk=poll.pk, thread=other_thread).count(), 1)
  164. self.assertEqual(PollVote.objects.filter(poll=poll, thread=other_thread).count(), 4)
  165. def test_merge_threads_moved_poll(self):
  166. """api merges two threads successfully, moving poll from other thread"""
  167. self.override_acl({
  168. 'can_merge_threads': 1
  169. })
  170. self.override_other_acl({
  171. 'can_merge_threads': 1
  172. })
  173. other_thread = testutils.post_thread(self.category_b)
  174. poll = testutils.post_poll(self.thread, self.user)
  175. response = self.client.post(self.api_link, {
  176. 'thread_url': other_thread.get_absolute_url()
  177. })
  178. self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
  179. # other thread has two posts now
  180. self.assertEqual(other_thread.post_set.count(), 3)
  181. # first thread is gone
  182. with self.assertRaises(Thread.DoesNotExist):
  183. Thread.objects.get(pk=self.thread.pk)
  184. # poll and its votes were moved
  185. self.assertEqual(Poll.objects.filter(pk=poll.pk, thread=other_thread).count(), 1)
  186. self.assertEqual(PollVote.objects.filter(poll=poll, thread=other_thread).count(), 4)
  187. def test_threads_merge_conflict(self):
  188. """api errors on merge conflict, returning list of available polls"""
  189. self.override_acl({
  190. 'can_merge_threads': 1
  191. })
  192. self.override_other_acl({
  193. 'can_merge_threads': 1
  194. })
  195. other_thread = testutils.post_thread(self.category_b)
  196. poll = testutils.post_poll(self.thread, self.user)
  197. other_poll = testutils.post_poll(other_thread, self.user)
  198. response = self.client.post(self.api_link, {
  199. 'thread_url': other_thread.get_absolute_url()
  200. })
  201. self.assertEqual(response.status_code, 400)
  202. self.assertEqual(response.json(), {
  203. 'polls': [
  204. [0, "Delete all polls"],
  205. [poll.pk, poll.question],
  206. [other_poll.pk, other_poll.question]
  207. ]
  208. })
  209. # polls and votes were untouched
  210. self.assertEqual(Poll.objects.count(), 2)
  211. self.assertEqual(PollVote.objects.count(), 8)
  212. def test_threads_merge_conflict_invalid_resolution(self):
  213. """api errors on invalid merge conflict resolution"""
  214. self.override_acl({
  215. 'can_merge_threads': 1
  216. })
  217. self.override_other_acl({
  218. 'can_merge_threads': 1
  219. })
  220. other_thread = testutils.post_thread(self.category_b)
  221. testutils.post_poll(self.thread, self.user)
  222. testutils.post_poll(other_thread, self.user)
  223. response = self.client.post(self.api_link, {
  224. 'thread_url': other_thread.get_absolute_url(),
  225. 'poll': 'jhdkajshdsak'
  226. })
  227. self.assertEqual(response.status_code, 400)
  228. self.assertEqual(response.json(), {
  229. 'detail': "Invalid choice."
  230. })
  231. # polls and votes were untouched
  232. self.assertEqual(Poll.objects.count(), 2)
  233. self.assertEqual(PollVote.objects.count(), 8)
  234. def test_threads_merge_conflict_delete_all(self):
  235. """api deletes all polls when delete all choice is selected"""
  236. self.override_acl({
  237. 'can_merge_threads': 1
  238. })
  239. self.override_other_acl({
  240. 'can_merge_threads': 1
  241. })
  242. other_thread = testutils.post_thread(self.category_b)
  243. testutils.post_poll(self.thread, self.user)
  244. testutils.post_poll(other_thread, self.user)
  245. response = self.client.post(self.api_link, {
  246. 'thread_url': other_thread.get_absolute_url(),
  247. 'poll': 0
  248. })
  249. self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
  250. # other thread has two posts now
  251. self.assertEqual(other_thread.post_set.count(), 3)
  252. # first thread is gone
  253. with self.assertRaises(Thread.DoesNotExist):
  254. Thread.objects.get(pk=self.thread.pk)
  255. # polls and votes are gone
  256. self.assertEqual(Poll.objects.count(), 0)
  257. self.assertEqual(PollVote.objects.count(), 0)
  258. def test_threads_merge_conflict_keep_first_poll(self):
  259. """api deletes other poll on merge"""
  260. self.override_acl({
  261. 'can_merge_threads': 1
  262. })
  263. self.override_other_acl({
  264. 'can_merge_threads': 1
  265. })
  266. other_thread = testutils.post_thread(self.category_b)
  267. poll = testutils.post_poll(self.thread, self.user)
  268. other_poll = testutils.post_poll(other_thread, self.user)
  269. response = self.client.post(self.api_link, {
  270. 'thread_url': other_thread.get_absolute_url(),
  271. 'poll': poll.pk
  272. })
  273. self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
  274. # other thread has two posts now
  275. self.assertEqual(other_thread.post_set.count(), 3)
  276. # first thread is gone
  277. with self.assertRaises(Thread.DoesNotExist):
  278. Thread.objects.get(pk=self.thread.pk)
  279. # other poll and its votes are gone
  280. self.assertEqual(Poll.objects.filter(thread=self.thread).count(), 0)
  281. self.assertEqual(PollVote.objects.filter(thread=self.thread).count(), 0)
  282. self.assertEqual(Poll.objects.filter(thread=other_thread).count(), 1)
  283. self.assertEqual(PollVote.objects.filter(thread=other_thread).count(), 4)
  284. Poll.objects.get(pk=poll.pk)
  285. with self.assertRaises(Poll.DoesNotExist):
  286. Poll.objects.get(pk=other_poll.pk)
  287. def test_threads_merge_conflict_keep_other_poll(self):
  288. """api deletes first poll on merge"""
  289. self.override_acl({
  290. 'can_merge_threads': 1
  291. })
  292. self.override_other_acl({
  293. 'can_merge_threads': 1
  294. })
  295. other_thread = testutils.post_thread(self.category_b)
  296. poll = testutils.post_poll(self.thread, self.user)
  297. other_poll = testutils.post_poll(other_thread, self.user)
  298. response = self.client.post(self.api_link, {
  299. 'thread_url': other_thread.get_absolute_url(),
  300. 'poll': other_poll.pk
  301. })
  302. self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
  303. # other thread has two posts now
  304. self.assertEqual(other_thread.post_set.count(), 3)
  305. # first thread is gone
  306. with self.assertRaises(Thread.DoesNotExist):
  307. Thread.objects.get(pk=self.thread.pk)
  308. # other poll and its votes are gone
  309. self.assertEqual(Poll.objects.filter(thread=self.thread).count(), 0)
  310. self.assertEqual(PollVote.objects.filter(thread=self.thread).count(), 0)
  311. self.assertEqual(Poll.objects.filter(thread=other_thread).count(), 1)
  312. self.assertEqual(PollVote.objects.filter(thread=other_thread).count(), 4)
  313. Poll.objects.get(pk=other_poll.pk)
  314. with self.assertRaises(Poll.DoesNotExist):
  315. Poll.objects.get(pk=poll.pk)