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

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

View File

@@ -0,0 +1,251 @@
"""
Тесты для helper_bot.server_prometheus: MetricsServer, start_metrics_server, stop_metrics_server.
"""
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from aiohttp import web
from helper_bot.server_prometheus import (
MetricsServer,
start_metrics_server,
stop_metrics_server,
)
@pytest.mark.unit
@pytest.mark.asyncio
class TestMetricsServer:
"""Тесты для класса MetricsServer."""
def test_init_sets_host_port_and_routes(self):
"""При инициализации задаются host, port и маршруты /metrics, /health."""
server = MetricsServer(host="127.0.0.1", port=9090)
assert server.host == "127.0.0.1"
assert server.port == 9090
assert server.runner is None
assert server.site is None
paths = []
for res in server.app.router.resources():
info = res.get_info()
path = info.get("path") or info.get("formatter")
if path:
paths.append(path)
assert "/metrics" in paths
assert "/health" in paths
@patch("helper_bot.server_prometheus.metrics")
async def test_metrics_handler_success_returns_prometheus_content(
self, mock_metrics_module
):
"""metrics_handler при успехе возвращает 200 и данные метрик."""
mock_metrics_module.get_metrics.return_value = b"# TYPE bot_commands_total counter"
server = MetricsServer(host="0.0.0.0", port=8080)
request = MagicMock(spec=web.Request)
response = await server.metrics_handler(request)
assert response.status == 200
assert response.body == b"# TYPE bot_commands_total counter"
assert "text/plain" in response.content_type
mock_metrics_module.get_metrics.assert_called_once()
@patch("helper_bot.server_prometheus.metrics", None)
async def test_metrics_handler_when_metrics_none_returns_500(self):
"""metrics_handler при недоступности metrics возвращает 500."""
server = MetricsServer(host="0.0.0.0", port=8080)
request = MagicMock(spec=web.Request)
response = await server.metrics_handler(request)
assert response.status == 500
assert "Metrics not available" in response.text
@patch("helper_bot.server_prometheus.metrics")
async def test_metrics_handler_on_exception_returns_500(
self, mock_metrics_module
):
"""metrics_handler при исключении в get_metrics возвращает 500."""
mock_metrics_module.get_metrics.side_effect = RuntimeError("metrics error")
server = MetricsServer(host="0.0.0.0", port=8080)
request = MagicMock(spec=web.Request)
response = await server.metrics_handler(request)
assert response.status == 500
assert "Error generating metrics" in response.text
@patch("helper_bot.server_prometheus.metrics")
async def test_health_handler_success_returns_ok(self, mock_metrics_module):
"""health_handler при успехе возвращает 200 OK."""
mock_metrics_module.get_metrics.return_value = b"some_metrics_data"
server = MetricsServer(host="0.0.0.0", port=8080)
request = MagicMock(spec=web.Request)
response = await server.health_handler(request)
assert response.status == 200
assert response.text == "OK"
@patch("helper_bot.server_prometheus.metrics", None)
async def test_health_handler_when_metrics_none_returns_503(self):
"""health_handler при недоступности metrics возвращает 503."""
server = MetricsServer(host="0.0.0.0", port=8080)
request = MagicMock(spec=web.Request)
response = await server.health_handler(request)
assert response.status == 503
assert "Metrics not available" in response.text
@patch("helper_bot.server_prometheus.metrics")
async def test_health_handler_empty_metrics_returns_503(
self, mock_metrics_module
):
"""health_handler при пустых метриках возвращает 503."""
mock_metrics_module.get_metrics.return_value = b""
server = MetricsServer(host="0.0.0.0", port=8080)
request = MagicMock(spec=web.Request)
response = await server.health_handler(request)
assert response.status == 503
assert "Empty metrics" in response.text
@patch("helper_bot.server_prometheus.metrics")
async def test_health_handler_get_metrics_raises_returns_503(
self, mock_metrics_module
):
"""health_handler при исключении get_metrics возвращает 503."""
mock_metrics_module.get_metrics.side_effect = ValueError("gen failed")
server = MetricsServer(host="0.0.0.0", port=8080)
request = MagicMock(spec=web.Request)
response = await server.health_handler(request)
assert response.status == 503
assert "Metrics generation failed" in response.text
@patch("helper_bot.server_prometheus.web.AppRunner")
@patch("helper_bot.server_prometheus.web.TCPSite")
async def test_start_creates_runner_and_site(
self, mock_tcp_site_cls, mock_app_runner_cls
):
"""start() создаёт AppRunner и TCPSite и запускает сервер."""
mock_runner = MagicMock()
mock_runner.setup = AsyncMock()
mock_app_runner_cls.return_value = mock_runner
mock_site = MagicMock()
mock_site.start = AsyncMock()
mock_tcp_site_cls.return_value = mock_site
server = MetricsServer(host="0.0.0.0", port=19998)
await server.start()
mock_app_runner_cls.assert_called_once_with(server.app)
mock_runner.setup.assert_awaited_once()
mock_tcp_site_cls.assert_called_once_with(mock_runner, "0.0.0.0", 19998)
mock_site.start.assert_awaited_once()
assert server.runner is mock_runner
assert server.site is mock_site
async def test_stop_stops_site_and_cleans_runner(self):
"""stop() останавливает site и очищает runner."""
server = MetricsServer(host="0.0.0.0", port=8080)
server.site = MagicMock()
server.site.stop = AsyncMock()
server.runner = MagicMock()
server.runner.cleanup = AsyncMock()
await server.stop()
server.site.stop.assert_awaited_once()
server.runner.cleanup.assert_awaited_once()
async def test_stop_when_site_none_does_not_raise(self):
"""stop() при site=None не падает."""
server = MetricsServer(host="0.0.0.0", port=8080)
server.site = None
server.runner = None
await server.stop()
@patch.object(MetricsServer, "start", new_callable=AsyncMock)
@patch.object(MetricsServer, "stop", new_callable=AsyncMock)
async def test_context_manager_enters_and_exits(
self, mock_stop, mock_start
):
"""Использование как async context manager вызывает start и stop."""
mock_start.return_value = None
server = MetricsServer(host="0.0.0.0", port=8080)
async with server:
pass
mock_start.assert_awaited_once()
mock_stop.assert_awaited_once()
@patch.object(MetricsServer, "start", new_callable=AsyncMock)
@patch.object(MetricsServer, "stop", new_callable=AsyncMock)
async def test_context_manager_exit_calls_stop_on_exception(
self, mock_stop, mock_start
):
"""При исключении внутри контекста stop всё равно вызывается."""
mock_start.return_value = None
server = MetricsServer(host="0.0.0.0", port=8080)
with pytest.raises(ValueError):
async with server:
raise ValueError("test")
mock_stop.assert_awaited_once()
@pytest.mark.unit
@pytest.mark.asyncio
class TestStartStopMetricsServer:
"""Тесты для start_metrics_server и stop_metrics_server."""
@patch("helper_bot.server_prometheus.MetricsServer")
async def test_start_metrics_server_creates_and_starts_server(
self, mock_server_cls
):
"""start_metrics_server создаёт MetricsServer и вызывает start()."""
mock_instance = MagicMock()
mock_instance.start = AsyncMock()
mock_server_cls.return_value = mock_instance
result = await start_metrics_server("0.0.0.0", 8080)
mock_server_cls.assert_called_once_with("0.0.0.0", 8080)
mock_instance.start.assert_awaited_once()
assert result is mock_instance
@patch("helper_bot.server_prometheus.MetricsServer")
async def test_stop_metrics_server_when_running_stops_and_clears_global(
self, mock_server_cls
):
"""stop_metrics_server при запущенном сервере останавливает его и обнуляет глобальную переменную."""
import helper_bot.server_prometheus as mod
mock_instance = MagicMock()
mock_instance.stop = AsyncMock()
old_server = mod.metrics_server
mod.metrics_server = mock_instance
try:
await stop_metrics_server()
mock_instance.stop.assert_awaited_once()
assert mod.metrics_server is None
finally:
mod.metrics_server = old_server
async def test_stop_metrics_server_when_none_does_not_raise(self):
"""stop_metrics_server при metrics_server=None не падает."""
import helper_bot.server_prometheus as mod
old_server = mod.metrics_server
mod.metrics_server = None
try:
await stop_metrics_server()
assert mod.metrics_server is None
finally:
mod.metrics_server = old_server