Browse Source

Make tests for thread start/reply/edit reply pass

rafalp 6 years ago
parent
commit
5b48ed6182

+ 70 - 95
misago/threads/tests/test_thread_editreply_api.py

@@ -4,10 +4,11 @@ from django.test.client import BOUNDARY, MULTIPART_CONTENT, encode_multipart
 from django.urls import reverse
 from django.utils import timezone
 
-from misago.acl.testutils import override_acl
+from misago.acl.test import patch_user_acl
 from misago.categories.models import Category
 from misago.threads import testutils
 from misago.threads.models import Post, Thread
+from misago.threads.test import patch_category_acl
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -27,21 +28,6 @@ class EditReplyTests(AuthenticatedUserTestCase):
             }
         )
 
-    def override_acl(self, extra_acl=None):
-        new_acl = self.user.acl_cache
-        new_acl['categories'][self.category.pk].update({
-            'can_see': 1,
-            'can_browse': 1,
-            'can_start_threads': 0,
-            'can_reply_threads': 0,
-            'can_edit_posts': 1,
-        })
-
-        if extra_acl:
-            new_acl['categories'][self.category.pk].update(extra_acl)
-
-        override_acl(self.user, new_acl)
-
     def put(self, url, data=None):
         content = encode_multipart(BOUNDARY, data or {})
         return self.client.put(url, content, content_type=MULTIPART_CONTENT)
@@ -55,32 +41,30 @@ class EditReplyTests(AuthenticatedUserTestCase):
 
     def test_thread_visibility(self):
         """thread's visibility is validated"""
-        self.override_acl({'can_see': 0})
-        response = self.put(self.api_link)
-        self.assertEqual(response.status_code, 404)
+        with patch_category_acl({'can_see': False}):
+            response = self.put(self.api_link)
+            self.assertEqual(response.status_code, 404)
 
-        self.override_acl({'can_browse': 0})
-        response = self.put(self.api_link)
-        self.assertEqual(response.status_code, 404)
+        with patch_category_acl({'can_browse': False}):
+            response = self.put(self.api_link)
+            self.assertEqual(response.status_code, 404)
 
-        self.override_acl({'can_see_all_threads': 0})
-        response = self.put(self.api_link)
-        self.assertEqual(response.status_code, 404)
+        with patch_category_acl({'can_see_all_threads': False}):
+            response = self.put(self.api_link)
+            self.assertEqual(response.status_code, 404)
 
+    @patch_category_acl({"can_edit_posts": 0})
     def test_cant_edit_reply(self):
         """permission to edit reply is validated"""
-        self.override_acl({'can_edit_posts': 0})
-
         response = self.put(self.api_link)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.json(), {
             "detail": "You can't edit posts in this category.",
         })
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_cant_edit_other_user_reply(self):
         """permission to edit reply by other users is validated"""
-        self.override_acl({'can_edit_posts': 1})
-
         self.post.poster = None
         self.post.save()
 
@@ -90,13 +74,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
             "detail": "You can't edit other users posts in this category.",
         })
 
+    @patch_category_acl({"can_edit_posts": 1, "post_edit_time": 1})
     def test_edit_too_old(self):
         """permission to edit reply within timelimit is validated"""
-        self.override_acl({
-            'can_edit_posts': 1,
-            'post_edit_time': 1,
-        })
-
         self.post.posted_on = timezone.now() - timedelta(minutes=5)
         self.post.save()
 
@@ -106,10 +86,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
             "detail": "You can't edit posts that are older than 1 minute.",
         })
 
-    def test_closed_category(self):
+    @patch_category_acl({"can_edit_posts": 1, "can_close_threads": False})
+    def test_closed_category_no_permission(self):
         """permssion to edit reply in closed category is validated"""
-        self.override_acl({'can_close_threads': 0})
-
         self.category.is_closed = True
         self.category.save()
 
@@ -119,16 +98,18 @@ class EditReplyTests(AuthenticatedUserTestCase):
             "detail": "This category is closed. You can't edit posts in it.",
         })
 
