diff --git a/openslides/assignments/serializers.py b/openslides/assignments/serializers.py index 8b67ea46b..1f13aee6d 100644 --- a/openslides/assignments/serializers.py +++ b/openslides/assignments/serializers.py @@ -1,6 +1,7 @@ from django.db import transaction from django.utils.translation import ugettext as _ +from openslides.poll.serializers import default_votes_validator from openslides.utils.rest_api import ( DictField, IntegerField, @@ -106,6 +107,7 @@ class AssignmentAllPollSerializer(ModelSerializer): 'has_votes', 'assignment') # js-data needs the assignment-id in the nested object to define relations. read_only_fields = ('yesnoabstain',) + validators = (default_votes_validator,) def get_has_votes(self, obj): """ diff --git a/openslides/motions/serializers.py b/openslides/motions/serializers.py index 58c2ca1b4..0474b23ae 100644 --- a/openslides/motions/serializers.py +++ b/openslides/motions/serializers.py @@ -1,6 +1,7 @@ from django.db import transaction from django.utils.translation import ugettext as _ +from openslides.poll.serializers import default_votes_validator from openslides.utils.rest_api import ( CharField, DictField, @@ -115,6 +116,7 @@ class MotionPollSerializer(ModelSerializer): 'votescast', 'votes', 'has_votes') + validators = (default_votes_validator,) def get_yes(self, obj): try: diff --git a/openslides/poll/serializers.py b/openslides/poll/serializers.py new file mode 100644 index 000000000..82d4a6a6f --- /dev/null +++ b/openslides/poll/serializers.py @@ -0,0 +1,15 @@ +from django.utils.translation import ugettext as _ + +from ..utils.rest_api import ValidationError + + +def default_votes_validator(data): + """ + Use this validator in your poll serializer. It checks that the values + for the default votes (see models.CollectDefaultVotesMixin) are greater + than or equal to -2. + """ + for key in data: + if key in ('votesvalid', 'votesinvalid', 'votescast') and data[key] < -2: + raise ValidationError({'detail': _('Value for {} must not be less than -2').format(key)}) + return data diff --git a/tests/integration/assignments/test_viewset.py b/tests/integration/assignments/test_viewset.py index 75027d1f0..5c3ff649c 100644 --- a/tests/integration/assignments/test_viewset.py +++ b/tests/integration/assignments/test_viewset.py @@ -1,5 +1,6 @@ from django.contrib.auth import get_user_model from django.core.urlresolvers import reverse +from rest_framework import status from rest_framework.test import APIClient from openslides.assignments.models import Assignment @@ -273,3 +274,36 @@ class MarkElectedOtherUser(TestCase): self.assertEqual(response.status_code, 200) self.assertFalse(Assignment.objects.get(pk=self.assignment.pk).elected.filter(username='test_user_Oonei3rahji5jugh1eev').exists()) + + +class UpdateAssignmentPoll(TestCase): + """ + Tests updating polls of assignments. + """ + def setUp(self): + self.client = APIClient() + self.client.login(username='admin', password='admin') + self.assignment = Assignment.objects.create(title='test_assignment_ohneivoh9caiB8Yiungo', open_posts=1) + self.assignment.set_candidate(get_user_model().objects.get(username='admin')) + self.poll = self.assignment.create_poll() + + def test_invalid_votesvalid_value(self): + response = self.client.put( + reverse('assignmentpoll-detail', args=[self.poll.pk]), + {'assignment_id': self.assignment.pk, + 'votesvalid': '-3'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_invalid_votesinvalid_value(self): + response = self.client.put( + reverse('assignmentpoll-detail', args=[self.poll.pk]), + {'assignment_id': self.assignment.pk, + 'votesinvalid': '-3'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_invalid_votescast_value(self): + response = self.client.put( + reverse('assignmentpoll-detail', args=[self.poll.pk]), + {'assignment_id': self.assignment.pk, + 'votescast': '-3'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/tests/integration/motions/test_viewset.py b/tests/integration/motions/test_viewset.py index 3f2abff4d..81c14df24 100644 --- a/tests/integration/motions/test_viewset.py +++ b/tests/integration/motions/test_viewset.py @@ -360,3 +360,38 @@ class SetState(TestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data, {'detail': 'The state of the motion was set to submitted.'}) self.assertEqual(Motion.objects.get(pk=self.motion.pk).state.name, 'submitted') + + +class UpdateMotionPoll(TestCase): + """ + Tests updating polls of motions. + """ + def setUp(self): + self.client = APIClient() + self.client.login(username='admin', password='admin') + self.motion = Motion( + title='test_title_Aiqueigh2dae9phabiqu', + text='test_text_Neekoh3zou6li5rue8iL') + self.motion.save() + self.poll = self.motion.create_poll() + + def test_invalid_votesvalid_value(self): + response = self.client.put( + reverse('motionpoll-detail', args=[self.poll.pk]), + {'motion_id': self.motion.pk, + 'votesvalid': '-3'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_invalid_votesinvalid_value(self): + response = self.client.put( + reverse('motionpoll-detail', args=[self.poll.pk]), + {'motion_id': self.motion.pk, + 'votesinvalid': '-3'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_invalid_votescast_value(self): + response = self.client.put( + reverse('motionpoll-detail', args=[self.poll.pk]), + {'motion_id': self.motion.pk, + 'votescast': '-3'}) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)