Переписал почти все тесты

feat: улучшено логирование и обработка скорингов в PostService и RagApiClient

- Добавлены отладочные сообщения для передачи скорингов в функции обработки постов.
- Обновлено логирование успешного получения скорингов из RAG API с дополнительной информацией.
- Оптимизирована обработка скорингов в функции get_text_message для улучшения отладки.
- Обновлены тесты для проверки новых функциональных возможностей и обработки ошибок.
This commit is contained in:
2026-01-30 00:55:47 +03:00
parent e87f4af82f
commit a5faa4bdc6
27 changed files with 4320 additions and 8 deletions

180
tests/test_main.py Normal file
View File

@@ -0,0 +1,180 @@
"""
Тесты для helper_bot.main: start_bot_with_retry, start_bot.
"""
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from helper_bot.main import start_bot, start_bot_with_retry
@pytest.mark.unit
@pytest.mark.asyncio
class TestStartBotWithRetry:
"""Тесты для start_bot_with_retry."""
async def test_success_on_first_try_exits_immediately(self, mock_bot, mock_dispatcher):
"""При успешном start_polling с первой попытки цикл завершается без повторов."""
mock_dispatcher.start_polling = AsyncMock()
await start_bot_with_retry(mock_bot, mock_dispatcher, max_retries=3)
mock_dispatcher.start_polling.assert_awaited_once_with(mock_bot, skip_updates=True)
@patch("helper_bot.main.asyncio.sleep", new_callable=AsyncMock)
async def test_network_error_retries_then_succeeds(
self, mock_sleep, mock_bot, mock_dispatcher
):
"""При сетевой ошибке выполняется повтор с задержкой, затем успех."""
mock_dispatcher.start_polling = AsyncMock(
side_effect=[ConnectionError("connection reset"), None]
)
await start_bot_with_retry(
mock_bot, mock_dispatcher, max_retries=3, base_delay=0.1
)
assert mock_dispatcher.start_polling.await_count == 2
mock_sleep.assert_awaited_once()
# base_delay * (2 ** 0) = 0.1
mock_sleep.assert_awaited_with(0.1)
async def test_non_network_error_raises_immediately(
self, mock_bot, mock_dispatcher
):
"""При не-сетевой ошибке исключение пробрасывается без повторов."""
mock_dispatcher.start_polling = AsyncMock(
side_effect=ValueError("critical")
)
with pytest.raises(ValueError, match="critical"):
await start_bot_with_retry(mock_bot, mock_dispatcher, max_retries=3)
mock_dispatcher.start_polling.assert_awaited_once()
@patch("helper_bot.main.asyncio.sleep", new_callable=AsyncMock)
async def test_max_retries_exceeded_raises(self, mock_sleep, mock_bot, mock_dispatcher):
"""При исчерпании попыток из-за сетевых ошибок исключение пробрасывается."""
mock_dispatcher.start_polling = AsyncMock(
side_effect=ConnectionError("network error")
)
with pytest.raises(ConnectionError, match="network error"):
await start_bot_with_retry(
mock_bot, mock_dispatcher, max_retries=2, base_delay=0.01
)
assert mock_dispatcher.start_polling.await_count == 2
assert mock_sleep.await_count == 1
async def test_timeout_error_triggers_retry(self, mock_bot, mock_dispatcher):
"""Ошибка с 'timeout' в сообщении считается сетевой и даёт повтор."""
call_count = 0
async def polling(*args, **kwargs):
nonlocal call_count
call_count += 1
if call_count == 1:
raise TimeoutError("timeout while connecting")
return None
mock_dispatcher.start_polling = AsyncMock(side_effect=polling)
with patch("helper_bot.main.asyncio.sleep", new_callable=AsyncMock):
await start_bot_with_retry(
mock_bot, mock_dispatcher, max_retries=3, base_delay=0.01
)
assert call_count == 2
@pytest.mark.unit
@pytest.mark.asyncio
class TestStartBot:
"""Тесты для start_bot с моками Bot, Dispatcher, start_metrics_server и т.д."""
@pytest.fixture
def mock_bdf(self, test_settings):
"""Мок фабрики зависимостей (bdf) с настройками и scoring_manager."""
bdf = MagicMock()
bdf.settings = {
**test_settings,
"Metrics": {"host": "127.0.0.1", "port": 9090},
}
scoring_manager = MagicMock()
scoring_manager.close = AsyncMock()
bdf.get_scoring_manager = MagicMock(return_value=scoring_manager)
return bdf
@patch("helper_bot.main.stop_metrics_server", new_callable=AsyncMock)
@patch("helper_bot.main.start_bot_with_retry", new_callable=AsyncMock)
@patch("helper_bot.main.start_metrics_server", new_callable=AsyncMock)
@patch("helper_bot.main.VoiceHandlers")
@patch("helper_bot.main.Dispatcher")
@patch("helper_bot.main.Bot")
async def test_start_bot_calls_metrics_server_and_polling(
self,
mock_bot_cls,
mock_dp_cls,
mock_voice_handlers_cls,
mock_start_metrics,
mock_start_retry,
mock_stop_metrics,
mock_bdf,
):
"""start_bot создаёт Bot и Dispatcher, запускает метрики, delete_webhook, start_bot_with_retry; в finally — stop_metrics и закрытие ресурсов."""
mock_bot = MagicMock()
mock_bot.delete_webhook = AsyncMock()
mock_bot.session = MagicMock()
mock_bot.session.close = AsyncMock()
mock_bot_cls.return_value = mock_bot
mock_dp = MagicMock()
mock_dp.update = MagicMock()
mock_dp.update.outer_middleware = MagicMock(return_value=None)
mock_dp.include_routers = MagicMock()
mock_dp.shutdown = MagicMock(return_value=lambda f: None)
mock_dp_cls.return_value = mock_dp
mock_voice_router = MagicMock()
mock_voice_handlers_cls.return_value.router = mock_voice_router
result = await start_bot(mock_bdf)
mock_bot_cls.assert_called_once()
mock_dp_cls.assert_called_once()
mock_bot.delete_webhook.assert_awaited_once_with(drop_pending_updates=True)
mock_start_metrics.assert_awaited_once_with("127.0.0.1", 9090)
mock_start_retry.assert_awaited_once()
mock_stop_metrics.assert_awaited_once()
mock_bdf.get_scoring_manager.return_value.close.assert_awaited()
mock_bot.session.close.assert_awaited()
assert result is mock_bot
@patch("helper_bot.main.stop_metrics_server", new_callable=AsyncMock)
@patch("helper_bot.main.start_bot_with_retry", new_callable=AsyncMock)
@patch("helper_bot.main.start_metrics_server", new_callable=AsyncMock)
@patch("helper_bot.main.VoiceHandlers")
@patch("helper_bot.main.Dispatcher")
@patch("helper_bot.main.Bot")
async def test_start_bot_uses_default_metrics_host_port_when_not_in_settings(
self,
mock_bot_cls,
mock_dp_cls,
mock_voice_handlers_cls,
mock_start_metrics,
mock_start_retry,
mock_stop_metrics,
mock_bdf,
test_settings,
):
"""Если в настройках нет Metrics, используются host 0.0.0.0 и port 8080."""
mock_bdf.settings = test_settings
mock_bot = MagicMock()
mock_bot.delete_webhook = AsyncMock()
mock_bot.session = MagicMock()
mock_bot.session.close = AsyncMock()
mock_bot_cls.return_value = mock_bot
mock_dp = MagicMock()
mock_dp.update = MagicMock()
mock_dp.update.outer_middleware = MagicMock(return_value=None)
mock_dp.include_routers = MagicMock()
mock_dp.shutdown = MagicMock(return_value=lambda f: None)
mock_dp_cls.return_value = mock_dp
mock_voice_handlers_cls.return_value.router = MagicMock()
await start_bot(mock_bdf)
mock_start_metrics.assert_awaited_once_with("0.0.0.0", 8080)