from django.contrib.auth import get_user_model from rest_framework.test import APITestCase from .models import Note, Poll, PollVote class PollAPITests(APITestCase): def setUp(self): self.author = get_user_model().objects.create_user(username='author') self.voter = get_user_model().objects.create_user(username='voter') self.other = get_user_model().objects.create_user(username='other') self.admin = get_user_model().objects.create_user(username='admin', is_staff=True) self.client.force_authenticate(self.author) def create_poll(self, **settings): payload = dict(title='Question ?', content='', kind='poll', is_public=True, recipient_ids=[], poll_settings=dict(vote_mode='single', exact_choices=1, is_anonymous=True, closes_at=None, options=['A', 'B', 'C'])) payload['poll_settings'].update(settings) response = self.client.post('/api/notes/', payload, format='json') self.assertEqual(response.status_code, 201, response.data) return response.data def vote(self, note, choices, user=None): self.client.force_authenticate(user or self.voter) return self.client.put(f"/api/notes/{note['id']}/vote/", {'option_ids': [note['poll']['options'][i]['id'] for i in choices]}, format='json') def test_single_vote_is_replaced_and_results_stay_anonymous(self): note = self.create_poll() self.assertEqual(self.vote(note, [0, 1]).status_code, 400) result = self.vote(note, [0]) self.assertEqual(result.status_code, 200, result.data) result = self.vote(note, [1]) self.assertEqual(result.data['poll']['total_voters'], 1) self.assertEqual([o['count'] for o in result.data['poll']['options']], [0, 1, 0]) for viewer in [self.author, self.admin, self.other]: self.client.force_authenticate(viewer) data = self.client.get(f"/api/notes/{note['id']}/").data['poll'] self.assertEqual(data['total_voters'], 1) self.assertFalse(any('voters' in option for option in data['options'])) def test_anonymous_api_never_contains_voter_identity(self): note = self.create_poll() self.vote(note, [0]) for viewer in [self.author, self.admin, self.other]: self.client.force_authenticate(viewer) poll = self.client.get(f"/api/notes/{note['id']}/").data['poll'] self.assertEqual(poll['my_option_ids'], []) for option in poll['options']: self.assertNotIn('voters', option) self.assertNotIn('user', option) self.assertNotIn('username', str(poll)) def test_named_multiple_votes(self): note = self.create_poll(vote_mode='multiple', is_anonymous=False) result = self.vote(note, [0, 2]) self.assertEqual(result.status_code, 200, result.data) self.assertEqual(result.data['poll']['options'][0]['voters'][0]['id'], self.voter.pk) self.assertEqual(result.data['poll']['options'][0]['percent'], 100) self.assertEqual(result.data['poll']['total_voters'], 1) def test_exact_count_and_cross_poll_option_validation(self): note = self.create_poll(vote_mode='exact', exact_choices=2) self.assertEqual(self.vote(note, [0]).status_code, 400) self.assertEqual(self.vote(note, [0, 1, 2]).status_code, 400) self.assertEqual(self.vote(note, [0, 1]).status_code, 200) self.client.force_authenticate(self.author) other = self.create_poll() self.client.force_authenticate(self.voter) result = self.client.put(f"/api/notes/{note['id']}/vote/", {'option_ids': [note['poll']['options'][0]['id'], other['poll']['options'][0]['id']]}, format='json') self.assertEqual(result.status_code, 400) def test_rules_question_and_visibility_lock_after_first_vote(self): note = self.create_poll() self.vote(note, [0]) self.client.force_authenticate(self.author) for payload in [dict(kind='info'), dict(title='Autre question'), dict(poll_settings=dict(vote_mode='single', exact_choices=1, is_anonymous=False, closes_at=None, options=['A', 'B', 'C']))]: result = self.client.patch(f"/api/notes/{note['id']}/", payload, format='json') self.assertEqual(result.status_code, 400, result.data) self.assertTrue(Poll.objects.get().is_anonymous) self.assertEqual(Note.objects.get().kind, 'poll') self.assertEqual(self.client.patch(f"/api/notes/{note['id']}/", {'content': 'Précision'}, format='json').status_code, 200) def test_closed_and_deadline_polls_reject_votes(self): note = self.create_poll() self.client.force_authenticate(self.other) self.assertEqual(self.client.post(f"/api/notes/{note['id']}/close/").status_code, 403) self.client.force_authenticate(self.author) self.assertEqual(self.client.post(f"/api/notes/{note['id']}/close/").status_code, 200) self.assertEqual(self.vote(note, [0]).status_code, 400) self.client.force_authenticate(self.author) expired = self.create_poll(closes_at='2020-01-01T00:00:00Z') self.assertEqual(self.vote(expired, [0]).status_code, 200) def test_poll_scope_and_recipients_are_always_public_for_everyone(self): note = self.create_poll() response = self.client.patch(f"/api/notes/{note['id']}/", {'is_public': False, 'recipient_ids': [self.voter.pk]}, format='json') self.assertEqual(response.status_code, 200, response.data) self.assertTrue(response.data['is_public']) self.assertEqual(response.data['recipients'], []) self.client.force_authenticate(self.other) self.assertEqual(self.client.get(f"/api/notes/{note['id']}/").status_code, 200) self.assertEqual(self.vote(note, [0], self.other).status_code, 200) def test_new_user_can_participate_in_public_poll_for_everyone(self): note = self.create_poll() new_user = get_user_model().objects.create_user(username='new') self.assertEqual(self.vote(note, [1], new_user).status_code, 200) def test_convert_existing_memo_to_poll_and_back_before_votes(self): note = Note.objects.create(author=self.author, title='Mémo') result = self.client.patch(f'/api/notes/{note.pk}/', dict(kind='poll', poll_settings=dict(options=['Oui', 'Non'])), format='json') self.assertEqual(result.status_code, 200, result.data) self.assertEqual(len(result.data['poll']['options']), 2) result = self.client.patch(f'/api/notes/{note.pk}/', dict(kind='memo'), format='json') self.assertEqual(result.status_code, 200) self.assertIsNone(result.data['poll']) self.assertFalse(Poll.objects.exists()) def test_invalid_poll_does_not_leave_note_behind(self): result = self.client.post('/api/notes/', dict(kind='poll', title='Test', poll_settings=dict(options=['A', 'A'])), format='json') self.assertEqual(result.status_code, 400) self.assertFalse(Note.objects.exists())