test_threads_bulkdelete_api.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import json
  2. from django.urls import reverse
  3. from misago.acl.testutils import override_acl
  4. from misago.categories import PRIVATE_THREADS_ROOT_NAME
  5. from misago.categories.models import Category
  6. from misago.threads import testutils
  7. from misago.threads.models import Thread
  8. from misago.threads.threadtypes import trees_map
  9. from .test_threads_api import ThreadsApiTestCase
  10. class ThreadsBulkDeleteApiTests(ThreadsApiTestCase):
  11. def setUp(self):
  12. super(ThreadsBulkDeleteApiTests, self).setUp()
  13. self.api_link = reverse('misago:api:thread-list')
  14. self.threads = [
  15. testutils.post_thread(
  16. category=self.category,
  17. poster=self.user,
  18. ),
  19. testutils.post_thread(category=self.category),
  20. testutils.post_thread(
  21. category=self.category,
  22. poster=self.user,
  23. ),
  24. ]
  25. def delete(self, url, data=None):
  26. return self.client.delete(url, json.dumps(data), content_type="application/json")
  27. def test_delete_anonymous(self):
  28. """anonymous users can't bulk delete threads"""
  29. self.logout_user()
  30. response = self.delete(self.api_link)
  31. self.assertContains(response, "This action is not available to guests.", status_code=403)
  32. def test_delete_no_ids(self):
  33. """api requires ids to delete"""
  34. self.override_acl({
  35. 'can_hide_own_threads': 0,
  36. 'can_hide_threads': 0,
  37. })
  38. response = self.delete(self.api_link)
  39. self.assertContains(response, "You have to specify at least one thread to delete.", status_code=403)
  40. def test_validate_ids(self):
  41. """api validates that ids are list of ints"""
  42. self.override_acl({
  43. 'can_hide_own_threads': 2,
  44. 'can_hide_threads': 2,
  45. })
  46. response = self.delete(self.api_link, True)
  47. self.assertContains(response, "One or more thread ids received were invalid.", status_code=403)
  48. response = self.delete(self.api_link, 'abbss')
  49. self.assertContains(response, "One or more thread ids received were invalid.", status_code=403)
  50. response = self.delete(self.api_link, [1, 2, 3, 'a', 'b', 'x'])
  51. self.assertContains(response, "One or more thread ids received were invalid.", status_code=403)
  52. def test_validate_ids_length(self):
  53. """api validates that ids are list of ints"""
  54. self.override_acl({
  55. 'can_hide_own_threads': 2,
  56. 'can_hide_threads': 2,
  57. })
  58. response = self.delete(self.api_link, list(range(100)))
  59. self.assertContains(response, "No more than 40 threads can be deleted at single time.", status_code=403)
  60. def test_validate_thread_visibility(self):
  61. """api valdiates if user can see deleted thread"""
  62. self.override_acl({
  63. 'can_hide_own_threads': 2,
  64. 'can_hide_threads': 2,
  65. })
  66. unapproved_thread = self.threads[1]
  67. unapproved_thread.is_unapproved = True
  68. unapproved_thread.save()
  69. threads_ids = [p.id for p in self.threads]
  70. response = self.delete(self.api_link, threads_ids)
  71. self.assertEqual(response.status_code, 200)
  72. self.assertEqual(response.json(), [])
  73. # unapproved thread wasn't deleted
  74. Thread.objects.get(pk=unapproved_thread.pk)
  75. deleted_threads = [self.threads[0], self.threads[2]]
  76. for thread in deleted_threads:
  77. with self.assertRaises(Thread.DoesNotExist):
  78. Thread.objects.get(pk=thread.pk)
  79. category = Category.objects.get(pk=self.category.pk)
  80. self.assertNotIn(category.last_thread_id, threads_ids)
  81. def test_delete_other_user_thread_no_permission(self):
  82. """api valdiates if user can delete other users threads"""
  83. self.override_acl({
  84. 'can_hide_own_threads': 2,
  85. 'can_hide_threads': 0,
  86. })
  87. response = self.delete(self.api_link, [p.id for p in self.threads])
  88. self.assertEqual(response.status_code, 200)
  89. self.assertEqual(response.json(), [
  90. {
  91. 'thread': {
  92. "id": self.threads[1].pk,
  93. "title": self.threads[1].title
  94. },
  95. 'error': "You don't have permission to delete this thread."
  96. }
  97. ])
  98. Thread.objects.get(pk=self.threads[1].pk)
  99. deleted_threads = [self.threads[0], self.threads[2]]
  100. for thread in deleted_threads:
  101. with self.assertRaises(Thread.DoesNotExist):
  102. Thread.objects.get(pk=thread.pk)
  103. category = Category.objects.get(pk=self.category.pk)
  104. self.assertEqual(category.last_thread_id, self.threads[1].pk)
  105. def test_delete_private_thread(self):
  106. """attempt to delete private thread fails"""
  107. private_thread = self.threads[0]
  108. private_thread.category = Category.objects.get(
  109. tree_id=trees_map.get_tree_id_for_root(PRIVATE_THREADS_ROOT_NAME),
  110. )
  111. private_thread.save()
  112. private_thread.threadparticipant_set.create(
  113. user=self.user,
  114. is_owner=True,
  115. )
  116. self.override_acl({
  117. 'can_hide_own_threads': 2,
  118. 'can_hide_threads': 2,
  119. })
  120. threads_ids = [p.id for p in self.threads]
  121. response = self.delete(self.api_link, threads_ids)
  122. self.assertEqual(response.status_code, 200)
  123. self.assertEqual(response.json(), [])
  124. Thread.objects.get(pk=private_thread.pk)
  125. deleted_threads = [self.threads[1], self.threads[2]]
  126. for thread in deleted_threads:
  127. with self.assertRaises(Thread.DoesNotExist):
  128. Thread.objects.get(pk=thread.pk)
  129. category = Category.objects.get(pk=self.category.pk)
  130. self.assertNotIn(category.last_thread_id, threads_ids)