196 lines
7.2 KiB
Python
196 lines
7.2 KiB
Python
"""
|
||
Тесты для helper_bot.handlers.admin.utils.
|
||
"""
|
||
|
||
from unittest.mock import AsyncMock, MagicMock, patch
|
||
|
||
import pytest
|
||
from helper_bot.handlers.admin.exceptions import AdminError
|
||
from helper_bot.handlers.admin.utils import (escape_html,
|
||
format_ban_confirmation,
|
||
format_user_info,
|
||
handle_admin_error,
|
||
return_to_admin_menu)
|
||
|
||
|
||
@pytest.mark.unit
|
||
class TestEscapeHtml:
|
||
"""Тесты для escape_html."""
|
||
|
||
def test_empty_string(self):
|
||
"""Пустая строка возвращает пустую строку."""
|
||
assert escape_html("") == ""
|
||
|
||
def test_none_returns_empty(self):
|
||
"""None возвращает пустую строку."""
|
||
assert escape_html(None) == ""
|
||
|
||
def test_plain_text_unchanged(self):
|
||
"""Обычный текст не меняется."""
|
||
assert escape_html("Hello world") == "Hello world"
|
||
|
||
def test_escaping_angle_brackets(self):
|
||
"""Экранирование < и >."""
|
||
assert escape_html("<script>") == "<script>"
|
||
|
||
def test_escaping_ampersand(self):
|
||
"""Экранирование &."""
|
||
assert escape_html("a & b") == "a & b"
|
||
|
||
def test_escaping_quotes(self):
|
||
"""Экранирование кавычек."""
|
||
assert escape_html('"test"') == ""test""
|
||
|
||
|
||
@pytest.mark.unit
|
||
class TestFormatUserInfo:
|
||
"""Тесты для format_user_info."""
|
||
|
||
def test_formats_all_fields(self):
|
||
"""Все поля подставляются и экранируются."""
|
||
result = format_user_info(
|
||
user_id=123,
|
||
username="user_name",
|
||
full_name="Иван Иванов",
|
||
)
|
||
assert "123" in result
|
||
assert "user_name" in result
|
||
assert "Иван Иванов" in result
|
||
assert "<b>Выбран пользователь:</b>" in result
|
||
assert "<b>ID:</b>" in result
|
||
assert "<b>Username:</b>" in result
|
||
assert "<b>Имя:</b>" in result
|
||
|
||
def test_escapes_username_and_full_name(self):
|
||
"""username и full_name экранируются через escape_html."""
|
||
result = format_user_info(
|
||
user_id=1,
|
||
username="<script>",
|
||
full_name="<b>Bold</b>",
|
||
)
|
||
assert "<script>" in result
|
||
assert "<b>Bold</b>" in result
|
||
|
||
|
||
@pytest.mark.unit
|
||
class TestFormatBanConfirmation:
|
||
"""Тесты для format_ban_confirmation."""
|
||
|
||
def test_ban_forever(self):
|
||
"""При ban_days=None отображается 'Навсегда'."""
|
||
result = format_ban_confirmation(user_id=456, reason="Спам", ban_days=None)
|
||
assert "Навсегда" in result
|
||
assert "456" in result
|
||
assert "Спам" in result
|
||
assert "<b>Необходимо подтверждение:</b>" in result
|
||
|
||
def test_ban_with_days(self):
|
||
"""При указании срока отображается количество дней."""
|
||
result = format_ban_confirmation(user_id=789, reason="Оскорбления", ban_days=7)
|
||
assert "7 дней" in result
|
||
assert "Оскорбления" in result
|
||
|
||
def test_escapes_reason(self):
|
||
"""Причина бана экранируется."""
|
||
result = format_ban_confirmation(user_id=1, reason="<html>", ban_days=1)
|
||
assert "<html>" in result
|
||
|
||
|
||
@pytest.mark.unit
|
||
@pytest.mark.asyncio
|
||
class TestReturnToAdminMenu:
|
||
"""Тесты для return_to_admin_menu."""
|
||
|
||
async def test_sets_state_and_sends_menu(self):
|
||
"""Устанавливается состояние ADMIN и отправляется меню."""
|
||
message = MagicMock()
|
||
message.from_user.id = 111
|
||
message.answer = AsyncMock()
|
||
state = MagicMock()
|
||
state.set_data = AsyncMock()
|
||
state.set_state = AsyncMock()
|
||
|
||
with patch(
|
||
"helper_bot.handlers.admin.utils.get_reply_keyboard_admin"
|
||
) as mock_kb:
|
||
mock_kb.return_value = "keyboard_markup"
|
||
|
||
await return_to_admin_menu(message, state)
|
||
|
||
state.set_data.assert_called_once_with({})
|
||
state.set_state.assert_called_once_with("ADMIN")
|
||
mock_kb.assert_called_once()
|
||
assert message.answer.call_count == 1
|
||
message.answer.assert_called_with(
|
||
"Вернулись в меню", reply_markup="keyboard_markup"
|
||
)
|
||
|
||
async def test_additional_message_sent_first(self):
|
||
"""При additional_message сначала отправляется оно, затем меню."""
|
||
message = MagicMock()
|
||
message.from_user.id = 222
|
||
message.answer = AsyncMock()
|
||
state = MagicMock()
|
||
state.set_data = AsyncMock()
|
||
state.set_state = AsyncMock()
|
||
|
||
with patch(
|
||
"helper_bot.handlers.admin.utils.get_reply_keyboard_admin",
|
||
return_value="keyboard_markup",
|
||
):
|
||
await return_to_admin_menu(
|
||
message, state, additional_message="Дополнительный текст"
|
||
)
|
||
|
||
assert message.answer.call_count == 2
|
||
message.answer.assert_any_call("Дополнительный текст")
|
||
message.answer.assert_any_call(
|
||
"Вернулись в меню", reply_markup="keyboard_markup"
|
||
)
|
||
|
||
|
||
@pytest.mark.unit
|
||
@pytest.mark.asyncio
|
||
class TestHandleAdminError:
|
||
"""Тесты для handle_admin_error."""
|
||
|
||
async def test_admin_error_sends_error_text(self):
|
||
"""При AdminError отправляется текст ошибки и возврат в меню."""
|
||
message = MagicMock()
|
||
message.from_user.id = 333
|
||
message.answer = AsyncMock()
|
||
state = MagicMock()
|
||
state.set_data = AsyncMock()
|
||
state.set_state = AsyncMock()
|
||
error = AdminError("Конкретная ошибка")
|
||
|
||
with patch(
|
||
"helper_bot.handlers.admin.utils.get_reply_keyboard_admin",
|
||
return_value="keyboard_markup",
|
||
):
|
||
await handle_admin_error(message, error, state, "test_context")
|
||
|
||
message.answer.assert_any_call("Ошибка: Конкретная ошибка")
|
||
state.set_data.assert_called_once_with({})
|
||
state.set_state.assert_called_once_with("ADMIN")
|
||
|
||
async def test_generic_error_sends_internal_message(self):
|
||
"""При любой другой ошибке отправляется общее сообщение."""
|
||
message = MagicMock()
|
||
message.from_user.id = 444
|
||
message.answer = AsyncMock()
|
||
state = MagicMock()
|
||
state.set_data = AsyncMock()
|
||
state.set_state = AsyncMock()
|
||
|
||
with patch(
|
||
"helper_bot.handlers.admin.utils.get_reply_keyboard_admin",
|
||
return_value="keyboard_markup",
|
||
):
|
||
await handle_admin_error(
|
||
message, ValueError("Что-то пошло не так"), state, "test"
|
||
)
|
||
|
||
message.answer.assert_any_call("Произошла внутренняя ошибка. Попробуйте позже.")
|
||
state.set_state.assert_called_once_with("ADMIN")
|