test: обновление тестов API коммуникаций и опросов
This commit is contained in:
parent
4dae6400d5
commit
d5929e846c
File diff suppressed because it is too large
Load Diff
@ -7,149 +7,149 @@
|
||||
- auth_client
|
||||
- private_chat, group_chat, department_chat
|
||||
"""
|
||||
import pytest
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
from communications.models import Message, Poll, PollOption, PollVote
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from communications.models import Message, Poll, PollOption, PollVote
|
||||
|
||||
# Импортируем fixtures из основного файла тестов
|
||||
from .test_communications_api import (
|
||||
user1, user2, user3,
|
||||
auth_client,
|
||||
private_chat, group_chat, department_chat,
|
||||
department
|
||||
)
|
||||
from .test_communications_api import (auth_client, department, department_chat,
|
||||
group_chat, private_chat, user1, user2,
|
||||
user3)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestPollCreationAPI:
|
||||
"""Тесты создания голосований через API"""
|
||||
|
||||
|
||||
def test_create_simple_poll_via_api(self, auth_client, private_chat, user1):
|
||||
"""Создание простого голосования через POST /api/v1/communications/polls/"""
|
||||
# Сначала создаем сообщение
|
||||
message = Message.objects.create(
|
||||
chat=private_chat,
|
||||
author=user1,
|
||||
content='Poll message'
|
||||
)
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'chat_id': private_chat.id,
|
||||
'question': 'What is your favorite color?',
|
||||
'options_data': ['Red', 'Blue', 'Green'],
|
||||
'is_anonymous': False,
|
||||
'is_multiple_choice': False,
|
||||
'is_quiz': False,
|
||||
'allows_custom_answers': False
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.data['question'] == 'What is your favorite color?'
|
||||
# Author должен установиться автоматически через perform_create
|
||||
poll = Poll.objects.get(id=response.data['id'])
|
||||
assert poll.author == user1
|
||||
|
||||
def test_create_anonymous_poll_via_api(self, auth_client, private_chat, user1):
|
||||
# Проверяем что опции созданы
|
||||
assert poll.options.count() == 3
|
||||
|
||||
def test_create_anonymous_poll_via_api(
|
||||
self, auth_client, private_chat, user1
|
||||
):
|
||||
"""Создание анонимного голосования"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'chat_id': private_chat.id,
|
||||
'question': 'Anonymous poll?',
|
||||
'options_data': ['Yes', 'No'],
|
||||
'is_anonymous': True
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.data['is_anonymous'] is True
|
||||
|
||||
def test_create_multiple_choice_poll_via_api(self, auth_client, private_chat, user1):
|
||||
|
||||
def test_create_multiple_choice_poll_via_api(
|
||||
self, auth_client, private_chat, user1
|
||||
):
|
||||
"""Создание голосования с множественным выбором"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'chat_id': private_chat.id,
|
||||
'question': 'Select multiple?',
|
||||
'options_data': ['Option 1', 'Option 2', 'Option 3'],
|
||||
'is_multiple_choice': True
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.data['is_multiple_choice'] is True
|
||||
|
||||
|
||||
def test_create_quiz_via_api(self, auth_client, private_chat, user1):
|
||||
"""Создание викторины (quiz mode)"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Quiz')
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'chat_id': private_chat.id,
|
||||
'question': '2 + 2 = ?',
|
||||
'is_quiz': True
|
||||
'options_data': ['3', '4', '5'],
|
||||
'is_quiz': True,
|
||||
'correct_option_index': 1 # Правильный ответ: 4
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
assert response.data['is_quiz'] is True
|
||||
|
||||
def test_create_poll_with_auto_close_time(self, auth_client, private_chat, user1):
|
||||
|
||||
def test_create_poll_with_auto_close_time(
|
||||
self, auth_client, private_chat, user1
|
||||
):
|
||||
"""Создание голосования с автоматическим закрытием"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
closes_at = timezone.now() + timedelta(hours=24)
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'chat_id': private_chat.id,
|
||||
'question': 'Poll with deadline',
|
||||
'closes_at': closes_at.isoformat()
|
||||
'options_data': ['Yes', 'No'],
|
||||
'closes_in_minutes': 60 # Закрытие через 1 час
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
poll = Poll.objects.get(id=response.data['id'])
|
||||
assert poll.closes_at is not None
|
||||
assert poll.closes_at > timezone.now()
|
||||
|
||||
def test_create_poll_without_message_fails(self, auth_client):
|
||||
"""Попытка создать poll без сообщения должна провалиться"""
|
||||
|
||||
def test_create_poll_without_chat_id_fails(self, auth_client):
|
||||
"""Попытка создать poll без chat_id должна провалиться"""
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'question': 'Poll without message'
|
||||
'question': 'Poll without chat',
|
||||
'options_data': ['Yes', 'No']
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_create_poll_unauthenticated_fails(self, client, private_chat, user1):
|
||||
|
||||
def test_create_poll_unauthenticated_fails(
|
||||
self, client, private_chat, user1
|
||||
):
|
||||
"""Неавторизованный пользователь не может создать poll"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'question': 'Unauthorized poll'
|
||||
'chat_id': private_chat.id,
|
||||
'question': 'Unauthorized poll',
|
||||
'options_data': ['Yes', 'No']
|
||||
}
|
||||
|
||||
|
||||
response = client.post(url, data, format='json')
|
||||
|
||||
assert response.status_code in [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN]
|
||||
|
||||
assert response.status_code in [
|
||||
status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestPollVoting:
|
||||
"""Тесты голосования"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def simple_poll(self, private_chat, user1):
|
||||
"""Простое голосование с опциями"""
|
||||
@ -164,78 +164,80 @@ class TestPollVoting:
|
||||
opt1 = PollOption.objects.create(poll=poll, text='Yes', position=0)
|
||||
opt2 = PollOption.objects.create(poll=poll, text='No', position=1)
|
||||
return poll, opt1, opt2
|
||||
|
||||
|
||||
def test_vote_single_choice(self, auth_client, simple_poll, user1):
|
||||
"""Голосование в single-choice опросе"""
|
||||
poll, opt1, opt2 = simple_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt1.id]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert response.data['total_voters'] == 1
|
||||
assert opt1.id in response.data['user_voted_option_ids']
|
||||
|
||||
results = response.data['results']
|
||||
assert results['total_voters'] == 1
|
||||
assert opt1.id in results['user_voted_option_ids']
|
||||
|
||||
def test_vote_multiple_in_single_choice_fails(self, auth_client, simple_poll):
|
||||
"""Попытка выбрать несколько в single-choice"""
|
||||
poll, opt1, opt2 = simple_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt1.id, opt2.id]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
def test_revote_replaces_previous(self, auth_client, simple_poll):
|
||||
"""Повторное голосование заменяет предыдущий выбор"""
|
||||
poll, opt1, opt2 = simple_poll
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
|
||||
|
||||
# Первое голосование
|
||||
auth_client.post(url, {'option_ids': [opt1.id]}, format='json')
|
||||
|
||||
|
||||
# Второе голосование
|
||||
response = auth_client.post(url, {'option_ids': [opt2.id]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert opt2.id in response.data['user_voted_option_ids']
|
||||
assert opt1.id not in response.data['user_voted_option_ids']
|
||||
results = response.data['results']
|
||||
assert opt2.id in results['user_voted_option_ids']
|
||||
assert opt1.id not in results['user_voted_option_ids']
|
||||
# Должен остаться 1 голосующий
|
||||
assert response.data['total_voters'] == 1
|
||||
|
||||
assert results['total_voters'] == 1
|
||||
|
||||
def test_vote_in_closed_poll_fails(self, auth_client, simple_poll):
|
||||
"""Голосование в закрытом опросе должно провалиться"""
|
||||
poll, opt1, opt2 = simple_poll
|
||||
poll.is_closed = True
|
||||
poll.save()
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt1.id]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
def test_vote_without_options_fails(self, auth_client, simple_poll):
|
||||
"""Голосование без выбора опций должно провалиться"""
|
||||
poll, opt1, opt2 = simple_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': []}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
def test_vote_with_invalid_option_fails(self, auth_client, simple_poll):
|
||||
"""Голосование с несуществующей опцией должно провалиться"""
|
||||
poll, opt1, opt2 = simple_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [99999]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestMultipleChoicePolls:
|
||||
"""Тесты голосований с множественным выбором"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multiple_choice_poll(self, private_chat, user1):
|
||||
"""Голосование с multiple choice"""
|
||||
@ -250,54 +252,63 @@ class TestMultipleChoicePolls:
|
||||
opt2 = PollOption.objects.create(poll=poll, text='B', position=1)
|
||||
opt3 = PollOption.objects.create(poll=poll, text='C', position=2)
|
||||
return poll, opt1, opt2, opt3
|
||||
|
||||
|
||||
def test_vote_multiple_options(self, auth_client, multiple_choice_poll):
|
||||
"""Можно выбрать несколько опций"""
|
||||
poll, opt1, opt2, opt3 = multiple_choice_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt1.id, opt2.id]}, format='json')
|
||||
|
||||
response = auth_client.post(
|
||||
url, {'option_ids': [opt1.id, opt2.id]}, format='json'
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.data['user_voted_option_ids']) == 2
|
||||
assert opt1.id in response.data['user_voted_option_ids']
|
||||
assert opt2.id in response.data['user_voted_option_ids']
|
||||
|
||||
results = response.data['results']
|
||||
assert len(results['user_voted_option_ids']) == 2
|
||||
assert opt1.id in results['user_voted_option_ids']
|
||||
assert opt2.id in results['user_voted_option_ids']
|
||||
|
||||
def test_vote_all_options(self, auth_client, multiple_choice_poll):
|
||||
"""Можно выбрать все опции"""
|
||||
poll, opt1, opt2, opt3 = multiple_choice_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(
|
||||
url,
|
||||
{'option_ids': [opt1.id, opt2.id, opt3.id]},
|
||||
format='json'
|
||||
)
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert len(response.data['user_voted_option_ids']) == 3
|
||||
|
||||
results = response.data['results']
|
||||
assert len(results['user_voted_option_ids']) == 3
|
||||
|
||||
def test_revote_multiple_choice(self, auth_client, multiple_choice_poll):
|
||||
"""Повторное голосование заменяет все предыдущие выборы"""
|
||||
poll, opt1, opt2, opt3 = multiple_choice_poll
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
|
||||
|
||||
# Первое: выбираем A и B
|
||||
auth_client.post(url, {'option_ids': [opt1.id, opt2.id]}, format='json')
|
||||
|
||||
auth_client.post(
|
||||
url, {'option_ids': [opt1.id, opt2.id]}, format='json'
|
||||
)
|
||||
|
||||
# Второе: выбираем только C
|
||||
response = auth_client.post(url, {'option_ids': [opt3.id]}, format='json')
|
||||
|
||||
response = auth_client.post(
|
||||
url, {'option_ids': [opt3.id]}, format='json'
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert opt3.id in response.data['user_voted_option_ids']
|
||||
assert opt1.id not in response.data['user_voted_option_ids']
|
||||
assert opt2.id not in response.data['user_voted_option_ids']
|
||||
results = response.data['results']
|
||||
assert opt3.id in results['user_voted_option_ids']
|
||||
assert opt1.id not in results['user_voted_option_ids']
|
||||
assert opt2.id not in results['user_voted_option_ids']
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestAnonymousPolls:
|
||||
"""Тесты анонимных голосований"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def anonymous_poll(self, private_chat, user1):
|
||||
"""Анонимное голосование"""
|
||||
@ -311,27 +322,27 @@ class TestAnonymousPolls:
|
||||
opt1 = PollOption.objects.create(poll=poll, text='Yes', position=0)
|
||||
opt2 = PollOption.objects.create(poll=poll, text='No', position=1)
|
||||
return poll, opt1, opt2
|
||||
|
||||
|
||||
def test_vote_in_anonymous_poll(self, auth_client, anonymous_poll):
|
||||
"""Можно голосовать в анонимном опросе"""
|
||||
poll, opt1, opt2 = anonymous_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt1.id]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
def test_anonymous_results_hide_voters(self, auth_client, anonymous_poll, user1):
|
||||
"""Результаты анонимного опроса не показывают голосующих"""
|
||||
poll, opt1, opt2 = anonymous_poll
|
||||
|
||||
|
||||
# Голосуем
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
auth_client.post(url, {'option_ids': [opt1.id]}, format='json')
|
||||
|
||||
|
||||
# Получаем результаты
|
||||
results = poll.get_results()
|
||||
|
||||
|
||||
# Проверяем что voters пустой для анонимного опроса
|
||||
for option_result in results['options']:
|
||||
assert option_result['voters'] == []
|
||||
@ -340,7 +351,7 @@ class TestAnonymousPolls:
|
||||
@pytest.mark.django_db
|
||||
class TestQuizPolls:
|
||||
"""Тесты викторин (quiz mode)"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def quiz_poll(self, private_chat, user1):
|
||||
"""Викторина с правильным ответом"""
|
||||
@ -355,31 +366,31 @@ class TestQuizPolls:
|
||||
opt2 = PollOption.objects.create(poll=poll, text='4', position=1, is_correct=True)
|
||||
opt3 = PollOption.objects.create(poll=poll, text='5', position=2, is_correct=False)
|
||||
return poll, opt1, opt2, opt3
|
||||
|
||||
|
||||
def test_vote_correct_answer(self, auth_client, quiz_poll):
|
||||
"""Голосование за правильный ответ"""
|
||||
poll, opt1, opt2, opt3 = quiz_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt2.id]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
def test_vote_wrong_answer(self, auth_client, quiz_poll):
|
||||
"""Голосование за неправильный ответ"""
|
||||
poll, opt1, opt2, opt3 = quiz_poll
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt1.id]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
|
||||
def test_quiz_results_show_correct_answer(self, auth_client, quiz_poll):
|
||||
"""Результаты викторины показывают правильный ответ"""
|
||||
poll, opt1, opt2, opt3 = quiz_poll
|
||||
|
||||
|
||||
results = poll.get_results()
|
||||
|
||||
|
||||
# Находим правильный ответ
|
||||
correct_option = next(
|
||||
opt for opt in results['options']
|
||||
@ -391,7 +402,7 @@ class TestQuizPolls:
|
||||
@pytest.mark.django_db
|
||||
class TestPollClosing:
|
||||
"""Тесты закрытия голосований"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def poll_with_votes(self, private_chat, user1, user2):
|
||||
"""Голосование с голосами"""
|
||||
@ -403,54 +414,54 @@ class TestPollClosing:
|
||||
)
|
||||
opt1 = PollOption.objects.create(poll=poll, text='Yes', position=0)
|
||||
opt2 = PollOption.objects.create(poll=poll, text='No', position=1)
|
||||
|
||||
|
||||
# Добавляем голоса
|
||||
PollVote.objects.create(poll=poll, option=opt1, voter=user1)
|
||||
poll.total_voters = 1
|
||||
poll.save()
|
||||
|
||||
|
||||
return poll, opt1, opt2
|
||||
|
||||
|
||||
def test_close_poll_by_author(self, auth_client, poll_with_votes, user1):
|
||||
"""Автор может закрыть голосование"""
|
||||
poll, opt1, opt2 = poll_with_votes
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/close/'
|
||||
response = auth_client.post(url)
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
poll.refresh_from_db()
|
||||
assert poll.is_closed is True
|
||||
assert poll.closed_at is not None
|
||||
|
||||
|
||||
def test_close_poll_by_non_author_fails(self, auth_client, poll_with_votes, user2):
|
||||
"""Не-автор не может закрыть голосование"""
|
||||
poll, opt1, opt2 = poll_with_votes
|
||||
|
||||
|
||||
# Логинимся как user2
|
||||
auth_client.force_authenticate(user=user2)
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/close/'
|
||||
response = auth_client.post(url)
|
||||
|
||||
|
||||
assert response.status_code in [status.HTTP_403_FORBIDDEN, status.HTTP_400_BAD_REQUEST]
|
||||
|
||||
|
||||
def test_close_already_closed_poll(self, auth_client, poll_with_votes):
|
||||
"""Попытка закрыть уже закрытый опрос"""
|
||||
poll, opt1, opt2 = poll_with_votes
|
||||
poll.is_closed = True
|
||||
poll.save()
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/close/'
|
||||
response = auth_client.post(url)
|
||||
|
||||
|
||||
# Должно быть успешно или 400 (опрос уже закрыт)
|
||||
assert response.status_code in [status.HTTP_200_OK, status.HTTP_400_BAD_REQUEST]
|
||||
|
||||
|
||||
def test_poll_auto_closes_after_deadline(self, private_chat, user1):
|
||||
"""Голосование автоматически закрывается после дедлайна"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
|
||||
|
||||
# Создаем poll с прошедшим дедлайном
|
||||
past_time = timezone.now() - timedelta(hours=1)
|
||||
poll = Poll.objects.create(
|
||||
@ -459,22 +470,22 @@ class TestPollClosing:
|
||||
question='Expired poll',
|
||||
closes_at=past_time
|
||||
)
|
||||
|
||||
|
||||
# В реальном приложении должна быть celery task которая закроет
|
||||
# Здесь проверяем что closes_at установлен
|
||||
assert poll.closes_at < timezone.now()
|
||||
|
||||
|
||||
# Можно вручную проверить логику закрытия
|
||||
if poll.closes_at and poll.closes_at < timezone.now() and not poll.is_closed:
|
||||
poll.close()
|
||||
|
||||
|
||||
assert poll.is_closed is True
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestPollResults:
|
||||
"""Тесты получения результатов"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def poll_with_results(self, private_chat, user1, user2):
|
||||
"""Голосование с результатами"""
|
||||
@ -487,57 +498,57 @@ class TestPollResults:
|
||||
)
|
||||
opt1 = PollOption.objects.create(poll=poll, text='A', position=0)
|
||||
opt2 = PollOption.objects.create(poll=poll, text='B', position=1)
|
||||
|
||||
|
||||
# user1 голосует за A
|
||||
PollVote.objects.create(poll=poll, option=opt1, voter=user1)
|
||||
opt1.vote_count = 1
|
||||
opt1.save()
|
||||
|
||||
|
||||
# user2 голосует за A
|
||||
PollVote.objects.create(poll=poll, option=opt1, voter=user2)
|
||||
opt1.vote_count = 2
|
||||
opt1.save()
|
||||
|
||||
|
||||
poll.total_voters = 2
|
||||
poll.save()
|
||||
|
||||
|
||||
return poll, opt1, opt2
|
||||
|
||||
|
||||
def test_get_poll_results_via_api(self, auth_client, poll_with_results):
|
||||
"""Получение результатов через API"""
|
||||
poll, opt1, opt2 = poll_with_results
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/results/'
|
||||
response = auth_client.post(url)
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
# Проверяем структуру ответа
|
||||
assert 'question' in response.data
|
||||
assert 'total_voters' in response.data
|
||||
assert 'options' in response.data
|
||||
|
||||
|
||||
def test_results_calculate_percentages(self, poll_with_results):
|
||||
"""Результаты правильно считают проценты"""
|
||||
poll, opt1, opt2 = poll_with_results
|
||||
|
||||
|
||||
results = poll.get_results()
|
||||
|
||||
|
||||
# opt1: 2 голоса из 2 = 100%
|
||||
opt1_result = next(o for o in results['options'] if o['id'] == opt1.id)
|
||||
assert opt1_result['percentage'] == 100.0
|
||||
|
||||
|
||||
# opt2: 0 голосов из 2 = 0%
|
||||
opt2_result = next(o for o in results['options'] if o['id'] == opt2.id)
|
||||
assert opt2_result['percentage'] == 0.0
|
||||
|
||||
|
||||
def test_results_show_voters_when_not_anonymous(self, poll_with_results, user1, user2):
|
||||
"""Результаты показывают голосующих в неанонимных опросах"""
|
||||
poll, opt1, opt2 = poll_with_results
|
||||
|
||||
|
||||
results = poll.get_results()
|
||||
|
||||
|
||||
opt1_result = next(o for o in results['options'] if o['id'] == opt1.id)
|
||||
|
||||
|
||||
# Должно быть 2 голосующих
|
||||
assert len(opt1_result['voters']) == 2
|
||||
voter_ids = [v['voter__id'] for v in opt1_result['voters']]
|
||||
@ -548,52 +559,53 @@ class TestPollResults:
|
||||
@pytest.mark.django_db
|
||||
class TestPollEdgeCases:
|
||||
"""Тесты граничных случаев"""
|
||||
|
||||
|
||||
def test_poll_with_empty_question_fails(self, auth_client, private_chat, user1):
|
||||
"""Голосование с пустым вопросом должно провалиться"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'question': ''
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
|
||||
def test_poll_with_very_long_question(self, auth_client, private_chat, user1):
|
||||
"""Голосование с очень длинным вопросом (> 500 символов)"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
|
||||
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'question': 'A' * 501 # Больше чем max_length=500
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_poll_without_options_can_be_created(self, auth_client, private_chat, user1):
|
||||
"""Можно создать голосование без опций (опции добавляются отдельно)"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
|
||||
|
||||
def test_poll_without_options_can_be_created(
|
||||
self, auth_client, private_chat, user1
|
||||
):
|
||||
"""Можно создать голосование без опций (options_data пустой)"""
|
||||
url = '/api/v1/communications/polls/'
|
||||
data = {
|
||||
'message': message.id,
|
||||
'question': 'Poll without options'
|
||||
'chat_id': private_chat.id,
|
||||
'question': 'Poll without options',
|
||||
'options_data': []
|
||||
}
|
||||
|
||||
|
||||
response = auth_client.post(url, data, format='json')
|
||||
|
||||
|
||||
# Создание должно быть успешным
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
poll = Poll.objects.get(id=response.data['id'])
|
||||
assert poll.options.count() == 0
|
||||
|
||||
|
||||
def test_vote_in_poll_without_options_fails(self, auth_client, private_chat, user1):
|
||||
"""Голосование в опросе без опций должно провалиться"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
@ -602,15 +614,19 @@ class TestPollEdgeCases:
|
||||
author=user1,
|
||||
question='No options'
|
||||
)
|
||||
|
||||
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [999]}, format='json')
|
||||
|
||||
|
||||
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_total_voters_count_unique_users(self, auth_client, private_chat, user1):
|
||||
|
||||
def test_total_voters_count_unique_users(
|
||||
self, auth_client, private_chat, user1
|
||||
):
|
||||
"""total_voters считает уникальных пользователей, не голоса"""
|
||||
message = Message.objects.create(chat=private_chat, author=user1, content='Poll')
|
||||
message = Message.objects.create(
|
||||
chat=private_chat, author=user1, content='Poll'
|
||||
)
|
||||
poll = Poll.objects.create(
|
||||
message=message,
|
||||
author=user1,
|
||||
@ -619,10 +635,17 @@ class TestPollEdgeCases:
|
||||
)
|
||||
opt1 = PollOption.objects.create(poll=poll, text='A', position=0)
|
||||
opt2 = PollOption.objects.create(poll=poll, text='B', position=1)
|
||||
|
||||
|
||||
# Один user голосует за 2 опции
|
||||
url = f'/api/v1/communications/polls/{poll.id}/vote/'
|
||||
response = auth_client.post(url, {'option_ids': [opt1.id, opt2.id]}, format='json')
|
||||
|
||||
response = auth_client.post(
|
||||
url, {'option_ids': [opt1.id, opt2.id]}, format='json'
|
||||
)
|
||||
|
||||
# total_voters должен быть 1, не 2
|
||||
assert response.data['total_voters'] == 1
|
||||
assert response.data['results']['total_voters'] == 1
|
||||
url, {'option_ids': [opt1.id, opt2.id]}, format='json'
|
||||
)
|
||||
|
||||
# total_voters должен быть 1, не 2
|
||||
assert response.data['results']['total_voters'] == 1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user