test_threads_api.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. from django.urls import reverse
  2. from misago.acl.testutils import override_acl
  3. from misago.categories.models import THREADS_ROOT_NAME, Category
  4. from misago.users.testutils import AuthenticatedUserTestCase
  5. from .. import testutils
  6. from ..models import Thread
  7. from ..threadtypes import trees_map
  8. class ThreadsApiTestCase(AuthenticatedUserTestCase):
  9. def setUp(self):
  10. super(ThreadsApiTestCase, self).setUp()
  11. threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
  12. self.root = Category.objects.get(tree_id=threads_tree_id, level=0)
  13. self.category = Category.objects.get(slug='first-category')
  14. self.thread = testutils.post_thread(category=self.category)
  15. self.api_link = self.thread.get_api_url()
  16. def override_acl(self, acl=None):
  17. final_acl = self.user.acl['categories'][self.category.pk]
  18. final_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. final_acl.update(acl)
  32. visible_categories = self.user.acl['visible_categories']
  33. browseable_categories = self.user.acl['browseable_categories']
  34. if not final_acl['can_see'] and self.category.pk in visible_categories:
  35. visible_categories.remove(self.category.pk)
  36. browseable_categories.remove(self.category.pk)
  37. if not final_acl['can_browse'] and self.category.pk in browseable_categories:
  38. browseable_categories.remove(self.category.pk)
  39. override_acl(self.user, {
  40. 'visible_categories': visible_categories,
  41. 'browseable_categories': browseable_categories,
  42. 'categories': {
  43. self.category.pk: final_acl
  44. }
  45. })
  46. def get_thread_json(self):
  47. response = self.client.get(self.thread.get_api_url())
  48. self.assertEqual(response.status_code, 200)
  49. return response.json()
  50. class ThreadRetrieveApiTests(ThreadsApiTestCase):
  51. def setUp(self):
  52. super(ThreadRetrieveApiTests, self).setUp()
  53. self.tested_links = [
  54. self.api_link,
  55. '%sposts/' % self.api_link,
  56. '%sposts/?page=1' % self.api_link,
  57. ]
  58. def test_api_returns_thread(self):
  59. """api has no showstoppers"""
  60. for link in self.tested_links:
  61. self.override_acl()
  62. response = self.client.get(link)
  63. self.assertEqual(response.status_code, 200)
  64. response_json = response.json()
  65. self.assertEqual(response_json['id'], self.thread.pk)
  66. self.assertEqual(response_json['title'], self.thread.title)
  67. if 'posts' in link:
  68. self.assertIn('post_set', response_json)
  69. def test_api_shows_owned_thread(self):
  70. """api handles "owned threads only"""
  71. for link in self.tested_links:
  72. self.override_acl({
  73. 'can_see_all_threads': 0
  74. })
  75. response = self.client.get(link)
  76. self.assertEqual(response.status_code, 404)
  77. self.thread.starter = self.user
  78. self.thread.save()
  79. for link in self.tested_links:
  80. self.override_acl({
  81. 'can_see_all_threads': 0
  82. })
  83. response = self.client.get(link)
  84. self.assertEqual(response.status_code, 200)
  85. def test_api_validates_category_see_permission(self):
  86. """api validates category visiblity"""
  87. for link in self.tested_links:
  88. self.override_acl({
  89. 'can_see': 0
  90. })
  91. response = self.client.get(link)
  92. self.assertEqual(response.status_code, 404)
  93. def test_api_validates_category_browse_permission(self):
  94. """api validates category browsability"""
  95. for link in self.tested_links:
  96. self.override_acl({
  97. 'can_browse': 0
  98. })
  99. response = self.client.get(link)
  100. self.assertEqual(response.status_code, 404)
  101. def test_api_validates_posts_visibility(self):
  102. """api validates posts visiblity"""
  103. self.override_acl({
  104. 'can_hide_posts': 0
  105. })
  106. hidden_post = testutils.reply_thread(self.thread, is_hidden=True, message="I'am hidden test message!")
  107. response = self.client.get(self.tested_links[1])
  108. self.assertNotContains(response, hidden_post.parsed) # post's body is hidden
  109. # add permission to see hidden posts
  110. self.override_acl({
  111. 'can_hide_posts': 1
  112. })
  113. response = self.client.get(self.tested_links[1])
  114. self.assertContains(response, hidden_post.parsed) # hidden post's body is visible with permission
  115. self.override_acl({
  116. 'can_approve_content': 0
  117. })
  118. # unapproved posts shouldn't show at all
  119. unapproved_post = testutils.reply_thread(self.thread, is_unapproved=True)
  120. response = self.client.get(self.tested_links[1])
  121. self.assertNotContains(response, unapproved_post.get_absolute_url())
  122. # add permission to see unapproved posts
  123. self.override_acl({
  124. 'can_approve_content': 1
  125. })
  126. response = self.client.get(self.tested_links[1])
  127. self.assertContains(response, unapproved_post.get_absolute_url())
  128. class ThreadsReadApiTests(ThreadsApiTestCase):
  129. def setUp(self):
  130. super(ThreadsReadApiTests, self).setUp()
  131. self.api_link = self.category.get_read_api_url()
  132. def test_read_category_invalid_id(self):
  133. """api validates that category id is int"""
  134. api_link = '{}?category=abcd'.format(reverse('misago:api:thread-read'))
  135. response = self.client.post(api_link)
  136. self.assertEqual(response.status_code, 404)
  137. def test_read_category_nonexistant_id(self):
  138. """api validates that category for id exists"""
  139. api_link = '{}123'.format(self.api_link)
  140. response = self.client.post(api_link)
  141. self.assertEqual(response.status_code, 404)
  142. def test_read_category_no_see(self):
  143. """api validates permission to see category"""
  144. self.override_acl({
  145. 'can_see': 0
  146. })
  147. response = self.client.post(self.api_link)
  148. self.assertEqual(response.status_code, 404)
  149. def test_read_category_no_browse(self):
  150. """api validates permission to browse category"""
  151. self.override_acl({
  152. 'can_browse': 0
  153. })
  154. response = self.client.post(self.api_link)
  155. self.assertEqual(response.status_code, 403)
  156. def test_read_category(self):
  157. """api sets threads in category as read"""
  158. self.assertEqual(self.category.categoryread_set.count(), 0)
  159. response = self.client.post(self.api_link)
  160. self.assertEqual(response.status_code, 200)
  161. self.category.categoryread_set.get(user=self.user)
  162. def test_read_all(self):
  163. """api sets all threads as read"""
  164. self.assertEqual(self.root.categoryread_set.count(), 0)
  165. self.assertEqual(self.category.categoryread_set.count(), 0)
  166. response = self.client.post(self.root.get_read_api_url())
  167. self.assertEqual(response.status_code, 200)
  168. self.root.categoryread_set.get(user=self.user)
  169. self.category.categoryread_set.get(user=self.user)
  170. class ThreadDeleteApiTests(ThreadsApiTestCase):
  171. def test_delete_thread_no_permission(self):
  172. """DELETE to API link with no permission to delete fails"""
  173. self.override_acl({
  174. 'can_hide_threads': 1
  175. })
  176. response = self.client.delete(self.api_link)
  177. self.assertEqual(response.status_code, 403)
  178. self.override_acl({
  179. 'can_hide_threads': 0
  180. })
  181. response_json = response.json()
  182. self.assertEqual(response_json['detail'],
  183. "You don't have permission to delete this thread.")
  184. response = self.client.delete(self.api_link)
  185. self.assertEqual(response.status_code, 403)
  186. def test_delete_thread(self):
  187. """DELETE to API link with permission deletes thread"""
  188. self.override_acl({
  189. 'can_hide_threads': 2
  190. })
  191. response = self.client.delete(self.api_link)
  192. self.assertEqual(response.status_code, 200)
  193. with self.assertRaises(Thread.DoesNotExist):
  194. Thread.objects.get(pk=self.thread.pk)