Browse Source

Another pass on removing assertContains from API tests

Rafał Pitoń 7 years ago
parent
commit
93d0bc0e56

+ 79 - 30
misago/threads/tests/test_thread_postbulkdelete_api.py

@@ -35,22 +35,34 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.logout_user()
 
         response = self.delete(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_delete_no_data(self):
         """api handles empty data"""
         response = self.client.delete(self.api_link, content_type="application/json")
-        self.assertContains(response, "Expected a list of items", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ['Expected a list of items but got type "dict".'],
+        })
 
     def test_delete_no_ids(self):
         """api requires ids to delete"""
         response = self.delete(self.api_link)
-        self.assertContains(response, "You have to specify at least one post to delete.", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["You have to specify at least one post to delete."],
+        })
 
     def test_delete_empty_ids(self):
         """api requires ids to delete"""
         response = self.delete(self.api_link, [])
-        self.assertContains(response, "You have to specify at least one post to delete.", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["You have to specify at least one post to delete."],
+        })
 
     def test_validate_ids(self):
         """api validates that ids are list of ints"""
@@ -60,13 +72,22 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         })
 
         response = self.delete(self.api_link, True)
-        self.assertContains(response, "Expected a list of items", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ['Expected a list of items but got type "bool".'],
+        })
 
         response = self.delete(self.api_link, 'abbss')
-        self.assertContains(response, "Expected a list of items", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ['Expected a list of items but got type "str".'],
+        })
 
         response = self.delete(self.api_link, [1, 2, 3, 'a', 'b', 'x'])
-        self.assertContains(response, "One or more post ids received were invalid.", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["One or more post ids received were invalid."],
+        })
 
     def test_validate_ids_length(self):
         """api validates that ids are list of ints"""
@@ -76,7 +97,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         })
 
         response = self.delete(self.api_link, list(range(100)))
-        self.assertContains(response, "No more than 24 posts can be deleted at single time.", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["No more than 24 posts can be deleted at single time."],
+        })
 
     def test_validate_posts_exist(self):
         """api validates that ids are visible posts"""
@@ -86,7 +110,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         })
 
         response = self.delete(self.api_link, [p.id * 10 for p in self.posts])
-        self.assertContains(response, "One or more posts to delete could not be found.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "One or more posts to delete could not be found.",
+        })
 
     def test_validate_posts_visibility(self):
         """api validates that ids are visible posts"""
@@ -99,7 +126,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.posts[1].save()
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(response, "One or more posts to delete could not be found.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "One or more posts to delete could not be found.",
+        })
 
     def test_validate_posts_same_thread(self):
         """api validates that ids are same thread posts"""
@@ -112,7 +142,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.posts.append(testutils.reply_thread(other_thread, poster=self.user))
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(response, "One or more posts to delete could not be found.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "One or more posts to delete could not be found.",
+        })
 
     def test_no_permission(self):
         """api validates permission to delete"""
@@ -122,7 +155,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         })
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(response, "You can't delete posts in this category.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete posts in this category.",
+        })
 
     def test_delete_other_user_post_no_permission(self):
         """api valdiates if user can delete other users posts"""
@@ -133,9 +169,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         })
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(
-            response, "You can't delete other users posts in this category", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete other users posts in this category.",
+        })
 
     def test_delete_protected_post_no_permission(self):
         """api validates if user can delete protected post"""
@@ -149,9 +186,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.posts[0].save()
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(
-            response, "This post is protected. You can't delete it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This post is protected. You can't delete it.",
+        })
 
     def test_delete_protected_post_after_edit_time(self):
         """api validates if user can delete delete post after edit time"""
@@ -165,10 +203,11 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.posts[0].save()
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(
-            response, "You can't delete posts that are older than 1 minute.", status_code=403
-        )
-
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete posts that are older than 1 minute.",
+        })
+        
     def test_delete_post_closed_thread_no_permission(self):
         """api valdiates if user can delete posts in closed threads"""
         self.override_acl({
@@ -180,9 +219,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.thread.save()
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(
-            response, "This thread is closed. You can't delete posts in it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This thread is closed. You can't delete posts in it.",
+        })
 
     def test_delete_post_closed_category_no_permission(self):
         """api valdiates if user can delete posts in closed categories"""
@@ -195,9 +235,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.category.save()
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(
-            response, "This category is closed. You can't delete posts in it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This category is closed. You can't delete posts in it.",
+        })
 
     def test_delete_first_post(self):
         """api disallows first post's deletion"""
@@ -210,7 +251,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         ids.append(self.thread.first_post_id)
 
         response = self.delete(self.api_link, ids)
-        self.assertContains(response, "You can't delete thread's first post.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete thread's first post.",
+        })
 
     def test_delete_best_answer(self):
         """api disallows best answer deletion"""
@@ -237,7 +281,10 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         self.posts[1].save()
 
         response = self.delete(self.api_link, [p.id for p in self.posts])
