smart-line-bot / tests /unit /test_monitoring.py
Smiel2's picture
Initial commit
2eae977 verified
Raw
History Blame Contribute Delete
5.42 kB
"""Unit tests for monitoring metrics."""
import pytest
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime
from app.monitoring.metrics import (
http_requests_total,
http_request_duration_seconds,
db_query_duration_seconds,
db_connections_active,
celery_tasks_total,
celery_task_duration_seconds,
celery_active_tasks,
line_api_calls_total,
ai_requests_total,
ai_tokens_used,
scraping_jobs_total,
active_users,
line_users_total,
MetricsMiddleware,
track_db_query,
track_celery_task,
)
class TestMetricsCounters:
"""Test Prometheus counters."""
def test_http_requests_total_labels(self):
"""Test HTTP requests counter with labels."""
# Reset mock
http_requests_total.reset()
# Should not raise - just testing metric creation
assert http_requests_total is not None
def test_celery_tasks_total_labels(self):
"""Test Celery tasks counter with labels."""
celery_tasks_total.reset()
assert celery_tasks_total is not None
def test_ai_requests_total_labels(self):
"""Test AI requests counter with labels."""
ai_requests_total.reset()
assert ai_requests_total is not None
class TestMetricsGauges:
"""Test Prometheus gauges."""
def test_active_users_gauge(self):
"""Test active users gauge."""
active_users.set(5)
# Should not raise
assert active_users is not None
def test_line_users_total_gauge(self):
"""Test LINE users total gauge."""
line_users_total.set(10)
assert line_users_total is not None
def test_db_connections_gauge(self):
"""Test database connections gauge."""
db_connections_active.set(3)
assert db_connections_active is not None
class TestMetricsMiddleware:
"""Test metrics middleware."""
def test_middleware_init(self):
"""Test middleware initialization."""
mock_app = Mock()
middleware = MetricsMiddleware(mock_app)
assert middleware.app == mock_app
@pytest.mark.asyncio
async def test_middleware_non_http(self):
"""Test middleware passes through non-HTTP requests."""
mock_app = Mock()
middleware = MetricsMiddleware(mock_app)
scope = {"type": "websocket"}
receive = Mock()
send = Mock()
await middleware(scope, receive, send)
mock_app.assert_called_once()
@pytest.mark.asyncio
async def test_middleware_http(self):
"""Test middleware tracks HTTP requests."""
mock_app = AsyncMock()
middleware = MetricsMiddleware(mock_app)
scope = {
"type": "http",
"method": "GET",
"path": "/test",
}
receive = Mock()
# Mock send that captures response start
async def mock_send(message):
if message["type"] == "http.response.start":
message["status"] = 200
send = Mock(side_effect=mock_send)
await middleware(scope, receive, send)
# App should be called
mock_app.assert_called_once()
class TestTrackDecorators:
"""Test metric tracking decorators."""
def test_track_db_query_decorator(self):
"""Test database query tracking decorator."""
@track_db_query("select", "users")
async def mock_query():
return ["result"]
# Should execute without error
import asyncio
result = asyncio.get_event_loop().run_until_complete(mock_query())
assert result == ["result"]
def test_track_celery_task_decorator(self):
"""Test Celery task tracking decorator."""
@track_celery_task("test_task")
def mock_task():
return "task_result"
result = mock_task()
assert result == "task_result"
class TestPrometheusMetrics:
"""Test Prometheus metrics integration."""
def test_metrics_are_counters(self):
"""Test that counters are properly configured."""
assert hasattr(http_requests_total, "labels")
assert hasattr(celery_tasks_total, "labels")
assert hasattr(ai_requests_total, "labels")
def test_metrics_are_gauges(self):
"""Test that gauges are properly configured."""
assert hasattr(active_users, "set")
assert hasattr(line_users_total, "set")
assert hasattr(db_connections_active, "set")
def test_metrics_have_descriptions(self):
"""Test that metrics have descriptions."""
# All metrics should have _value attribute or similar
assert http_requests_total is not None
class TestMetricsCollection:
"""Test metrics collection scenarios."""
def test_increment_counter(self):
"""Test incrementing a counter."""
# Reset first
ai_requests_total.reset()
# Should not raise
ai_requests_total.labels(status="success").inc(1)
def test_observe_histogram(self):
"""Test observing histogram values."""
# Should not raise
http_request_duration_seconds.labels(
method="GET",
endpoint="/api/test",
).observe(0.5)
def test_gauge_set_value(self):
"""Test setting gauge value."""
# Should not raise
active_users.set(42)