-        # allow to post in closed category
-        self.override_acl({'can_close_threads': 1})
+    @patch_category_acl({"can_edit_posts": 1, "can_close_threads": True})
+    def test_closed_category(self):
+        """permssion to edit reply in closed category is validated"""
+        self.category.is_closed = True
+        self.category.save()
 
         response = self.put(self.api_link)
         self.assertEqual(response.status_code, 400)
 
-    def test_closed_thread(self):
+    @patch_category_acl({"can_edit_posts": 1, "can_close_threads": False})
+    def test_closed_thread_no_permission(self):
         """permssion to edit reply in closed thread is validated"""
-        self.override_acl({'can_close_threads': 0})
-
         self.thread.is_closed = True
         self.thread.save()
 
@@ -138,16 +119,18 @@ class EditReplyTests(AuthenticatedUserTestCase):
             "detail": "This thread is closed. You can't edit posts in it.",
         })
 
-        # allow to post in closed thread
-        self.override_acl({'can_close_threads': 1})
+    @patch_category_acl({"can_edit_posts": 1, "can_close_threads": True})
+    def test_closed_thread(self):
+        """permssion to edit reply in closed thread is validated"""
+        self.thread.is_closed = True
+        self.thread.save()
 
         response = self.put(self.api_link)
         self.assertEqual(response.status_code, 400)
 
-    def test_protected_post(self):
+    @patch_category_acl({"can_edit_posts": 1, "can_protect_posts": False})
+    def test_protected_post_no_permission(self):
         """permssion to edit protected post is validated"""
-        self.override_acl({'can_protect_posts': 0})
-
         self.post.is_protected = True
         self.post.save()
 
@@ -157,26 +140,27 @@ class EditReplyTests(AuthenticatedUserTestCase):
             "detail": "This post is protected. You can't edit it.",
         })
 
-        # allow to post in closed thread
-        self.override_acl({'can_protect_posts': 1})
+    @patch_category_acl({"can_edit_posts": 1, "can_protect_posts": True})
+    def test_protected_post_no(self):
+        """permssion to edit protected post is validated"""
+        self.post.is_protected = True
+        self.post.save()
 
         response = self.put(self.api_link)
         self.assertEqual(response.status_code, 400)
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_empty_data(self):
         """no data sent handling has no showstoppers"""
-        self.override_acl()
-
         response = self.put(self.api_link, data={})
         self.assertEqual(response.status_code, 400)
         self.assertEqual(response.json(), {
             "post": ["You have to enter a message."],
         })
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_invalid_data(self):
         """api errors for invalid request data"""
-        self.override_acl()
-
         response = self.client.put(
             self.api_link,
             'false',
@@ -187,10 +171,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
             "non_field_errors": ["Invalid data. Expected a dictionary, but got bool."]
         })
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_edit_event(self):
         """events can't be edited"""
-        self.override_acl()
-
         self.post.is_event = True
         self.post.save()
 
@@ -200,10 +183,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
             "detail": "Events can't be edited.",
         })
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_post_is_validated(self):
         """post is validated"""
-        self.override_acl()
-
         response = self.put(
             self.api_link, data={
                 'post': "a",
@@ -216,15 +198,14 @@ class EditReplyTests(AuthenticatedUserTestCase):
             }
         )
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_edit_reply_no_change(self):
         """endpoint isn't bumping edits count if no change was made to post's body"""
-        self.override_acl()
         self.assertEqual(self.post.edits_record.count(), 0)
 
         response = self.put(self.api_link, data={'post': self.post.original})
         self.assertEqual(response.status_code, 200)
 
-        self.override_acl()
         response = self.client.get(self.thread.get_absolute_url())
         self.assertContains(response, self.post.parsed)
 
@@ -237,15 +218,14 @@ class EditReplyTests(AuthenticatedUserTestCase):
 
         self.assertEqual(self.post.edits_record.count(), 0)
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_edit_reply(self):
         """endpoint updates reply"""
-        self.override_acl()
         self.assertEqual(self.post.edits_record.count(), 0)
 
         response = self.put(self.api_link, data={'post': "This is test edit!"})
         self.assertEqual(response.status_code, 200)
 
-        self.override_acl()
         response = self.client.get(self.thread.get_absolute_url())
         self.assertContains(response, "<p>This is test edit!</p>")
 
