Files
telegram-helper-bot/tests/test_decorators.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

173 lines
6.3 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.
"""
Тесты для декораторов group и private handlers (error_handler).
"""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from aiogram import types
from helper_bot.handlers.group.decorators import error_handler as group_error_handler
from helper_bot.handlers.private.decorators import (
error_handler as private_error_handler,
)
class FakeMessage:
"""Класс-маркер, чтобы мок проходил isinstance(..., types.Message) в декораторе."""
pass
@pytest.mark.unit
@pytest.mark.asyncio
class TestGroupErrorHandler:
"""Тесты для error_handler из group/decorators."""
async def test_success_returns_result(self):
"""При успешном выполнении возвращается результат функции."""
@group_error_handler
async def sample_handler():
return "ok"
result = await sample_handler()
assert result == "ok"
async def test_exception_is_reraised(self):
"""При исключении оно пробрасывается дальше."""
@group_error_handler
async def failing_handler():
raise ValueError("test error")
with pytest.raises(ValueError, match="test error"):
await failing_handler()
@patch("helper_bot.handlers.group.decorators.logger")
async def test_exception_is_logged(self, mock_logger):
"""При исключении вызывается logger.error."""
@group_error_handler
async def failing_handler():
raise RuntimeError("logged error")
with pytest.raises(RuntimeError):
await failing_handler()
mock_logger.error.assert_called_once()
assert "logged error" in mock_logger.error.call_args[0][0]
assert "failing_handler" in mock_logger.error.call_args[0][0]
@patch("helper_bot.handlers.group.decorators.types")
@patch("helper_bot.utils.base_dependency_factory.get_global_instance")
@patch("helper_bot.handlers.group.decorators.logger")
async def test_exception_sends_to_important_logs_when_message_has_bot(
self, mock_logger, mock_get_global, mock_types
):
"""При исключении и наличии message с bot отправляется сообщение в important_logs."""
mock_types.Message = FakeMessage
message = MagicMock()
message.__class__ = FakeMessage
message.bot = MagicMock()
message.bot.send_message = AsyncMock()
mock_bdf = MagicMock()
mock_bdf.settings = {"Telegram": {"important_logs": "-100123"}}
mock_get_global.return_value = mock_bdf
@group_error_handler
async def failing_handler(msg):
assert msg is message
raise ValueError("error for logs")
with pytest.raises(ValueError):
await failing_handler(message)
mock_get_global.assert_called_once()
message.bot.send_message.assert_called_once()
call_kwargs = message.bot.send_message.call_args[1]
assert call_kwargs["chat_id"] == "-100123"
call_text = call_kwargs["text"]
assert "error for logs" in call_text
assert "failing_handler" in call_text
assert "Traceback" in call_text
@pytest.mark.unit
@pytest.mark.asyncio
class TestPrivateErrorHandler:
"""Тесты для error_handler из private/decorators."""
async def test_success_returns_result(self):
"""При успешном выполнении возвращается результат функции."""
@private_error_handler
async def sample_handler():
return 42
result = await sample_handler()
assert result == 42
async def test_exception_is_reraised(self):
"""При исключении оно пробрасывается дальше."""
@private_error_handler
async def failing_handler():
raise TypeError("private error")
with pytest.raises(TypeError, match="private error"):
await failing_handler()
@patch("helper_bot.handlers.private.decorators.logger")
async def test_exception_is_logged(self, mock_logger):
"""При исключении вызывается logger.error."""
@private_error_handler
async def failing_handler():
raise KeyError("key missing")
with pytest.raises(KeyError):
await failing_handler()
mock_logger.error.assert_called_once()
assert "key missing" in mock_logger.error.call_args[0][0]
@patch("helper_bot.handlers.private.decorators.types")
@patch("helper_bot.utils.base_dependency_factory.get_global_instance")
async def test_exception_sends_to_important_logs_when_message_has_bot(
self, mock_get_global, mock_types
):
"""При исключении и наличии message с bot отправляется сообщение в important_logs."""
mock_types.Message = FakeMessage
message = MagicMock()
message.__class__ = FakeMessage
message.bot = MagicMock()
message.bot.send_message = AsyncMock()
mock_bdf = MagicMock()
mock_bdf.settings = {"Telegram": {"important_logs": "-100456"}}
mock_get_global.return_value = mock_bdf
@private_error_handler
async def failing_handler(msg):
raise RuntimeError("private runtime")
with pytest.raises(RuntimeError):
await failing_handler(message)
mock_get_global.assert_called_once()
message.bot.send_message.assert_called_once()
call_kwargs = message.bot.send_message.call_args[1]
assert call_kwargs["chat_id"] == "-100456"
call_text = call_kwargs["text"]
assert "private runtime" in call_text
assert "failing_handler" in call_text
async def test_no_message_in_args_no_send(self):
"""Если в args нет Message, send_message не вызывается (только логирование)."""
@private_error_handler
async def failing_handler():
raise ValueError("no message")
with pytest.raises(ValueError):
await failing_handler()
# get_global_instance не должен вызываться, т.к. message не найден в args