Просмотр исходного кода

Replace assert contains with strict json comparision in more API tests.

Rafał Pitoń 7 лет назад
Родитель
Сommit
ea140f7fde

+ 90 - 15
misago/threads/tests/test_thread_merge_api.py

@@ -322,7 +322,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has two posts and an event now
         self.assertEqual(other_thread.post_set.count(), 3)
@@ -346,7 +351,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # posts reads are kept
         postreads = self.user.postread_set.filter(post__is_event=False).order_by('id')
@@ -381,7 +391,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # subscriptions are kept
         self.assertEqual(self.user.subscription_set.count(), 1)
@@ -411,7 +426,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # subscriptions are kept
         self.assertEqual(self.user.subscription_set.count(), 1)
@@ -450,7 +470,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # subscriptions are kept
         self.assertEqual(self.user.subscription_set.count(), 1)
@@ -472,7 +497,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has three posts and an event now
         self.assertEqual(other_thread.post_set.count(), 4)
@@ -501,7 +531,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has three posts and an event now
         self.assertEqual(other_thread.post_set.count(), 4)
@@ -601,7 +636,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'best_answer': 0,
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has four posts and an event now
         self.assertEqual(other_thread.post_set.count(), 5)
@@ -633,7 +673,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'best_answer': self.thread.pk,
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has four posts and an event now
         self.assertEqual(other_thread.post_set.count(), 5)
@@ -665,7 +710,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'best_answer': other_thread.pk,
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has four posts and an event now
         self.assertEqual(other_thread.post_set.count(), 5)
@@ -691,7 +741,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has two posts and an event now
         self.assertEqual(other_thread.post_set.count(), 3)
@@ -717,7 +772,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'other_thread': other_thread.get_absolute_url(),
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has two posts and an event now
         self.assertEqual(other_thread.post_set.count(), 3)
@@ -803,7 +863,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'poll': 0,
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has two posts and an event now
         self.assertEqual(other_thread.post_set.count(), 3)
@@ -831,7 +896,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'poll': poll.pk,
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has two posts and an event now
         self.assertEqual(other_thread.post_set.count(), 3)
@@ -866,7 +936,12 @@ class ThreadMergeApiTests(ThreadsApiTestCase):
                 'poll': other_poll.pk,
             }
         )
-        self.assertContains(response, other_thread.get_absolute_url(), status_code=200)
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'id': other_thread.id,
+            'title': other_thread.title,
+            'url': other_thread.get_absolute_url(),
+        })
 
         # other thread has two posts and an event now
         self.assertEqual(other_thread.post_set.count(), 3)

+ 127 - 79
misago/threads/tests/test_thread_pollcreate_api.py

@@ -1,5 +1,7 @@
 from django.urls import reverse
 
+from misago.acl import add_acl
+from misago.core.utils import serialize_datetime
 from misago.threads.models import Poll, Thread
 from misago.threads.serializers.poll import MAX_POLL_OPTIONS
 
@@ -13,6 +15,9 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
 
         response = self.post(self.api_link)
         self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This action is not available to guests.",
+        })
 
     def test_invalid_thread_id(self):
         """api validates that thread id is integer"""
@@ -24,6 +29,9 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
 
         response = self.post(api_link)
         self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.json(), {
+            'detail': "NOT FOUND",
+        })
 
     def test_nonexistant_thread_id(self):
         """api validates that thread exists"""
@@ -35,13 +43,19 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
 
         response = self.post(api_link)
         self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.json(), {
+            'detail': "No Thread matches the given query.",
+        })
 
     def test_no_permission(self):
         """api validates that user has permission to start poll in thread"""
         self.override_acl({'can_start_polls': 0})
 
         response = self.post(self.api_link)
-        self.assertContains(response, "can't start polls", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't start polls.",
+        })
 
     def test_no_permission_closed_thread(self):
         """api validates that user has permission to start poll in closed thread"""
@@ -51,7 +65,10 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
         self.thread.save()
 
         response = self.post(self.api_link)