@@ -268,10 +248,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
         self.assertEqual(post_edit.editor_name, self.user.username)
         self.assertEqual(post_edit.editor_slug, self.user.slug)
 
+    @patch_category_acl({"can_edit_posts": 2, "can_hide_threads": 1})
     def test_edit_first_post_hidden(self):
         """endpoint updates hidden thread's first post"""
-        self.override_acl({'can_hide_threads': 1, 'can_edit_posts': 2})
-
         self.thread.is_hidden = True
         self.thread.save()
         self.thread.first_post.is_hidden = True
@@ -288,10 +267,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
         response = self.put(api_link, data={'post': "This is test edit!"})
         self.assertEqual(response.status_code, 200)
 
+    @patch_category_acl({"can_edit_posts": 1, "can_protect_posts": True})
     def test_protect_post(self):
         """can protect post"""
-        self.override_acl({'can_protect_posts': 1})
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -303,10 +281,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = self.user.post_set.order_by('id').last()
         self.assertTrue(post.is_protected)
 
+    @patch_category_acl({"can_edit_posts": 1, "can_protect_posts": False})
     def test_protect_post_no_permission(self):
         """cant protect post without permission"""
-        self.override_acl({'can_protect_posts': 0})
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -318,10 +295,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = self.user.post_set.order_by('id').last()
         self.assertFalse(post.is_protected)
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_post_unicode(self):
         """unicode characters can be posted"""
-        self.override_acl()
-
         response = self.put(
             self.api_link, data={
                 'post': "Chrzążczyżewoszyce, powiat Łękółody.",
@@ -329,6 +305,7 @@ class EditReplyTests(AuthenticatedUserTestCase):
         )
         self.assertEqual(response.status_code, 200)
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_reply_category_moderation_queue(self):
         """edit sends reply to queue due to category setup"""
         self.category.require_edits_approval = True
@@ -344,10 +321,10 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = self.user.post_set.all()[:1][0]
         self.assertTrue(post.is_unapproved)
 
+    @patch_category_acl({"can_edit_posts": 1})
+    @patch_user_acl({"can_approve_content": True})
     def test_reply_category_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
-        override_acl(self.user, {'can_approve_content': 1})
-
         self.category.require_edits_approval = True
         self.category.save()
 
@@ -361,10 +338,9 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = self.user.post_set.all()[:1][0]
         self.assertFalse(post.is_unapproved)
 
+    @patch_category_acl({"can_edit_posts": 1, "require_edits_approval": True})
     def test_reply_user_moderation_queue(self):
         """edit sends reply to queue due to user acl"""
-        self.override_acl({'require_edits_approval': 1})
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -375,12 +351,13 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = self.user.post_set.all()[:1][0]
         self.assertTrue(post.is_unapproved)
 
+    @patch_category_acl({
+        "can_edit_posts": 1,
+        "require_edits_approval": True,
+    })
+    @patch_user_acl({"can_approve_content": True})
     def test_reply_user_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
-        override_acl(self.user, {'can_approve_content': 1})
-
-        self.override_acl({'require_edits_approval': 1})
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -391,17 +368,17 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = self.user.post_set.all()[:1][0]
         self.assertFalse(post.is_unapproved)
 
+    @patch_category_acl({
+        "can_edit_posts": 1,
+        "require_threads_approval": True,
+        "require_replies_approval": True,
+    })
     def test_reply_omit_other_moderation_queues(self):
         """other queues are omitted"""
         self.category.require_threads_approval = True
         self.category.require_replies_approval = True
         self.category.save()
 
-        self.override_acl({
-            'require_threads_approval': 1,
-            'require_replies_approval': 1,
-        })
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -426,6 +403,7 @@ class EditReplyTests(AuthenticatedUserTestCase):
             }
         )
 
+    @patch_category_acl({"can_edit_posts": 1})
     def test_first_reply_category_moderation_queue(self):
         """edit sends thread to queue due to category setup"""
         self.setUpFirstReplyTest()
@@ -447,12 +425,12 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = Post.objects.get(pk=self.post.pk)
         self.assertTrue(post.is_unapproved)
 
