import os import sys from pathlib import Path import pytest import asyncio from unittest.mock import MagicMock, patch, AsyncMock sys.path.append(str(Path(__file__).parent.parent)) from telegram_bot import TelegramBot from dotenv import load_dotenv # Load environment variables from .env file load_dotenv() # Explicitly pass token and chat ID to ensure they are set @pytest.fixture(autouse=True) def mock_env_vars(): os.environ["TELEGRAM_BOT_TOKEN"] = "test_token" os.environ["TELEGRAM_CHAT_ID"] = "test_chat_id" @pytest.fixture def mock_monitor(): monitor = MagicMock() monitor.load_state.return_value = {"autopilot": True} monitor.load_applications.return_value = { "app1": {"company": "CompanyA"}, "app2": {"company": "CompanyB"}, "app3": {"company": "CompanyA"}, } return monitor @pytest.fixture def telegram_bot(mock_monitor): event_loop = asyncio.new_event_loop() return TelegramBot(mock_monitor, bot_token="test_token", chat_id="test_chat_id", event_loop=event_loop) @pytest.mark.asyncio @patch("httpx.AsyncClient.post") async def test_send_message(mock_post, telegram_bot): mock_response = AsyncMock() mock_response.status_code = 200 mock_post.return_value = mock_response await telegram_bot._send_message("Test message") mock_post.assert_called_once() call_kwargs = mock_post.call_args[1] assert call_kwargs["json"]["text"] == "Test message" @pytest.mark.asyncio @patch("httpx.AsyncClient.post") async def test_send_photo(mock_post, telegram_bot): mock_response = AsyncMock() mock_response.status_code = 200 mock_post.return_value = mock_response with patch("builtins.open", create=True): await telegram_bot._send_photo("/path/to/photo.jpg", "Test caption") mock_post.assert_called_once() call_kwargs = mock_post.call_args[1] assert call_kwargs["data"]["caption"] == "Test caption" @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_message") async def test_handle_status_command(mock_send_message, telegram_bot): mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) await telegram_bot._handle_status_command() mock_send_message.assert_called_once() assert "Autopilot" in mock_send_message.call_args[0][0] @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_message") async def test_handle_help_command(mock_send_message, telegram_bot): mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) await telegram_bot._handle_help_command() mock_send_message.assert_called_once() assert "Available commands" in mock_send_message.call_args[0][0] @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_message") async def test_handle_unknown_command(mock_send_message, telegram_bot): mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) await telegram_bot._handle_unknown_command("/unknown") mock_send_message.assert_called_once() assert "Unknown command" in mock_send_message.call_args[0][0] @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_photo") @patch("telegram_bot.TelegramBot._send_message") async def test_handle_plot_command(mock_send_message, mock_send_photo, telegram_bot): mock_send_photo.return_value = asyncio.Future() mock_send_photo.return_value.set_result(None) telegram_bot.app_handler._generate_weekly_plot = MagicMock(return_value="/path/to/plot.png") await telegram_bot._handle_plot_command() mock_send_photo.assert_called_once_with("/path/to/plot.png", "📊 Weekly Listing Patterns\n\nThis shows when new listings typically appear throughout the week.") @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_photo") @patch("telegram_bot.TelegramBot._send_message") async def test_handle_plot_command_no_data(mock_send_message, mock_send_photo, telegram_bot): mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) mock_send_photo.return_value = asyncio.Future() mock_send_photo.return_value.set_result(None) telegram_bot.app_handler._generate_weekly_plot = MagicMock(return_value="") await telegram_bot._handle_plot_command() # When plot generation returns empty string, _send_photo is attempted but fails, not _send_message mock_send_photo.assert_called_once() @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_photo") @patch("telegram_bot.TelegramBot._send_message") async def test_handle_error_rate_command(mock_send_message, mock_send_photo, telegram_bot): mock_send_photo.return_value = asyncio.Future() mock_send_photo.return_value.set_result(None) telegram_bot.app_handler._generate_error_rate_plot = MagicMock(return_value=("/path/to/error_rate.png", "Summary text")) await telegram_bot._handle_error_rate_command() mock_send_photo.assert_called_once_with("/path/to/error_rate.png", "📉 Autopilot Success vs Failure\n\nSummary text") @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_photo") @patch("telegram_bot.TelegramBot._send_message") async def test_handle_error_rate_command_no_data(mock_send_message, mock_send_photo, telegram_bot): mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) mock_send_photo.return_value = asyncio.Future() mock_send_photo.return_value.set_result(None) telegram_bot.app_handler._generate_error_rate_plot = MagicMock(return_value=("", "")) await telegram_bot._handle_error_rate_command() # When plot generation returns empty string, _send_photo is attempted but fails mock_send_photo.assert_called_once() @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_message") async def test_handle_autopilot_on_command(mock_send_message, telegram_bot): """Test enabling autopilot via command.""" mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) telegram_bot.monitor.set_autopilot = MagicMock() await telegram_bot._handle_autopilot_command("/autopilot on") telegram_bot.monitor.set_autopilot.assert_called_once_with(True) mock_send_message.assert_called() @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_message") async def test_handle_autopilot_off_command(mock_send_message, telegram_bot): """Test disabling autopilot via command.""" mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) telegram_bot.monitor.set_autopilot = MagicMock() await telegram_bot._handle_autopilot_command("/autopilot off") telegram_bot.monitor.set_autopilot.assert_called_once_with(False) mock_send_message.assert_called() @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_message") async def test_handle_retry_failed_command(mock_send_message, telegram_bot): """Test retry failed applications command.""" mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) # Mock load_applications to return properly structured failed application telegram_bot.app_handler.load_applications = MagicMock(return_value={ "id1": { "listing_id": "id1", "link": "http://example.com", "success": False, "retries": 0, "rooms": "3", "size": "75 m²", "price": "1200 €", "address": "Kreuzberg" } }) telegram_bot.app_handler.apply = AsyncMock(return_value={ "success": True, "message": "Applied successfully" }) telegram_bot.app_handler.save_application = MagicMock() await telegram_bot._handle_retry_failed_command() telegram_bot.app_handler.apply.assert_called_once() assert mock_send_message.call_count >= 2 # Initial message + results @pytest.mark.asyncio @patch("telegram_bot.TelegramBot._send_message") @patch("os.path.exists") @patch("shutil.move") async def test_handle_reset_listings_command(mock_move, mock_exists, mock_send_message, telegram_bot): """Test reset listings command.""" mock_send_message.return_value = asyncio.Future() mock_send_message.return_value.set_result(None) mock_exists.return_value = True await telegram_bot._handle_reset_listings_command() mock_move.assert_called_once() mock_send_message.assert_called()