-        self.assertContains(response, "thread is closed", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This thread is closed. You can't start polls in it.",
+        })
 
         self.override_acl(category={'can_close_threads': 1})
 
@@ -66,7 +83,10 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
         self.category.save()
 
         response = self.post(self.api_link)
-        self.assertContains(response, "category is closed", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This category is closed. You can't start polls in it.",
+        })
 
         self.override_acl(category={'can_close_threads': 1})
 
@@ -81,7 +101,10 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
         self.thread.save()
 
         response = self.post(self.api_link)
-        self.assertContains(response, "can't start polls in other users threads", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't start polls in other users threads.",
+        })
 
         self.override_acl({'can_start_polls': 2})
 
@@ -107,12 +130,21 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
         )
 
         response = self.post(self.api_link)
-        self.assertContains(response, "There's already a poll in this thread.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "There's already a poll in this thread.",
+        })
 
     def test_empty_data(self):
         """api handles empty request data"""
         response = self.post(self.api_link)
         self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': ["This field is required."],
+            'length': ["This field is required."],
+            'allowed_choices': ["This field is required."],
+        })
 
         response_json = response.json()
         self.assertEqual(len(response_json), 4)
@@ -125,11 +157,12 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
             }
         )
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(
-            response_json['length'], ["Ensure this value is greater than or equal to 0."]
-        )
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': ["This field is required."],
+            'length': ["Ensure this value is greater than or equal to 0."],
+            'allowed_choices': ["This field is required."],
+        })
 
         response = self.post(
             self.api_link, data={
@@ -137,11 +170,12 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
             }
         )
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(
-            response_json['length'], ["Ensure this value is less than or equal to 180."]
-        )
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': ["This field is required."],
+            'length': ["Ensure this value is less than or equal to 180."],
+            'allowed_choices': ["This field is required."],
+        })
 
     def test_question_validation(self):
         """api validates question length"""
@@ -151,11 +185,12 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
             }
         )
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(
-            response_json['question'], ["Ensure this field has no more than 255 characters."]
-        )
+        self.assertEqual(response.json(), {
+            'question': ["Ensure this field has no more than 255 characters."],
+            'choices': ["This field is required."],
+            'length': ["This field is required."],
+            'allowed_choices': ["This field is required."],
+        })
 
     def test_validate_choice_length(self):
         """api validates single choice length"""
@@ -170,9 +205,12 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
             }
         )
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(response_json['choices'], ["One or more poll choices are invalid."])
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': ["One or more poll choices are invalid."],
+            'length': ["This field is required."],
+            'allowed_choices': ["This field is required."],
+        })
 
         response = self.post(
             self.api_link,
@@ -186,19 +224,23 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
             }
         )
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(response_json['choices'], ["One or more poll choices are invalid."])
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': ["One or more poll choices are invalid."],
+            'length': ["This field is required."],
+            'allowed_choices': ["This field is required."],
+        })
 
     def test_validate_two_choices(self):
         """api validates that there are at least two choices in poll"""
         response = self.post(self.api_link, data={'choices': [{'label': 'Choice'}]})
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(
-            response_json['choices'], ["You need to add at least two choices to a poll."]
-        )
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': ["You need to add at least two choices to a poll."],
+            'length': ["This field is required."],
+            'allowed_choices': ["This field is required."],
+        })
 
     def test_validate_max_choices(self):
         """api validates that there are no more choices in poll than allowed number"""
@@ -214,22 +256,25 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
         self.assertEqual(response.status_code, 400)
 
         error_formats = (MAX_POLL_OPTIONS, MAX_POLL_OPTIONS + 1)
