test_threads_merge_api.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. import json
  2. from django.urls import reverse
  3. from misago.acl import add_acl
  4. from misago.categories.models import Category
  5. from misago.threads import testutils
  6. from misago.threads.api.threadendpoints.merge import MERGE_LIMIT
  7. from misago.threads.models import Poll, PollVote, Post, Thread
  8. from misago.threads.serializers import ThreadsListSerializer
  9. from .test_threads_api import ThreadsApiTestCase
  10. class ThreadsMergeApiTests(ThreadsApiTestCase):
  11. def setUp(self):
  12. super(ThreadsMergeApiTests, self).setUp()
  13. self.api_link = reverse('misago:api:thread-merge')
  14. Category(
  15. name='Category B',
  16. slug='category-b',
  17. ).insert_at(self.category, position='last-child', save=True)
  18. self.category_b = Category.objects.get(slug='category-b')
  19. def test_merge_no_threads(self):
  20. """api validates if we are trying to merge no threads"""
  21. response = self.client.post(self.api_link, content_type="application/json")
  22. self.assertEqual(response.status_code, 403)
  23. response_json = response.json()
  24. self.assertEqual(response_json, {
  25. 'detail': "You have to select at least two threads to merge."
  26. })
  27. def test_merge_empty_threads(self):
  28. """api validates if we are trying to empty threads list"""
  29. response = self.client.post(self.api_link, json.dumps({
  30. 'threads': []
  31. }), content_type="application/json")
  32. self.assertEqual(response.status_code, 403)
  33. response_json = response.json()
  34. self.assertEqual(response_json, {
  35. 'detail': "You have to select at least two threads to merge."
  36. })
  37. def test_merge_invalid_threads(self):
  38. """api validates if we are trying to merge invalid thread ids"""
  39. response = self.client.post(self.api_link, json.dumps({
  40. 'threads': 'abcd'
  41. }), content_type="application/json")
  42. self.assertEqual(response.status_code, 403)
  43. response_json = response.json()
  44. self.assertEqual(response_json, {
  45. 'detail': "One or more thread ids received were invalid."
  46. })
  47. response = self.client.post(self.api_link, json.dumps({
  48. 'threads': ['a', '-', 'c']
  49. }), content_type="application/json")
  50. self.assertEqual(response.status_code, 403)
  51. response_json = response.json()
  52. self.assertEqual(response_json, {
  53. 'detail': "One or more thread ids received were invalid."
  54. })
  55. def test_merge_single_thread(self):
  56. """api validates if we are trying to merge single thread"""
  57. response = self.client.post(self.api_link, json.dumps({
  58. 'threads': [self.thread.id]
  59. }), content_type="application/json")
  60. self.assertEqual(response.status_code, 403)
  61. response_json = response.json()
  62. self.assertEqual(response_json, {
  63. 'detail': "You have to select at least two threads to merge."
  64. })
  65. def test_merge_with_nonexisting_thread(self):
  66. """api validates if we are trying to merge with invalid thread"""
  67. testutils.post_thread(category=self.category_b)
  68. response = self.client.post(self.api_link, json.dumps({
  69. 'threads': [self.thread.id, self.thread.id + 1000]
  70. }), content_type="application/json")
  71. self.assertEqual(response.status_code, 403)
  72. response_json = response.json()
  73. self.assertEqual(response_json, {
  74. 'detail': "One or more threads to merge could not be found."
  75. })
  76. def test_merge_with_invisible_thread(self):
  77. """api validates if we are trying to merge with inaccesible thread"""
  78. unaccesible_thread = testutils.post_thread(category=self.category_b)
  79. response = self.client.post(self.api_link, json.dumps({
  80. 'threads': [self.thread.id, unaccesible_thread.id]
  81. }), content_type="application/json")
  82. self.assertEqual(response.status_code, 403)
  83. response_json = response.json()
  84. self.assertEqual(response_json, {
  85. 'detail': "One or more threads to merge could not be found."
  86. })
  87. def test_merge_no_permission(self):
  88. """api validates permission to merge threads"""
  89. thread = testutils.post_thread(category=self.category)
  90. response = self.client.post(self.api_link, json.dumps({
  91. 'threads': [self.thread.id, thread.id]
  92. }), content_type="application/json")
  93. self.assertEqual(response.status_code, 403)
  94. response_json = response.json()
  95. self.assertEqual(response_json, [
  96. {
  97. 'id': thread.pk,
  98. 'title': thread.title,
  99. 'errors': [
  100. "You don't have permission to merge this thread with others."
  101. ]
  102. },
  103. {
  104. 'id': self.thread.pk,
  105. 'title': self.thread.title,
  106. 'errors': [
  107. "You don't have permission to merge this thread with others."
  108. ]
  109. },
  110. ])
  111. def test_merge_too_many_threads(self):
  112. """api rejects too many threads to merge"""
  113. threads = []
  114. for _ in range(MERGE_LIMIT + 1):
  115. threads.append(testutils.post_thread(category=self.category).pk)
  116. self.override_acl({
  117. 'can_merge_threads': True,
  118. 'can_close_threads': False,
  119. 'can_edit_threads': False,
  120. 'can_reply_threads': False,
  121. })
  122. response = self.client.post(self.api_link, json.dumps({
  123. 'threads': threads
  124. }), content_type="application/json")
  125. self.assertEqual(response.status_code, 403)
  126. response_json = response.json()
  127. self.assertEqual(response_json, {
  128. 'detail': "No more than %s threads can be merged at single time." % MERGE_LIMIT
  129. })
  130. def test_merge_no_final_thread(self):
  131. """api rejects merge because no data to merge threads was specified"""
  132. self.override_acl({
  133. 'can_merge_threads': True,
  134. 'can_close_threads': False,
  135. 'can_edit_threads': False,
  136. 'can_reply_threads': False,
  137. })
  138. thread = testutils.post_thread(category=self.category)
  139. response = self.client.post(self.api_link, json.dumps({
  140. 'threads': [self.thread.id, thread.id]
  141. }), content_type="application/json")
  142. self.assertEqual(response.status_code, 400)
  143. response_json = response.json()
  144. self.assertEqual(response_json, {
  145. 'title': ['This field is required.'],
  146. 'category': ['This field is required.'],
  147. })
  148. def test_merge_invalid_final_title(self):
  149. """api rejects merge because final thread title was invalid"""
  150. self.override_acl({
  151. 'can_merge_threads': True,
  152. 'can_close_threads': False,
  153. 'can_edit_threads': False,
  154. 'can_reply_threads': False,
  155. })
  156. thread = testutils.post_thread(category=self.category)
  157. response = self.client.post(self.api_link, json.dumps({
  158. 'threads': [self.thread.id, thread.id],
  159. 'title': '$$$',
  160. 'category': self.category.id,
  161. }), content_type="application/json")
  162. self.assertEqual(response.status_code, 400)
  163. response_json = response.json()
  164. self.assertEqual(response_json, {
  165. 'title': ["Thread title should be at least 5 characters long (it has 3)."]
  166. })
  167. def test_merge_invalid_category(self):
  168. """api rejects merge because final category was invalid"""
  169. self.override_acl({
  170. 'can_merge_threads': True,
  171. 'can_close_threads': False,
  172. 'can_edit_threads': False,
  173. 'can_reply_threads': False,
  174. })
  175. thread = testutils.post_thread(category=self.category)
  176. response = self.client.post(self.api_link, json.dumps({
  177. 'threads': [self.thread.id, thread.id],
  178. 'title': 'Valid thread title',
  179. 'category': self.category_b.id,
  180. }), content_type="application/json")
  181. self.assertEqual(response.status_code, 400)
  182. response_json = response.json()
  183. self.assertEqual(response_json, {
  184. 'category': ["Requested category could not be found."]
  185. })
  186. def test_merge_unallowed_start_thread(self):
  187. """api rejects merge because category isn't allowing starting threads"""
  188. self.override_acl({
  189. 'can_merge_threads': True,
  190. 'can_close_threads': False,
  191. 'can_edit_threads': False,
  192. 'can_reply_threads': False,
  193. 'can_start_threads': 0
  194. })
  195. thread = testutils.post_thread(category=self.category)
  196. response = self.client.post(self.api_link, json.dumps({
  197. 'threads': [self.thread.id, thread.id],
  198. 'title': 'Valid thread title',
  199. 'category': self.category.id
  200. }), content_type="application/json")
  201. self.assertEqual(response.status_code, 400)
  202. response_json = response.json()
  203. self.assertEqual(response_json, {
  204. 'category': [
  205. "You can't create new threads in selected category."
  206. ]
  207. })
  208. def test_merge_invalid_weight(self):
  209. """api rejects merge because final weight was invalid"""
  210. self.override_acl({
  211. 'can_merge_threads': True,
  212. 'can_close_threads': False,
  213. 'can_edit_threads': False,
  214. 'can_reply_threads': False,
  215. })
  216. thread = testutils.post_thread(category=self.category)
  217. response = self.client.post(self.api_link, json.dumps({
  218. 'threads': [self.thread.id, thread.id],
  219. 'title': 'Valid thread title',
  220. 'category': self.category.id,
  221. 'weight': 4,
  222. }), content_type="application/json")
  223. self.assertEqual(response.status_code, 400)
  224. response_json = response.json()
  225. self.assertEqual(response_json, {
  226. 'weight': ["Ensure this value is less than or equal to 2."]
  227. })
  228. def test_merge_unallowed_global_weight(self):
  229. """api rejects merge because global weight was unallowed"""
  230. self.override_acl({
  231. 'can_merge_threads': True,
  232. 'can_close_threads': False,
  233. 'can_edit_threads': False,
  234. 'can_reply_threads': False,
  235. })
  236. thread = testutils.post_thread(category=self.category)
  237. response = self.client.post(self.api_link, json.dumps({
  238. 'threads': [self.thread.id, thread.id],
  239. 'title': 'Valid thread title',
  240. 'category': self.category.id,
  241. 'weight': 2,
  242. }), content_type="application/json")
  243. self.assertEqual(response.status_code, 400)
  244. response_json = response.json()
  245. self.assertEqual(response_json, {
  246. 'weight': [
  247. "You don't have permission to pin threads globally in this category."
  248. ]
  249. })
  250. def test_merge_unallowed_local_weight(self):
  251. """api rejects merge because local weight was unallowed"""
  252. self.override_acl({
  253. 'can_merge_threads': True,
  254. 'can_close_threads': False,
  255. 'can_edit_threads': False,
  256. 'can_reply_threads': False,
  257. })
  258. thread = testutils.post_thread(category=self.category)
  259. response = self.client.post(self.api_link, json.dumps({
  260. 'threads': [self.thread.id, thread.id],
  261. 'title': 'Valid thread title',
  262. 'category': self.category.id,
  263. 'weight': 1,
  264. }), content_type="application/json")
  265. self.assertEqual(response.status_code, 400)
  266. response_json = response.json()
  267. self.assertEqual(response_json, {
  268. 'weight': [
  269. "You don't have permission to pin threads in this category."
  270. ]
  271. })
  272. def test_merge_allowed_local_weight(self):
  273. """api allows local weight"""
  274. self.override_acl({
  275. 'can_merge_threads': True,
  276. 'can_close_threads': False,
  277. 'can_edit_threads': False,
  278. 'can_reply_threads': False,
  279. 'can_pin_threads': 1,
  280. })
  281. thread = testutils.post_thread(category=self.category)
  282. response = self.client.post(self.api_link, json.dumps({
  283. 'threads': [self.thread.id, thread.id],
  284. 'title': '$$$',
  285. 'category': self.category.id,
  286. 'weight': 1,
  287. }), content_type="application/json")
  288. self.assertEqual(response.status_code, 400)
  289. response_json = response.json()
  290. self.assertEqual(response_json, {
  291. 'title': ["Thread title should be at least 5 characters long (it has 3)."]
  292. })
  293. def test_merge_allowed_global_weight(self):
  294. """api allows global weight"""
  295. self.override_acl({
  296. 'can_merge_threads': True,
  297. 'can_close_threads': False,
  298. 'can_edit_threads': False,
  299. 'can_reply_threads': False,
  300. 'can_pin_threads': 2,
  301. })
  302. thread = testutils.post_thread(category=self.category)
  303. response = self.client.post(self.api_link, json.dumps({
  304. 'threads': [self.thread.id, thread.id],
  305. 'title': '$$$',
  306. 'category': self.category.id,
  307. 'weight': 2,
  308. }), content_type="application/json")
  309. self.assertEqual(response.status_code, 400)
  310. response_json = response.json()
  311. self.assertEqual(response_json, {
  312. 'title': ["Thread title should be at least 5 characters long (it has 3)."]
  313. })
  314. def test_merge_unallowed_close(self):
  315. """api rejects merge because closing thread was unallowed"""
  316. self.override_acl({
  317. 'can_merge_threads': True,
  318. 'can_close_threads': False,
  319. 'can_edit_threads': False,
  320. 'can_reply_threads': False,
  321. })
  322. thread = testutils.post_thread(category=self.category)
  323. response = self.client.post(self.api_link, json.dumps({
  324. 'threads': [self.thread.id, thread.id],
  325. 'title': 'Valid thread title',
  326. 'category': self.category.id,
  327. 'is_closed': True,
  328. }), content_type="application/json")
  329. self.assertEqual(response.status_code, 400)
  330. response_json = response.json()
  331. self.assertEqual(response_json, {
  332. 'is_closed': [
  333. "You don't have permission to close threads in this category."
  334. ]
  335. })
  336. def test_merge_with_close(self):
  337. """api allows for closing thread"""
  338. self.override_acl({
  339. 'can_merge_threads': True,
  340. 'can_edit_threads': False,
  341. 'can_reply_threads': False,
  342. 'can_close_threads': True,
  343. })
  344. thread = testutils.post_thread(category=self.category)
  345. response = self.client.post(self.api_link, json.dumps({
  346. 'threads': [self.thread.id, thread.id],
  347. 'title': '$$$',
  348. 'category': self.category.id,
  349. 'weight': 0,
  350. 'is_closed': True,
  351. }), content_type="application/json")
  352. self.assertEqual(response.status_code, 400)
  353. response_json = response.json()
  354. self.assertEqual(response_json, {
  355. 'title': ["Thread title should be at least 5 characters long (it has 3)."]
  356. })
  357. def test_merge_unallowed_hidden(self):
  358. """api rejects merge because hidden thread was unallowed"""
  359. self.override_acl({
  360. 'can_merge_threads': True,
  361. 'can_close_threads': False,
  362. 'can_edit_threads': False,
  363. 'can_reply_threads': False,
  364. 'can_hide_threads': 0,
  365. })
  366. thread = testutils.post_thread(category=self.category)
  367. response = self.client.post(self.api_link, json.dumps({
  368. 'threads': [self.thread.id, thread.id],
  369. 'title': 'Valid thread title',
  370. 'category': self.category.id,
  371. 'is_hidden': True,
  372. }), content_type="application/json")
  373. self.assertEqual(response.status_code, 400)
  374. response_json = response.json()
  375. self.assertEqual(response_json, {
  376. 'is_hidden': [
  377. "You don't have permission to hide threads in this category."
  378. ]
  379. })
  380. def test_merge_with_hide(self):
  381. """api allows for hiding thread"""
  382. self.override_acl({
  383. 'can_merge_threads': True,
  384. 'can_close_threads': False,
  385. 'can_edit_threads': False,
  386. 'can_reply_threads': False,
  387. 'can_hide_threads': 1,
  388. })
  389. thread = testutils.post_thread(category=self.category)
  390. response = self.client.post(self.api_link, json.dumps({
  391. 'threads': [self.thread.id, thread.id],
  392. 'title': '$$$',
  393. 'category': self.category.id,
  394. 'weight': 0,
  395. 'is_hidden': True,
  396. }), content_type="application/json")
  397. self.assertEqual(response.status_code, 400)
  398. response_json = response.json()
  399. self.assertEqual(response_json, {
  400. 'title': ["Thread title should be at least 5 characters long (it has 3)."]
  401. })
  402. def test_merge(self):
  403. """api performs basic merge"""
  404. posts_ids = [p.id for p in Post.objects.all()]
  405. self.override_acl({
  406. 'can_merge_threads': True,
  407. 'can_close_threads': False,
  408. 'can_edit_threads': False,
  409. 'can_reply_threads': False,
  410. })
  411. thread = testutils.post_thread(category=self.category)
  412. response = self.client.post(self.api_link, json.dumps({
  413. 'threads': [self.thread.id, thread.id],
  414. 'title': 'Merged thread!',
  415. 'category': self.category.id,
  416. }), content_type="application/json")
  417. self.assertEqual(response.status_code, 200)
  418. # is response json with new thread?
  419. response_json = response.json()
  420. new_thread = Thread.objects.get(pk=response_json['id'])
  421. new_thread.is_read = False
  422. new_thread.subscription = None
  423. new_thread.top_category = None
  424. add_acl(self.user, new_thread.category)
  425. add_acl(self.user, new_thread)
  426. self.assertEqual(response_json, ThreadsListSerializer(new_thread).data)
  427. # did posts move to new thread?
  428. for post in Post.objects.filter(id__in=posts_ids):
  429. self.assertEqual(post.thread_id, new_thread.id)
  430. # are old threads gone?
  431. self.assertEqual([t.pk for t in Thread.objects.all()], [new_thread.pk])
  432. def test_merge_kitchensink(self):
  433. """api performs merge"""
  434. posts_ids = [p.id for p in Post.objects.all()]
  435. self.override_acl({
  436. 'can_merge_threads': True,
  437. 'can_close_threads': True,
  438. 'can_hide_threads': 1,
  439. 'can_pin_threads': 2
  440. })
  441. thread = testutils.post_thread(category=self.category)
  442. response = self.client.post(self.api_link, json.dumps({
  443. 'threads': [self.thread.id, thread.id],
  444. 'title': 'Merged thread!',
  445. 'category': self.category.id,
  446. 'is_closed': 1,
  447. 'is_hidden': 1,
  448. 'weight': 2
  449. }), content_type="application/json")
  450. self.assertEqual(response.status_code, 200)
  451. # is response json with new thread?
  452. response_json = response.json()
  453. new_thread = Thread.objects.get(pk=response_json['id'])
  454. new_thread.is_read = False
  455. new_thread.subscription = None
  456. new_thread.top_category = None
  457. self.assertEqual(new_thread.weight, 2)
  458. self.assertTrue(new_thread.is_closed)
  459. self.assertTrue(new_thread.is_hidden)
  460. add_acl(self.user, new_thread.category)
  461. add_acl(self.user, new_thread)
  462. self.assertEqual(response_json, ThreadsListSerializer(new_thread).data)
  463. # did posts move to new thread?
  464. for post in Post.objects.filter(id__in=posts_ids):
  465. self.assertEqual(post.thread_id, new_thread.id)
  466. # are old threads gone?
  467. self.assertEqual([t.pk for t in Thread.objects.all()], [new_thread.pk])
  468. def test_merge_with_top_category(self):
  469. """api performs merge with top category"""
  470. posts_ids = [p.id for p in Post.objects.all()]
  471. self.override_acl({
  472. 'can_merge_threads': True,
  473. 'can_close_threads': False,
  474. 'can_edit_threads': False,
  475. 'can_reply_threads': False,
  476. })
  477. thread = testutils.post_thread(category=self.category)
  478. response = self.client.post(self.api_link, json.dumps({
  479. 'top_category': self.root.id,
  480. 'threads': [self.thread.id, thread.id],
  481. 'title': 'Merged thread!',
  482. 'category': self.category.id,
  483. }), content_type="application/json")
  484. self.assertEqual(response.status_code, 200)
  485. # is response json with new thread?
  486. response_json = response.json()
  487. new_thread = Thread.objects.get(pk=response_json['id'])
  488. new_thread.is_read = False
  489. new_thread.subscription = None
  490. new_thread.top_category = self.category
  491. add_acl(self.user, new_thread.category)
  492. add_acl(self.user, new_thread)
  493. self.assertEqual(response_json, ThreadsListSerializer(new_thread).data)
  494. # did posts move to new thread?
  495. for post in Post.objects.filter(id__in=posts_ids):
  496. self.assertEqual(post.thread_id, new_thread.id)
  497. # are old threads gone?
  498. self.assertEqual([t.pk for t in Thread.objects.all()], [new_thread.pk])
  499. def test_merge_threads_kept_poll(self):
  500. """api merges two threads successfully, keeping poll from old thread"""
  501. self.override_acl({
  502. 'can_merge_threads': True,
  503. })
  504. other_thread = testutils.post_thread(self.category)
  505. poll = testutils.post_poll(other_thread, self.user)
  506. response = self.client.post(self.api_link, json.dumps({
  507. 'threads': [self.thread.id, other_thread.id],
  508. 'title': 'Merged thread!',
  509. 'category': self.category.id,
  510. }), content_type="application/json")
  511. self.assertEqual(response.status_code, 200)
  512. response_json = response.json()
  513. new_thread = Thread.objects.get(pk=response_json['id'])
  514. # poll and its votes were kept
  515. self.assertEqual(Poll.objects.filter(pk=poll.pk, thread=new_thread).count(), 1)
  516. self.assertEqual(PollVote.objects.filter(poll=poll, thread=new_thread).count(), 4)
  517. self.assertEqual(Poll.objects.count(), 1)
  518. self.assertEqual(PollVote.objects.count(), 4)
  519. def test_merge_threads_moved_poll(self):
  520. """api merges two threads successfully, moving poll from other thread"""
  521. self.override_acl({
  522. 'can_merge_threads': True,
  523. })
  524. other_thread = testutils.post_thread(self.category)
  525. poll = testutils.post_poll(self.thread, self.user)
  526. response = self.client.post(self.api_link, json.dumps({
  527. 'threads': [self.thread.id, other_thread.id],
  528. 'title': 'Merged thread!',
  529. 'category': self.category.id,
  530. }), content_type="application/json")
  531. self.assertEqual(response.status_code, 200)
  532. response_json = response.json()
  533. new_thread = Thread.objects.get(pk=response_json['id'])
  534. # poll and its votes were kept
  535. self.assertEqual(Poll.objects.filter(pk=poll.pk, thread=new_thread).count(), 1)
  536. self.assertEqual(PollVote.objects.filter(poll=poll, thread=new_thread).count(), 4)
  537. self.assertEqual(Poll.objects.count(), 1)
  538. self.assertEqual(PollVote.objects.count(), 4)
  539. def test_threads_merge_conflict(self):
  540. """api errors on merge conflict, returning list of available polls"""
  541. self.override_acl({
  542. 'can_merge_threads': True,
  543. })
  544. other_thread = testutils.post_thread(self.category)
  545. poll = testutils.post_poll(self.thread, self.user)
  546. other_poll = testutils.post_poll(other_thread, self.user)
  547. response = self.client.post(self.api_link, json.dumps({
  548. 'threads': [self.thread.id, other_thread.id],
  549. 'title': 'Merged thread!',
  550. 'category': self.category.id,
  551. }), content_type="application/json")
  552. self.assertEqual(response.status_code, 400)
  553. self.assertEqual(response.json(), {
  554. 'polls': [
  555. [0, "Delete all polls"],
  556. [poll.pk, poll.question],
  557. [other_poll.pk, other_poll.question]
  558. ]
  559. })
  560. # polls and votes were untouched
  561. self.assertEqual(Poll.objects.count(), 2)
  562. self.assertEqual(PollVote.objects.count(), 8)
  563. def test_threads_merge_conflict_invalid_resolution(self):
  564. """api errors on invalid merge conflict resolution"""
  565. self.override_acl({
  566. 'can_merge_threads': True,
  567. })
  568. other_thread = testutils.post_thread(self.category)
  569. testutils.post_poll(self.thread, self.user)
  570. testutils.post_poll(other_thread, self.user)
  571. response = self.client.post(self.api_link, json.dumps({
  572. 'threads': [self.thread.id, other_thread.id],
  573. 'title': 'Merged thread!',
  574. 'category': self.category.id,
  575. 'poll': 'dsa7dsadsa9789'
  576. }), content_type="application/json")
  577. self.assertEqual(response.status_code, 400)
  578. self.assertEqual(response.json(), {
  579. 'detail': "Invalid choice."
  580. })
  581. # polls and votes were untouched
  582. self.assertEqual(Poll.objects.count(), 2)
  583. self.assertEqual(PollVote.objects.count(), 8)
  584. def test_threads_merge_conflict_delete_all(self):
  585. """api deletes all polls when delete all choice is selected"""
  586. self.override_acl({
  587. 'can_merge_threads': True,
  588. })
  589. other_thread = testutils.post_thread(self.category)
  590. testutils.post_poll(self.thread, self.user)
  591. testutils.post_poll(other_thread, self.user)
  592. response = self.client.post(self.api_link, json.dumps({
  593. 'threads': [self.thread.id, other_thread.id],
  594. 'title': 'Merged thread!',
  595. 'category': self.category.id,
  596. 'poll': 0
  597. }), content_type="application/json")
  598. self.assertEqual(response.status_code, 200)
  599. # polls and votes are gone
  600. self.assertEqual(Poll.objects.count(), 0)
  601. self.assertEqual(PollVote.objects.count(), 0)
  602. def test_threads_merge_conflict_keep_first_poll(self):
  603. """api deletes other poll on merge"""
  604. self.override_acl({
  605. 'can_merge_threads': True,
  606. })
  607. other_thread = testutils.post_thread(self.category)
  608. poll = testutils.post_poll(self.thread, self.user)
  609. other_poll = testutils.post_poll(other_thread, self.user)
  610. response = self.client.post(self.api_link, json.dumps({
  611. 'threads': [self.thread.id, other_thread.id],
  612. 'title': 'Merged thread!',
  613. 'category': self.category.id,
  614. 'poll': poll.pk
  615. }), content_type="application/json")
  616. self.assertEqual(response.status_code, 200)
  617. # other poll and its votes are gone
  618. self.assertEqual(Poll.objects.count(), 1)
  619. self.assertEqual(PollVote.objects.count(), 4)
  620. Poll.objects.get(pk=poll.pk)
  621. with self.assertRaises(Poll.DoesNotExist):
  622. Poll.objects.get(pk=other_poll.pk)
  623. def test_threads_merge_conflict_keep_other_poll(self):
  624. """api deletes first poll on merge"""
  625. self.override_acl({
  626. 'can_merge_threads': True,
  627. })
  628. other_thread = testutils.post_thread(self.category)
  629. poll = testutils.post_poll(self.thread, self.user)
  630. other_poll = testutils.post_poll(other_thread, self.user)
  631. response = self.client.post(self.api_link, json.dumps({
  632. 'threads': [self.thread.id, other_thread.id],
  633. 'title': 'Merged thread!',
  634. 'category': self.category.id,
  635. 'poll': other_poll.pk
  636. }), content_type="application/json")
  637. self.assertEqual(response.status_code, 200)
  638. # other poll and its votes are gone
  639. self.assertEqual(Poll.objects.count(), 1)
  640. self.assertEqual(PollVote.objects.count(), 4)
  641. Poll.objects.get(pk=other_poll.pk)
  642. with self.assertRaises(Poll.DoesNotExist):
  643. Poll.objects.get(pk=poll.pk)