-        self.assertContains(response, "You can't delete events in this category.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete events in this category.",
+        })
 
     def test_delete_owned_posts(self):
         """api deletes owned thread posts"""
@@ -250,6 +297,8 @@ class PostBulkDeleteApiTests(ThreadsApiTestCase):
         ids = [self.posts[0].id, self.posts[-1].id]
 
         response = self.delete(self.api_link, ids)
+        self.assertEqual(response.status_code, 200)
+        
         self.thread = Thread.objects.get(pk=self.thread.pk)
 
         self.assertNotEqual(self.thread.last_post_id, ids[-1])

+ 48 - 26
misago/threads/tests/test_thread_postdelete_api.py

@@ -28,14 +28,20 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         self.logout_user()
 
         response = self.client.delete(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_no_permission(self):
         """api validates permission to delete post"""
         self.override_acl({'can_hide_own_posts': 1, 'can_hide_posts': 1})
 
         response = self.client.delete(self.api_link)
-        self.assertContains(response, "You can't delete posts in this category.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete posts in this category.",
+        })
 
     def test_delete_other_user_post_no_permission(self):
         """api valdiates if user can delete other users posts"""
@@ -49,9 +55,10 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         self.post.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "You can't delete other users posts in this category", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete other users posts in this category.",
+        })
 
     def test_delete_protected_post_no_permission(self):
         """api validates if user can delete protected post"""
@@ -65,9 +72,10 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         self.post.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "This post is protected. You can't delete it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This post is protected. You can't delete it.",
+        })
 
     def test_delete_protected_post_after_edit_time(self):
         """api validates if user can delete delete post after edit time"""
@@ -81,9 +89,10 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         self.post.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "You can't delete posts that are older than 1 minute.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete posts that are older than 1 minute.",
+        })
 
     def test_delete_post_closed_thread_no_permission(self):
         """api valdiates if user can delete posts in closed threads"""
@@ -96,9 +105,10 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         self.thread.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "This thread is closed. You can't delete posts in it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This thread is closed. You can't delete posts in it.",
+        })
 
     def test_delete_post_closed_category_no_permission(self):
         """api valdiates if user can delete posts in closed categories"""
@@ -111,9 +121,10 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         self.category.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "This category is closed. You can't delete posts in it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This category is closed. You can't delete posts in it.",
+        })
 
     def test_delete_first_post(self):
         """api disallows first post deletion"""
@@ -128,7 +139,10 @@ class PostDeleteApiTests(ThreadsApiTestCase):
         )
 
         response = self.client.delete(api_link)
-        self.assertContains(response, "You can't delete thread's first post.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete thread's first post.",
+        })
 
     def test_delete_best_answer(self):
         """api disallows best answer deletion"""
@@ -193,7 +207,10 @@ class EventDeleteApiTests(ThreadsApiTestCase):
         self.logout_user()
 
         response = self.client.delete(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_no_permission(self):
         """api validates permission to delete event"""
@@ -204,7 +221,10 @@ class EventDeleteApiTests(ThreadsApiTestCase):
         })
 
         response = self.client.delete(self.api_link)
-        self.assertContains(response, "You can't delete events in this category.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't delete events in this category.",
+        })
 
     def test_delete_event_closed_thread_no_permission(self):
         """api valdiates if user can delete events in closed threads"""
@@ -217,9 +237,10 @@ class EventDeleteApiTests(ThreadsApiTestCase):
         self.thread.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "This thread is closed. You can't delete events in it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This thread is closed. You can't delete events in it.",
+        })
 
     def test_delete_event_closed_category_no_permission(self):
         """api valdiates if user can delete events in closed categories"""
@@ -232,9 +253,10 @@ class EventDeleteApiTests(ThreadsApiTestCase):
         self.category.save()
 
         response = self.client.delete(self.api_link)
-        self.assertContains(
-            response, "This category is closed. You can't delete events in it.", status_code=403
-        )
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "This category is closed. You can't delete events in it.",
+        })
 
     def test_delete_event(self):
         """api differs posts from events"""

+ 89 - 60
misago/threads/tests/test_thread_postmerge_api.py

@@ -70,42 +70,58 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             json.dumps({}),
             content_type="application/json",
         )
-        self.assertContains(response, "You can't merge posts in this thread.", status_code=403)
+        self.assertEqual(response.status_code, 403)
+        self.assertEqual(response.json(), {
+            'detail': "You can't merge posts in this thread.",
+        })
 
     def test_empty_data_json(self):
         """api handles empty json data"""
         response = self.client.post(
             self.api_link, json.dumps({}), content_type="application/json"
         )
-        self.assertContains(
-            response, "You have to select at least two posts to merge.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["You have to select at least two posts to merge."],
+        })
 
     def test_empty_data_form(self):
         """api handles empty form data"""
         response = self.client.post(self.api_link, {})
-
-        self.assertContains(
-            response, "You have to select at least two posts to merge.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["You have to select at least two posts to merge."],
+        })
 
     def test_invalid_data(self):
         """api handles post that is invalid type"""
         self.override_acl()
         response = self.client.post(self.api_link, '[]', content_type="application/json")