-
-        response_json = response.json()
-        self.assertEqual(
-            response_json['choices'],
-            ["You can't add more than %s options to a single poll (added %s)." % error_formats]
-        )
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': [
+                "You can't add more than %s options to a single poll (added %s)." % error_formats
+            ],
+            'length': ["This field is required."],
+            'allowed_choices': ["This field is required."],
+        })
 
     def test_allowed_choices_validation(self):
         """api validates allowed choices number"""
         response = self.post(self.api_link, data={'allowed_choices': 0})
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(
-            response_json['allowed_choices'], ["Ensure this value is greater than or equal to 1."]
-        )
+        self.assertEqual(response.json(), {
+            'question': ["This field is required."],
+            'choices': ["This field is required."],
+            'length': ["This field is required."],
+            'allowed_choices': ["Ensure this value is greater than or equal to 1."],
+        })
 
         response = self.post(
             self.api_link,
@@ -248,12 +293,11 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
             }
         )
         self.assertEqual(response.status_code, 400)
-
-        response_json = response.json()
-        self.assertEqual(
-            response_json['non_field_errors'],
-            ["Number of allowed choices can't be greater than number of all choices."]
-        )
+        self.assertEqual(response.json(), {
+            'non_field_errors': [
+                "Number of allowed choices can't be greater than number of all choices."
+            ],
+        })
 
     def test_poll_created(self):
         """api creates public poll if provided with valid data"""
@@ -279,37 +323,41 @@ class ThreadPollCreateTests(ThreadPollApiTestCase):
             }
         )
         self.assertEqual(response.status_code, 200)
-
-        response_json = response.json()
-
-        self.assertEqual(response_json['poster_name'], self.user.username)
-        self.assertEqual(response_json['length'], 40)
-        self.assertEqual(response_json['question'], "Select two best colors")
-        self.assertEqual(response_json['allowed_choices'], 2)
-        self.assertTrue(response_json['allow_revotes'])
-        self.assertEqual(response_json['votes'], 0)
-        self.assertTrue(response_json['is_public'])
-
-        self.assertEqual(len(response_json['choices']), 3)
-        self.assertEqual(len(set([c['hash'] for c in response_json['choices']])), 3)
-        self.assertEqual([c['label'] for c in response_json['choices']], ['Red', 'Green', 'Blue'])
+        self.maxDiff = None
+
+        poll = Poll.objects.all()[0]
+        add_acl(self.user, poll)
+
+        expected_choices = []
+        for choice in poll.choices:
+            expected_choices.append(choice.copy())
+            expected_choices[-1]['selected'] = False
+
+        self.assertEqual(response.json(), {
+            'id': poll.id,
+            'poster_name': self.user.username,
+            'posted_on': serialize_datetime(poll.posted_on),
+            'length': 40,
+            'question': "Select two best colors",
+            'allowed_choices': 2,
+            'allow_revotes': True,
+            'votes': 0,
+            'is_public': True,
+            'acl': poll.acl,
+            'choices': expected_choices,
+            'api': {
+                'index': poll.get_api_url(),
+                'votes': poll.get_votes_api_url(),
+            },
+            'url': {
+                'poster': self.user.get_absolute_url(),
+            },
+        })
+        
+        self.assertEqual(len(poll.choices), 3)
+        self.assertEqual(len(set([c['hash'] for c in poll.choices])), 3)
+        self.assertEqual([c['label'] for c in poll.choices], ['Red', 'Green', 'Blue'])
 
         thread = Thread.objects.get(pk=self.thread.pk)
         self.assertTrue(thread.has_poll)
-
-        poll = thread.poll
-
-        self.assertEqual(poll.category_id, self.category.id)
-        self.assertEqual(poll.thread_id, self.thread.id)
-        self.assertEqual(poll.poster_id, self.user.id)
-        self.assertEqual(poll.poster_name, self.user.username)
-        self.assertEqual(poll.poster_slug, self.user.slug)
-        self.assertEqual(poll.length, 40)
-        self.assertEqual(poll.question, "Select two best colors")
-        self.assertEqual(poll.allowed_choices, 2)
-        self.assertTrue(poll.allow_revotes)
-        self.assertEqual(poll.votes, 0)
-        self.assertTrue(poll.is_public)
-
-        self.assertEqual(len(poll.choices), 3)
-        self.assertEqual(len(set([c['hash'] for c in poll.choices])), 3)
+        self.assertEqual(thread.poll, poll)

+ 39 - 8
misago/threads/tests/test_thread_polldelete_api.py