+    @patch_category_acl({"can_edit_posts": 1})
+    @patch_user_acl({'can_approve_content': True})
     def test_first_reply_category_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
         self.setUpFirstReplyTest()
 
-        override_acl(self.user, {'can_approve_content': 1})
-
         self.category.require_edits_approval = True
         self.category.save()
 
@@ -470,12 +448,11 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = Post.objects.get(pk=self.post.pk)
         self.assertFalse(post.is_unapproved)
 
+    @patch_category_acl({"can_edit_posts": 1, "require_edits_approval": True})
     def test_first_reply_user_moderation_queue(self):
         """edit sends thread to queue due to user acl"""
         self.setUpFirstReplyTest()
 
-        self.override_acl({'require_edits_approval': 1})
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -490,14 +467,12 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = Post.objects.get(pk=self.post.pk)
         self.assertTrue(post.is_unapproved)
 
+    @patch_category_acl({"can_edit_posts": 1, "require_edits_approval": True})
+    @patch_user_acl({'can_approve_content': True})
     def test_first_reply_user_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
         self.setUpFirstReplyTest()
 
-        override_acl(self.user, {'can_approve_content': 1})
-
-        self.override_acl({'require_edits_approval': 1})
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -512,6 +487,11 @@ class EditReplyTests(AuthenticatedUserTestCase):
         post = Post.objects.get(pk=self.post.pk)
         self.assertFalse(post.is_unapproved)
 
+    @patch_category_acl({
+        "can_edit_posts": 1,
+        "require_threads_approval": True,
+        "require_replies_approval": True,
+    })
     def test_first_reply_omit_other_moderation_queues(self):
         """other queues are omitted"""
         self.setUpFirstReplyTest()
@@ -520,11 +500,6 @@ class EditReplyTests(AuthenticatedUserTestCase):
         self.category.require_replies_approval = True
         self.category.save()
 
-        self.override_acl({
-            'require_threads_approval': 1,
-            'require_replies_approval': 1,
-        })
-
         response = self.put(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",

+ 42 - 59
misago/threads/tests/test_thread_reply_api.py

@@ -1,9 +1,10 @@
 from django.urls import reverse
 
-from misago.acl.testutils import override_acl
+from misago.acl.test import patch_user_acl
 from misago.categories.models import Category
 from misago.threads import testutils
 from misago.threads.models import Thread
+from misago.threads.test import patch_category_acl
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -20,20 +21,6 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
             }
         )
 
-    def override_acl(self, extra_acl=None):
-        new_acl = self.user.acl_cache
-        new_acl['categories'][self.category.pk].update({
-            'can_see': 1,
-            'can_browse': 1,
-            'can_start_threads': 0,
-            'can_reply_threads': 1,
-        })
-
-        if extra_acl:
-            new_acl['categories'][self.category.pk].update(extra_acl)
-
-        override_acl(self.user, new_acl)
-
     def test_cant_reply_thread_as_guest(self):
         """user has to be authenticated to be able to post reply"""
         self.logout_user()
@@ -43,32 +30,30 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
 
     def test_thread_visibility(self):
         """thread's visibility is validated"""
-        self.override_acl({'can_see': 0})
-        response = self.client.post(self.api_link)
-        self.assertEqual(response.status_code, 404)
+        with patch_category_acl({'can_see': 0}):
+            response = self.client.post(self.api_link)
+            self.assertEqual(response.status_code, 404)
 
-        self.override_acl({'can_browse': 0})
-        response = self.client.post(self.api_link)
-        self.assertEqual(response.status_code, 404)
+        with patch_category_acl({'can_browse': 0}):
+            response = self.client.post(self.api_link)
+            self.assertEqual(response.status_code, 404)
 
-        self.override_acl({'can_see_all_threads': 0})
-        response = self.client.post(self.api_link)
-        self.assertEqual(response.status_code, 404)
+        with patch_category_acl({'can_see_all_threads': 0}):
+            response = self.client.post(self.api_link)
+            self.assertEqual(response.status_code, 404)
 
+    @patch_category_acl({"can_reply_threads": False})
     def test_cant_reply_thread(self):
         """permission to reply thread is validated"""
