2025-12-27 11:59:04 +01:00
import os
2025-12-28 19:59:31 +01:00
import sys
from pathlib import Path
2025-12-27 11:59:04 +01:00
import pytest
2026-01-01 15:27:25 +01:00
import asyncio
from unittest . mock import MagicMock , patch , AsyncMock
2025-12-28 19:59:31 +01:00
sys . path . append ( str ( Path ( __file__ ) . parent . parent ) )
2025-12-27 11:59:04 +01:00
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 ) :
2026-01-01 15:27:25 +01:00
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 " )
2025-12-27 11:59:04 +01:00
mock_post . assert_called_once ( )
2026-01-01 15:27:25 +01:00
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
2025-12-27 11:59:04 +01:00
with patch ( " builtins.open " , create = True ) :
2026-01-01 15:27:25 +01:00
await telegram_bot . _send_photo ( " /path/to/photo.jpg " , " Test caption " )
2025-12-27 11:59:04 +01:00
mock_post . assert_called_once ( )
2026-01-01 15:27:25 +01:00
call_kwargs = mock_post . call_args [ 1 ]
assert call_kwargs [ " data " ] [ " caption " ] == " Test caption "
2025-12-27 11:59:04 +01:00
2026-01-01 15:27:25 +01:00
@pytest.mark.asyncio
2025-12-27 11:59:04 +01:00
@patch ( " telegram_bot.TelegramBot._send_message " )
2026-01-01 15:27:25 +01:00
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 ( )
2025-12-27 11:59:04 +01:00
mock_send_message . assert_called_once ( )
assert " Autopilot " in mock_send_message . call_args [ 0 ] [ 0 ]
2026-01-01 15:27:25 +01:00
@pytest.mark.asyncio
2025-12-27 11:59:04 +01:00
@patch ( " telegram_bot.TelegramBot._send_message " )
2026-01-01 15:27:25 +01:00
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 ( )
2025-12-27 11:59:04 +01:00
mock_send_message . assert_called_once ( )
2026-01-01 15:27:25 +01:00
assert " Available commands " in mock_send_message . call_args [ 0 ] [ 0 ]
2025-12-27 11:59:04 +01:00
2026-01-01 15:27:25 +01:00
@pytest.mark.asyncio
2025-12-27 11:59:04 +01:00
@patch ( " telegram_bot.TelegramBot._send_message " )
2026-01-01 15:27:25 +01:00
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 " )
2025-12-27 11:59:04 +01:00
mock_send_message . assert_called_once ( )
2025-12-28 19:59:31 +01:00
assert " Unknown command " in mock_send_message . call_args [ 0 ] [ 0 ]
2026-01-01 15:27:25 +01:00
@pytest.mark.asyncio
2025-12-28 19:59:31 +01:00
@patch ( " telegram_bot.TelegramBot._send_photo " )
@patch ( " telegram_bot.TelegramBot._send_message " )
2026-01-01 15:27:25 +01:00
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 )
2025-12-28 19:59:31 +01:00
telegram_bot . app_handler . _generate_weekly_plot = MagicMock ( return_value = " /path/to/plot.png " )
2026-01-01 15:27:25 +01:00
await telegram_bot . _handle_plot_command ( )
2025-12-28 19:59:31 +01:00
mock_send_photo . assert_called_once_with ( " /path/to/plot.png " , " 📊 <b>Weekly Listing Patterns</b> \n \n This shows when new listings typically appear throughout the week. " )
2026-01-01 15:27:25 +01:00
@pytest.mark.asyncio
@patch ( " telegram_bot.TelegramBot._send_photo " )
2025-12-28 19:59:31 +01:00
@patch ( " telegram_bot.TelegramBot._send_message " )
2026-01-01 15:27:25 +01:00
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 )
2025-12-28 19:59:31 +01:00
telegram_bot . app_handler . _generate_weekly_plot = MagicMock ( return_value = " " )
2026-01-01 15:27:25 +01:00
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 ( )
2025-12-28 19:59:31 +01:00
2026-01-01 15:27:25 +01:00
@pytest.mark.asyncio
2025-12-28 19:59:31 +01:00
@patch ( " telegram_bot.TelegramBot._send_photo " )
@patch ( " telegram_bot.TelegramBot._send_message " )
2026-01-01 15:27:25 +01:00
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 )
2025-12-28 19:59:31 +01:00
telegram_bot . app_handler . _generate_error_rate_plot = MagicMock ( return_value = ( " /path/to/error_rate.png " , " Summary text " ) )
2026-01-01 15:27:25 +01:00
await telegram_bot . _handle_error_rate_command ( )
2025-12-28 19:59:31 +01:00
mock_send_photo . assert_called_once_with ( " /path/to/error_rate.png " , " 📉 <b>Autopilot Success vs Failure</b> \n \n Summary text " )
2026-01-01 15:27:25 +01:00
@pytest.mark.asyncio
@patch ( " telegram_bot.TelegramBot._send_photo " )
2025-12-28 19:59:31 +01:00
@patch ( " telegram_bot.TelegramBot._send_message " )
2026-01-01 15:27:25 +01:00
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 )
2025-12-28 19:59:31 +01:00
telegram_bot . app_handler . _generate_error_rate_plot = MagicMock ( return_value = ( " " , " " ) )
2026-01-01 15:27:25 +01:00
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 ( )