@@ -20,6 +20,9 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
 
         response = self.client.delete(self.api_link)
         self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This action is not available to guests.",
+        })
 
     def test_invalid_thread_id(self):
         """api validates that thread id is integer"""
@@ -33,6 +36,9 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
 
         response = self.client.delete(api_link)
         self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.json(), {
+            'detail': "NOT FOUND",
+        })
 
     def test_nonexistant_thread_id(self):
         """api validates that thread exists"""
@@ -46,6 +52,9 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
 
         response = self.client.delete(api_link)
         self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.json(), {
+            'detail': "No Thread matches the given query.",
+        })
 
     def test_invalid_poll_id(self):
         """api validates that poll id is integer"""
@@ -59,6 +68,9 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
 
         response = self.client.delete(api_link)
         self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.json(), {
+            'detail': "NOT FOUND",
+        })
 
     def test_nonexistant_poll_id(self):
         """api validates that poll exists"""
@@ -72,13 +84,19 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
 
         response = self.client.delete(api_link)
         self.assertEqual(response.status_code, 404)
+        self.assertEqual(response.json(), {
+            'detail': "NOT FOUND",
+        })
 
     def test_no_permission(self):
         """api validates that user has permission to delete poll in thread"""
         self.override_acl({'can_delete_polls': 0})
 
         response = self.client.delete(self.api_link)
-        self.assertContains(response, "can't delete polls", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete polls.",
+        })
 
     def test_no_permission_timeout(self):
         """api validates that user's window to delete poll in thread has closed"""
@@ -88,9 +106,10 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
         self.poll.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "can't delete polls that are older than 5 minutes", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete polls that are older than 5 minutes.",
+        })
 
     def test_no_permission_poll_closed(self):
         """api validates that user's window to delete poll in thread has closed"""
@@ -101,7 +120,10 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
         self.poll.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(response, "This poll is over", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This poll is over. You can't delete it.",
+        })
 
     def test_no_permission_other_user_poll(self):
         """api validates that user has permission to delete other user poll in thread"""
@@ -111,7 +133,10 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
         self.poll.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(response, "can't delete other users polls", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete other users polls in this category.",
+        })
 
     def test_no_permission_closed_thread(self):
         """api validates that user has permission to delete poll in closed thread"""
@@ -121,7 +146,10 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
         self.thread.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(response, "thread is closed", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This thread is closed. You can't delete polls in it.",
+        })
 
         self.override_acl(category={'can_close_threads': 1})
 
@@ -136,7 +164,10 @@ class ThreadPollDeleteTests(ThreadPollApiTestCase):
         self.category.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(response, "category is closed", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This category is closed. You can't delete polls in it.",
+        })
 
         self.override_acl(category={'can_close_threads': 1})
 

+ 10 - 7
misago/threads/tests/test_thread_postread_api.py

@@ -29,19 +29,22 @@ class PostReadApiTests(ThreadsApiTestCase):
         self.logout_user()
 
         response = self.client.post(self.api_link)
-        self.assertContains(response, "This action is not available to guests.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This action is not available to guests.",
+        })
 
     def test_read_post(self):
         """api marks post as read"""
         response = self.client.post(self.api_link)
         self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'thread_is_read': False,
+        })
 
         self.assertEqual(self.user.postread_set.count(), 1)
         self.user.postread_set.get(post=self.post)
 
-        # one post read, first post is still unread
-        self.assertFalse(response.json()['thread_is_read'])
-
         # read second post
         response = self.client.post(reverse(
             'misago:api:thread-post-read',
@@ -51,13 +54,13 @@ class PostReadApiTests(ThreadsApiTestCase):
             }
         ))
         self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.json(), {
+            'thread_is_read': True,
+        })
 
         self.assertEqual(self.user.postread_set.count(), 2)
         self.user.postread_set.get(post=self.thread.first_post)
 
-        # both posts are read
-        self.assertTrue(response.json()['thread_is_read'])
-
     def test_read_subscribed_thread_post(self):
         """api marks post as read and updates subscription"""
         self.thread.subscription_set.create(