-        self.override_acl({'can_reply_threads': 0})
-
         response = self.client.post(self.api_link)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.json(), {
             "detail": "You can't reply to threads in this category.",
         })
 
-    def test_closed_category(self):
+    @patch_category_acl({"can_reply_threads": True, "can_close_threads": False})
+    def test_closed_category_no_permission(self):
         """permssion to reply in closed category is validated"""
-        self.override_acl({'can_close_threads': 0})
-
         self.category.is_closed = True
         self.category.save()
 
@@ -78,16 +63,18 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
             "detail": "This category is closed. You can't reply to threads in it.",
         })
 
-        # allow to post in closed category
-        self.override_acl({'can_close_threads': 1})
+    @patch_category_acl({"can_reply_threads": True, "can_close_threads": True})
+    def test_closed_category(self):
+        """permssion to reply in closed category is validated"""
+        self.category.is_closed = True
+        self.category.save()
 
         response = self.client.post(self.api_link)
         self.assertEqual(response.status_code, 400)
 
-    def test_closed_thread(self):
+    @patch_category_acl({"can_reply_threads": True, "can_close_threads": False})
+    def test_closed_thread_no_permission(self):
         """permssion to reply in closed thread is validated"""
-        self.override_acl({'can_close_threads': 0})
-
         self.thread.is_closed = True
         self.thread.save()
 
@@ -97,26 +84,27 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
             "detail": "You can't reply to closed threads in this category.",
         })
 
-        # allow to post in closed thread
-        self.override_acl({'can_close_threads': 1})
+    @patch_category_acl({"can_reply_threads": True, "can_close_threads": True})
+    def test_closed_thread(self):
+        """permssion to reply in closed thread is validated"""
+        self.thread.is_closed = True
+        self.thread.save()
 
         response = self.client.post(self.api_link)
         self.assertEqual(response.status_code, 400)
 
+    @patch_category_acl({"can_reply_threads": True})
     def test_empty_data(self):
         """no data sent handling has no showstoppers"""
-        self.override_acl()
-
         response = self.client.post(self.api_link, data={})
         self.assertEqual(response.status_code, 400)
         self.assertEqual(response.json(), {
             "post": ["You have to enter a message."],
         })
 
+    @patch_category_acl({"can_reply_threads": True})
     def test_invalid_data(self):
         """api errors for invalid request data"""
-        self.override_acl()
-
         response = self.client.post(
             self.api_link,
             'false',
@@ -127,10 +115,9 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
             'non_field_errors': ['Invalid data. Expected a dictionary, but got bool.']
         })
 
+    @patch_category_acl({"can_reply_threads": True})
     def test_post_is_validated(self):
         """post is validated"""
-        self.override_acl()
-
         response = self.client.post(
             self.api_link, data={
                 'post': "a",
@@ -144,9 +131,9 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
             }
         )
 
+    @patch_category_acl({"can_reply_threads": True})
     def test_can_reply_thread(self):
         """endpoint creates new reply"""
-        self.override_acl()
         response = self.client.post(
             self.api_link, data={
                 'post': "This is test response!",
@@ -156,7 +143,6 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
 
         thread = Thread.objects.get(pk=self.thread.pk)
 
-        self.override_acl()
         response = self.client.get(self.thread.get_absolute_url())
         self.assertContains(response, "<p>This is test response!</p>")
 
@@ -187,10 +173,9 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.last_poster_name, self.user.username)
         self.assertEqual(category.last_poster_slug, self.user.slug)
 
+    @patch_category_acl({"can_reply_threads": True})
     def test_post_unicode(self):
         """unicode characters can be posted"""
-        self.override_acl()
-
         response = self.client.post(
             self.api_link, data={
                 'post': "Chrzążczyżewoszyce, powiat Łękółody.",
@@ -198,6 +183,7 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         )
         self.assertEqual(response.status_code, 200)
 
+    @patch_category_acl({"can_reply_threads": True})
     def test_category_moderation_queue(self):
         """reply thread in category that requires approval"""
         self.category.require_replies_approval = True
@@ -222,10 +208,10 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.threads, self.category.threads)
         self.assertEqual(category.posts, self.category.posts)
 
+    @patch_category_acl({"can_reply_threads": True})
+    @patch_user_acl({"can_approve_content": True})
     def test_category_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
-        override_acl(self.user, {'can_approve_content': 1})
-
         self.category.require_replies_approval = True
         self.category.save()
 
@@ -248,10 +234,9 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.threads, self.category.threads)
         self.assertEqual(category.posts, self.category.posts + 1)
 
