|
@@ -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)
|