Spaces:
Runtime error
Runtime error
| """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 | |
| 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() | |
| 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.""" | |
| 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.""" | |
| 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) |