+    @patch_category_acl({"can_reply_threads": True, "require_replies_approval": True})
     def test_user_moderation_queue(self):
         """reply thread by user that requires approval"""
-        self.override_acl({'require_replies_approval': 1})
-
         response = self.client.post(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -271,12 +256,10 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.threads, self.category.threads)
         self.assertEqual(category.posts, self.category.posts)
 
+    @patch_category_acl({"can_reply_threads": True, "require_replies_approval": True})
+    @patch_user_acl({"can_approve_content": True})
     def test_user_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
-        override_acl(self.user, {'can_approve_content': 1})
-
-        self.override_acl({'require_replies_approval': 1})
-
         response = self.client.post(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",
@@ -296,17 +279,17 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.threads, self.category.threads)
         self.assertEqual(category.posts, self.category.posts + 1)
 
+    @patch_category_acl({
+        "can_reply_threads": True,
+        "require_threads_approval": True,
+        "require_edits_approval": True,
+    })
     def test_omit_other_moderation_queues(self):
         """other queues are omitted"""
         self.category.require_threads_approval = True
         self.category.require_edits_approval = True
         self.category.save()
 
-        self.override_acl({
-            'require_threads_approval': 1,
-            'require_edits_approval': 1,
-        })
-
         response = self.client.post(
             self.api_link, data={
                 'post': "Lorem ipsum dolor met!",

+ 32 - 81
misago/threads/tests/test_thread_start_api.py

@@ -1,7 +1,8 @@
 from django.urls import reverse
 
-from misago.acl.testutils import override_acl
+from misago.acl.test import patch_user_acl
 from misago.categories.models import Category
+from misago.threads.test import patch_category_acl
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -12,30 +13,6 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.category = Category.objects.get(slug='first-category')
         self.api_link = reverse('misago:api:thread-list')
 
-    def override_acl(self, extra_acl=None):
-        new_acl = self.user.acl_cache
-        new_acl['categories'][self.category.pk].update({
-            'can_see': 1,
-            'can_browse': 1,
-            'can_start_threads': 1,
-            'can_pin_threads': 0,
-            'can_close_threads': 0,
-            'can_hide_threads': 0,
-            'can_hide_own_threads': 0,
-        })
-
-        if extra_acl:
-            new_acl['categories'][self.category.pk].update(extra_acl)
-
-            if 'can_see' in extra_acl and not extra_acl['can_see']:
-                new_acl['visible_categories'].remove(self.category.pk)
-                new_acl['browseable_categories'].remove(self.category.pk)
-
-            if 'can_browse' in extra_acl and not extra_acl['can_browse']:
-                new_acl['browseable_categories'].remove(self.category.pk)
-
-        override_acl(self.user, new_acl)
-
     def test_cant_start_thread_as_guest(self):
         """user has to be authenticated to be able to post thread"""
         self.logout_user()
@@ -43,10 +20,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         response = self.client.post(self.api_link)
         self.assertEqual(response.status_code, 403)
 
+    @patch_category_acl({"can_see": False})
     def test_cant_see(self):
         """has no permission to see selected category"""
-        self.override_acl({'can_see': 0})
-
         response = self.client.post(self.api_link, {
             'category': self.category.pk,
         })
@@ -57,10 +33,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             'title': ['You have to enter thread title.'],
         })
 
+    @patch_category_acl({"can_browse": False})
     def test_cant_browse(self):
         """has no permission to browse selected category"""
-        self.override_acl({'can_browse': 0})
-
         response = self.client.post(self.api_link, {
             'category': self.category.pk,
         })
@@ -71,10 +46,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             'title': ['You have to enter thread title.'],
         })
 
+    @patch_category_acl({"can_start_threads": False})
     def test_cant_start_thread(self):
         """permission to start thread in category is validated"""
