Files
telegram-helper-bot/tests/test_admin_utils.py
Andrey 3d6b4353f9
All checks were successful
CI pipeline / Test & Code Quality (push) Successful in 34s
Refactor imports across multiple files to improve code organization and readability.
2026-02-28 23:24:25 +03:00

199 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Тесты для 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>") == "&lt;script&gt;"
def test_escaping_ampersand(self):
"""Экранирование &."""
assert escape_html("a & b") == "a &amp; b"
def test_escaping_quotes(self):
"""Экранирование кавычек."""
assert escape_html('"test"') == "&quot;test&quot;"
@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 "&lt;script&gt;" in result
assert "&lt;b&gt;Bold&lt;/b&gt;" 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 "&lt;html&gt;" 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")