-        self.assertContains(response, "Invalid data. Expected a dictionary", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'non_field_errors': ["Invalid data. Expected a dictionary, but got list."],
+        })
 
         self.override_acl()
         response = self.client.post(self.api_link, '123', content_type="application/json")
-        self.assertContains(response, "Invalid data. Expected a dictionary", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'non_field_errors': ["Invalid data. Expected a dictionary, but got int."],
+        })
 
         self.override_acl()
         response = self.client.post(self.api_link, '"string"', content_type="application/json")
-        self.assertContains(response, "Invalid data. Expected a dictionary", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'non_field_errors': ["Invalid data. Expected a dictionary, but got str."],
+        })
 
         self.override_acl()
         response = self.client.post(self.api_link, 'malformed', content_type="application/json")
-        self.assertContains(response, "JSON parse error", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'detail': "JSON parse error - Expecting value: line 1 column 1 (char 0)",
+        })
 
     def test_no_posts_ids(self):
         """api rejects no posts ids"""
@@ -116,10 +132,11 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "You have to select at least two posts to merge.", status_code=400
-        )
-
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["You have to select at least two posts to merge."],
+        })
+        
     def test_invalid_posts_data(self):
         """api handles invalid data"""
         response = self.client.post(
@@ -129,9 +146,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "Expected a list of items but got type", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ['Expected a list of items but got type "str".'],
+        })
 
     def test_invalid_posts_ids(self):
         """api handles invalid post id"""
@@ -142,9 +160,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "One or more post ids received were invalid.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["One or more post ids received were invalid."],
+        })
 
     def test_one_post_id(self):
         """api rejects one post id"""
@@ -155,9 +174,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "You have to select at least two posts to merge.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["You have to select at least two posts to merge."],
+        })
 
     def test_merge_limit(self):
         """api rejects more posts than merge limit"""
@@ -168,9 +188,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "No more than {} posts can be merged".format(POSTS_LIMIT), status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["No more than {} posts can be merged at single time.".format(POSTS_LIMIT)],
+        })
 
     def test_merge_event(self):
         """api recjects events"""
@@ -183,7 +204,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(response, "Events can't be merged.", status_code=400)
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["Events can't be merged."],
+        })
 
     def test_merge_notfound_pk(self):
         """api recjects nonexistant pk's"""
@@ -194,9 +218,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "One or more posts to merge could not be found.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["One or more posts to merge could not be found."],
+        })
 
     def test_merge_cross_threads(self):
         """api recjects attempt to merge with post made in other thread"""
@@ -210,9 +235,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "One or more posts to merge could not be found.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["One or more posts to merge could not be found."],
+        })
 
     def test_merge_authenticated_with_guest_post(self):
         """api recjects attempt to merge with post made by deleted user"""
@@ -225,9 +251,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "Posts made by different users can't be merged.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["Posts made by different users can't be merged."],
+        })
 
     def test_merge_guest_with_authenticated_post(self):
         """api recjects attempt to merge with post made by deleted user"""
@@ -240,9 +267,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "Posts made by different users can't be merged.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["Posts made by different users can't be merged."],
+        })
 
     def test_merge_guest_posts_different_usernames(self):
         """api recjects attempt to merge posts made by different guests"""
@@ -256,9 +284,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "Posts made by different users can't be merged.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["Posts made by different users can't be merged."],
+        })
 
     def test_merge_different_visibility(self):
         """api recjects attempt to merge posts with different visibility"""
@@ -274,9 +303,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "Posts with different visibility can't be merged.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["Posts with different visibility can't be merged."],
+        })
 
     def test_merge_different_approval(self):
         """api recjects attempt to merge posts with different approval"""
@@ -292,9 +322,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             }),
             content_type="application/json",
         )
-        self.assertContains(
-            response, "Posts with different visibility can't be merged.", status_code=400
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["Posts with different visibility can't be merged."],
+        })
 
     def test_closed_thread(self):
         """api validates permission to merge in closed thread"""
@@ -311,11 +342,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             json.dumps({'posts': posts}),
             content_type="application/json",
         )
-        self.assertContains(
-            response,
-            "This thread is closed. You can't merge posts in it.",
-            status_code=400,
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["This thread is closed. You can't merge posts in it."],
+        })
 
         # allow closing threads
         self.override_acl({'can_close_threads': 1})
@@ -342,11 +372,10 @@ class ThreadPostMergeApiTestCase(AuthenticatedUserTestCase):
             json.dumps({'posts': posts}),
             content_type="application/json",
         )
-        self.assertContains(
-            response,
-            "This category is closed. You can't merge posts in it.",
-            status_code=400,
-        )
+        self.assertEqual(response.status_code, 400)
+        self.assertEqual(response.json(), {
+            'posts': ["This category is closed. You can't merge posts in it."],
+        })
 
         # allow closing threads
         self.override_acl({'can_close_threads': 1})