-        self.override_acl({'can_start_threads': 0})
-
         response = self.client.post(self.api_link, {
             'category': self.category.pk,
         })
@@ -85,13 +59,12 @@ class StartThreadTests(AuthenticatedUserTestCase):
             'title': ['You have to enter thread title.'],
         })
 
+    @patch_category_acl({"can_start_threads": True, "can_close_threads": False})
     def test_cant_start_thread_in_locked_category(self):
         """can't post in closed category"""
         self.category.is_closed = True
         self.category.save()
 
-        self.override_acl({'can_close_threads': 0})
-
         response = self.client.post(self.api_link, {
             'category': self.category.pk,
         })
@@ -104,11 +77,6 @@ class StartThreadTests(AuthenticatedUserTestCase):
 
     def test_cant_start_thread_in_invalid_category(self):
         """can't post in invalid category"""
-        self.category.is_closed = True
-        self.category.save()
-
-        self.override_acl({'can_close_threads': 0})
-
         response = self.client.post(self.api_link, {'category': self.category.pk * 100000})
         self.assertEqual(response.status_code, 400)
         self.assertEqual(response.json(), {
@@ -120,10 +88,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             'title': ['You have to enter thread title.'],
         })
 
+    @patch_category_acl({"can_start_threads": True})
     def test_empty_data(self):
         """no data sent handling has no showstoppers"""
-        self.override_acl()
-
         response = self.client.post(self.api_link, data={})
         self.assertEqual(response.status_code, 400)
         self.assertEqual(
@@ -134,10 +101,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             }
         )
 
+    @patch_category_acl({"can_start_threads": True})
     def test_invalid_data(self):
         """api errors for invalid request data"""
-        self.override_acl()
-
         response = self.client.post(
             self.api_link,
             'false',
@@ -148,10 +114,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             'non_field_errors': ['Invalid data. Expected a dictionary, but got bool.']
         })
 
+    @patch_category_acl({"can_start_threads": True})
     def test_title_is_validated(self):
         """title is validated"""
-        self.override_acl()
-
         response = self.client.post(
             self.api_link,
             data={
@@ -168,10 +133,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             }
         )
 
+    @patch_category_acl({"can_start_threads": True})
     def test_post_is_validated(self):
         """post is validated"""
-        self.override_acl()
-
         response = self.client.post(
             self.api_link,
             data={
@@ -188,9 +152,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
             }
         )
 
+    @patch_category_acl({"can_start_threads": True})
     def test_can_start_thread(self):
         """endpoint creates new thread"""
