"""Tests for refactored private handlers""" from unittest.mock import AsyncMock, MagicMock, Mock, patch import pytest from aiogram import types from aiogram.fsm.context import FSMContext from helper_bot.handlers.private.constants import BUTTON_TEXTS, FSM_STATES from helper_bot.handlers.private.private_handlers import ( PrivateHandlers, create_private_handlers, ) from helper_bot.handlers.private.services import BotSettings class TestPrivateHandlers: """Test class for PrivateHandlers""" @pytest.fixture def mock_db(self): """Mock database""" db = Mock() db.user_exists = AsyncMock(return_value=False) db.add_user = AsyncMock() db.update_user_date = AsyncMock() db.update_stickers_info = AsyncMock() db.add_post = AsyncMock() db.add_message = AsyncMock() db.update_helper_message = AsyncMock() db.update_user_activity = AsyncMock() return db @pytest.fixture def mock_settings(self): """Mock bot settings""" return BotSettings( group_for_posts="test_posts", group_for_message="test_message", main_public="test_public", group_for_logs="test_logs", important_logs="test_important", preview_link="test_link", logs="test_logs_setting", test="test_test_setting", ) @pytest.fixture def mock_message(self): """Mock Telegram message""" message = Mock(spec=types.Message) # Создаем мок для from_user from_user = Mock() from_user.id = 12345 from_user.full_name = "Test User" from_user.username = "testuser" from_user.is_bot = False from_user.language_code = "ru" message.from_user = from_user message.text = "test message" message.message_id = 1 # Создаем мок для chat chat = Mock() chat.id = 12345 message.chat = chat message.bot = Mock() message.bot.send_message = AsyncMock() message.forward = AsyncMock() message.answer = AsyncMock() message.answer_sticker = AsyncMock() return message @pytest.fixture def mock_state(self): """Mock FSM state""" state = Mock(spec=FSMContext) state.set_state = AsyncMock() state.get_state = AsyncMock(return_value=FSM_STATES["START"]) return state def test_create_private_handlers(self, mock_db, mock_settings): """Test creating private handlers instance""" handlers = create_private_handlers(mock_db, mock_settings) assert isinstance(handlers, PrivateHandlers) assert handlers.db == mock_db assert handlers.settings == mock_settings def test_private_handlers_initialization(self, mock_db, mock_settings): """Test PrivateHandlers initialization""" handlers = PrivateHandlers(mock_db, mock_settings) assert handlers.db == mock_db assert handlers.settings == mock_settings assert handlers.user_service is not None assert handlers.post_service is not None assert handlers.sticker_service is not None assert handlers.router is not None @pytest.mark.asyncio async def test_handle_emoji_message( self, mock_db, mock_settings, mock_message, mock_state ): """Test emoji message handler""" handlers = create_private_handlers(mock_db, mock_settings) # Mock the check_user_emoji function with pytest.MonkeyPatch().context() as m: mock_check_emoji = AsyncMock(return_value="😊") m.setattr( "helper_bot.handlers.private.private_handlers.check_user_emoji", mock_check_emoji, ) # Test the handler await handlers.handle_emoji_message(mock_message, mock_state) # Verify state was set mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) # Verify message was logged mock_message.forward.assert_called_once_with( chat_id=mock_settings.group_for_logs ) @pytest.mark.asyncio async def test_handle_emoji_message_no_emoji(self, mock_db, mock_settings, mock_message, mock_state): """handle_emoji_message при user_emoji=None не отправляет ответ с эмодзи.""" handlers = create_private_handlers(mock_db, mock_settings) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.check_user_emoji', AsyncMock(return_value=None)) await handlers.handle_emoji_message(mock_message, mock_state) mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) mock_message.answer.assert_not_called() mock_message.forward.assert_called_once_with( chat_id=mock_settings.group_for_logs ) @pytest.mark.asyncio async def test_handle_start_message( self, mock_db, mock_settings, mock_message, mock_state ): """Test start message handler""" handlers = create_private_handlers(mock_db, mock_settings) # Mock the get_first_name and messages functions with pytest.MonkeyPatch().context() as m: m.setattr( "helper_bot.handlers.private.private_handlers.get_first_name", lambda x: "Test", ) m.setattr( "helper_bot.handlers.private.private_handlers.messages.get_message", lambda x, y: "Hello Test!", ) mock_keyboard = AsyncMock(return_value=Mock()) m.setattr( "helper_bot.handlers.private.private_handlers.get_reply_keyboard", mock_keyboard, ) # Test the handler await handlers.handle_start_message(mock_message, mock_state) # Verify state was set mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) # Verify user was ensured to exist mock_db.add_user.assert_called_once() mock_db.update_user_date.assert_called_once() @pytest.mark.asyncio async def test_handle_restart_message(self, mock_db, mock_settings, mock_message, mock_state): """handle_restart_message перезапускает состояние и отправляет сообщение.""" handlers = create_private_handlers(mock_db, mock_settings) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.get_reply_keyboard', AsyncMock(return_value=Mock())) m.setattr('helper_bot.handlers.private.private_handlers.update_user_info', AsyncMock()) m.setattr('helper_bot.handlers.private.private_handlers.check_user_emoji', AsyncMock()) await handlers.handle_restart_message(mock_message, mock_state) mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) mock_message.answer.assert_called_once() @pytest.mark.asyncio async def test_suggest_post(self, mock_db, mock_settings, mock_message, mock_state): """suggest_post переводит в состояние SUGGEST и отправляет текст.""" handlers = create_private_handlers(mock_db, mock_settings) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.messages.get_message', lambda x, y: "Suggest text") await handlers.suggest_post(mock_message, mock_state) mock_state.set_state.assert_called_once_with(FSM_STATES["SUGGEST"]) mock_message.answer.assert_called_once() @pytest.mark.asyncio async def test_end_message(self, mock_db, mock_settings, mock_message, mock_state): """end_message отправляет прощание и переводит в START.""" handlers = create_private_handlers(mock_db, mock_settings) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.messages.get_message', lambda x, y: "Bye") await handlers.end_message(mock_message, mock_state) mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) assert mock_message.answer.await_count >= 1 @pytest.mark.asyncio async def test_stickers(self, mock_db, mock_settings, mock_message, mock_state): """stickers обновляет инфо о стикерах и отправляет ссылку.""" handlers = create_private_handlers(mock_db, mock_settings) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.get_reply_keyboard', AsyncMock(return_value=Mock())) await handlers.stickers(mock_message, mock_state) mock_db.update_stickers_info.assert_awaited_once_with(mock_message.from_user.id) mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) mock_message.answer.assert_called_once() @pytest.mark.asyncio async def test_connect_with_admin(self, mock_db, mock_settings, mock_message, mock_state): """connect_with_admin переводит в PRE_CHAT и отправляет сообщение.""" handlers = create_private_handlers(mock_db, mock_settings) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.messages.get_message', lambda x, y: "Admin contact") await handlers.connect_with_admin(mock_message, mock_state) mock_state.set_state.assert_called_once_with(FSM_STATES["PRE_CHAT"]) mock_message.answer.assert_called_once() @pytest.mark.asyncio async def test_resend_message_in_group_pre_chat(self, mock_db, mock_settings, mock_message, mock_state): """resend_message_in_group при PRE_CHAT переводит в START и отправляет question.""" handlers = create_private_handlers(mock_db, mock_settings) mock_state.get_state = AsyncMock(return_value=FSM_STATES["PRE_CHAT"]) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.get_reply_keyboard', AsyncMock(return_value=Mock())) m.setattr('helper_bot.handlers.private.private_handlers.messages.get_message', lambda x, y: "Question?") await handlers.resend_message_in_group_for_message(mock_message, mock_state) mock_message.forward.assert_called_once_with(chat_id=mock_settings.group_for_message) mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) @pytest.mark.asyncio async def test_resend_message_in_group_chat(self, mock_db, mock_settings, mock_message, mock_state): """resend_message_in_group при CHAT оставляет в CHAT и отправляет question с leave markup.""" handlers = create_private_handlers(mock_db, mock_settings) mock_state.get_state = AsyncMock(return_value=FSM_STATES["CHAT"]) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.get_reply_keyboard_leave_chat', lambda: Mock()) m.setattr('helper_bot.handlers.private.private_handlers.messages.get_message', lambda x, y: "Question?") await handlers.resend_message_in_group_for_message(mock_message, mock_state) mock_message.forward.assert_called_once_with(chat_id=mock_settings.group_for_message) mock_message.answer.assert_called() @pytest.mark.asyncio async def test_suggest_router_answers_and_schedules_background(self, mock_db, mock_settings, mock_message, mock_state): """suggest_router сразу отвечает и планирует фоновую обработку.""" mock_message.media_group_id = None handlers = create_private_handlers(mock_db, mock_settings) with pytest.MonkeyPatch().context() as m: m.setattr('helper_bot.handlers.private.private_handlers.get_reply_keyboard', AsyncMock(return_value=Mock())) m.setattr('helper_bot.handlers.private.private_handlers.messages.get_message', lambda x, y: "Success") with patch.object(handlers.post_service, 'process_post', new_callable=AsyncMock): await handlers.suggest_router(mock_message, mock_state) mock_state.set_state.assert_called_once_with(FSM_STATES["START"]) mock_message.answer.assert_called_once() class TestBotSettings: """Test class for BotSettings dataclass""" def test_bot_settings_creation(self): """Test creating BotSettings instance""" settings = BotSettings( group_for_posts="posts", group_for_message="message", main_public="public", group_for_logs="logs", important_logs="important", preview_link="link", logs="logs_setting", test="test_setting", ) assert settings.group_for_posts == "posts" assert settings.group_for_message == "message" assert settings.main_public == "public" assert settings.group_for_logs == "logs" assert settings.important_logs == "important" assert settings.preview_link == "link" assert settings.logs == "logs_setting" assert settings.test == "test_setting" class TestConstants: """Test class for constants""" def test_fsm_states(self): """Test FSM states constants""" assert FSM_STATES["START"] == "START" assert FSM_STATES["SUGGEST"] == "SUGGEST" assert FSM_STATES["PRE_CHAT"] == "PRE_CHAT" assert FSM_STATES["CHAT"] == "CHAT" def test_button_texts(self): """Test button text constants""" assert BUTTON_TEXTS["SUGGEST_POST"] == "📢Предложить свой пост" assert BUTTON_TEXTS["SAY_GOODBYE"] == "👋🏼Сказать пока!" assert BUTTON_TEXTS["LEAVE_CHAT"] == "Выйти из чата" assert BUTTON_TEXTS["RETURN_TO_BOT"] == "Вернуться в бота" assert BUTTON_TEXTS["WANT_STICKERS"] == "🤪Хочу стикеры" assert BUTTON_TEXTS["CONNECT_ADMIN"] == "📩Связаться с админами"