""" Тесты для декораторов 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