-        self.override_acl()
         response = self.client.post(
             self.api_link,
             data={
@@ -206,7 +170,6 @@ class StartThreadTests(AuthenticatedUserTestCase):
         response_json = response.json()
         self.assertEqual(response_json['url'], thread.get_absolute_url())
 
-        self.override_acl()
         response = self.client.get(thread.get_absolute_url())
         self.assertContains(response, self.category.name)
         self.assertContains(response, thread.title)
@@ -245,10 +208,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.last_poster_name, self.user.username)
         self.assertEqual(category.last_poster_slug, self.user.slug)
 
+    @patch_category_acl({"can_start_threads": True, "can_close_threads": False})
     def test_start_closed_thread_no_permission(self):
         """permission is checked before thread is closed"""
-        self.override_acl({'can_close_threads': 0})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -263,10 +225,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertFalse(thread.is_closed)
 
+    @patch_category_acl({"can_start_threads": True, "can_close_threads": True})
     def test_start_closed_thread(self):
         """can post closed thread"""
-        self.override_acl({'can_close_threads': 1})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -281,10 +242,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertTrue(thread.is_closed)
 
+    @patch_category_acl({"can_start_threads": True, "can_pin_threads": 1})
     def test_start_unpinned_thread(self):
         """can post unpinned thread"""
-        self.override_acl({'can_pin_threads': 1})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -299,10 +259,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertEqual(thread.weight, 0)
 
+    @patch_category_acl({"can_start_threads": True, "can_pin_threads": 1})
     def test_start_locally_pinned_thread(self):
         """can post locally pinned thread"""
-        self.override_acl({'can_pin_threads': 1})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -317,10 +276,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertEqual(thread.weight, 1)
 
+    @patch_category_acl({"can_start_threads": True, "can_pin_threads": 2})
     def test_start_globally_pinned_thread(self):
         """can post globally pinned thread"""
-        self.override_acl({'can_pin_threads': 2})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -335,10 +293,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertEqual(thread.weight, 2)
 
+    @patch_category_acl({"can_start_threads": True, "can_pin_threads": 1})
     def test_start_globally_pinned_thread_no_permission(self):
         """cant post globally pinned thread without permission"""
-        self.override_acl({'can_pin_threads': 1})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -353,10 +310,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertEqual(thread.weight, 0)
 
+    @patch_category_acl({"can_start_threads": True, "can_pin_threads": 0})
     def test_start_locally_pinned_thread_no_permission(self):
         """cant post locally pinned thread without permission"""
-        self.override_acl({'can_pin_threads': 0})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -371,10 +327,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertEqual(thread.weight, 0)
 
+    @patch_category_acl({"can_start_threads": True, "can_hide_threads": 1})
     def test_start_hidden_thread(self):
         """can post hidden thread"""
-        self.override_acl({'can_hide_threads': 1})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -392,10 +347,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         category = Category.objects.get(pk=self.category.pk)
         self.assertNotEqual(category.last_thread_id, thread.id)
 
+    @patch_category_acl({"can_start_threads": True, "can_hide_threads": 0})
     def test_start_hidden_thread_no_permission(self):
         """cant post hidden thread without permission"""
-        self.override_acl({'can_hide_threads': 0})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -410,10 +364,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         thread = self.user.thread_set.all()[:1][0]
         self.assertFalse(thread.is_hidden)
 
+    @patch_category_acl({"can_start_threads": True})
     def test_post_unicode(self):
         """unicode characters can be posted"""
-        self.override_acl()
-
         response = self.client.post(
             self.api_link,
             data={
@@ -424,6 +377,7 @@ class StartThreadTests(AuthenticatedUserTestCase):
         )
         self.assertEqual(response.status_code, 200)
 
+    @patch_category_acl({"can_start_threads": True})
     def test_category_moderation_queue(self):
         """start unapproved thread in category that requires approval"""
         self.category.require_threads_approval = True
@@ -451,10 +405,10 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.posts, self.category.posts)
         self.assertFalse(category.last_thread_id == thread.id)
 
+    @patch_category_acl({"can_start_threads": True})
+    @patch_user_acl({"can_approve_content": True})
     def test_category_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
-        override_acl(self.user, {'can_approve_content': 1})
-
         self.category.require_threads_approval = True
         self.category.save()
 
@@ -480,10 +434,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.posts, self.category.posts + 1)
         self.assertEqual(category.last_thread_id, thread.id)
 
+    @patch_category_acl({"can_start_threads": True, "require_threads_approval": True})
     def test_user_moderation_queue(self):
         """start unapproved thread in category that requires approval"""
-        self.override_acl({'require_threads_approval': 1})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -506,12 +459,10 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.posts, self.category.posts)
         self.assertFalse(category.last_thread_id == thread.id)
 
+    @patch_category_acl({"can_start_threads": True, "require_threads_approval": True})
+    @patch_user_acl({"can_approve_content": True})
     def test_user_moderation_queue_bypass(self):
         """bypass moderation queue due to user's acl"""
-        override_acl(self.user, {'can_approve_content': 1})
-
-        self.override_acl({'require_threads_approval': 1})
-
         response = self.client.post(
             self.api_link,
             data={
@@ -534,17 +485,17 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(category.posts, self.category.posts + 1)
         self.assertEqual(category.last_thread_id, thread.id)
 
+    @patch_category_acl({
+        "can_start_threads": True,
+        "require_replies_approval": True,
+        "require_edits_approval": True,
+    })
     def test_omit_other_moderation_queues(self):
         """other queues are omitted"""
         self.category.require_replies_approval = True
         self.category.require_edits_approval = True
         self.category.save()
 
-        self.override_acl({
-            'require_replies_approval': 1,
-            'require_edits_approval': 1,
-        })
-
         response = self.client.post(
             self.api_link,
             data={