Download tests/api/test_analytics_dashboard_endpoints.py from techprotrade/annator-command-center: direct link, hf CLI and curl.
- Browser
- Download file 36.7 kB
-
https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/api/test_analytics_dashboard_endpoints.py
- Command line
-
hf download hf://spaces/techprotrade/annator-command-center/tests/api/test_analytics_dashboard_endpoints.py
-
curl -L -o test_analytics_dashboard_endpoints.py https://huggingface.co/spaces/techprotrade/annator-command-center/resolve/main/tests/api/test_analytics_dashboard_endpoints.py
36.7 kB
| """ | |
| Analytics Dashboard Endpoints Unit Tests | |
| Tests for workflow analytics dashboard APIs including: | |
| - Dashboard KPIs | |
| - Top-performing workflows | |
| - Execution timeline | |
| - Error breakdown | |
| - Alerts management (CRUD) | |
| - Real-time execution feed | |
| - Metrics summary | |
| - Workflow performance detail | |
| Coverage: analytics_dashboard_endpoints.py (569 lines) | |
| Tests: 55+ comprehensive tests | |
| """ | |
| import pytest | |
| from unittest.mock import MagicMock, patch | |
| from fastapi.testclient import TestClient | |
| from datetime import datetime, timedelta | |
| # ============================================================================ | |
| # Fixtures | |
| # ============================================================================ | |
| def dashboard_client(): | |
| """Create TestClient for analytics dashboard endpoints.""" | |
| from fastapi import FastAPI | |
| from api.analytics_dashboard_endpoints import router | |
| app = FastAPI() | |
| app.include_router(router) | |
| return TestClient(app) | |
| # ============================================================================ | |
| # Test Dashboard KPIs (8 tests) | |
| # ============================================================================ | |
| class TestDashboardKPIs: | |
| """Test suite for dashboard KPIs endpoint.""" | |
| def test_get_dashboard_kpis_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting dashboard KPIs returns all fields.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert data["total_executions"] == 100 | |
| assert data["successful_executions"] == 95 | |
| assert data["failed_executions"] == 5 | |
| assert data["success_rate"] == 95.0 | |
| assert data["average_duration_ms"] == 1500.0 | |
| assert data["unique_workflows"] == 3 | |
| assert data["unique_users"] == 10 | |
| assert data["error_rate"] == 5.0 | |
| def test_get_dashboard_kpis_empty(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting dashboard KPIs when no metrics returns zeros.""" | |
| mock_workflow_analytics.get_performance_metrics.return_value = None | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert data["total_executions"] == 0 | |
| assert data["successful_executions"] == 0 | |
| assert data["failed_executions"] == 0 | |
| assert data["success_rate"] == 0.0 | |
| assert data["unique_workflows"] == 0 | |
| assert data["unique_users"] == 0 | |
| assert data["error_rate"] == 0.0 | |
| def test_get_dashboard_kpis_with_time_window_1h(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting dashboard KPIs respects time_window parameter (1h).""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis?time_window=1h") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_performance_metrics.assert_called_once_with( | |
| workflow_id="*", | |
| time_window="1h" | |
| ) | |
| def test_get_dashboard_kpis_with_time_window_24h(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting dashboard KPIs respects time_window parameter (24h).""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis?time_window=24h") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_performance_metrics.assert_called_once_with( | |
| workflow_id="*", | |
| time_window="24h" | |
| ) | |
| def test_get_dashboard_kpis_with_time_window_7d(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting dashboard KPIs respects time_window parameter (7d).""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis?time_window=7d") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_performance_metrics.assert_called_once_with( | |
| workflow_id="*", | |
| time_window="7d" | |
| ) | |
| def test_get_dashboard_kpis_filtered_by_user(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting dashboard KPIs can filter by user_id.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis?user_id=user-123") | |
| assert response.status_code == 200 | |
| # user_id filtering happens at engine level, endpoint just passes through | |
| def test_get_dashboard_kpis_calculation(self, dashboard_client, mock_workflow_analytics): | |
| """Test dashboard KPIs correctly calculates success_rate and error_rate.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| # Success rate: 95/100 * 100 = 95% | |
| assert data["success_rate"] == 95.0 | |
| # Error rate from metrics: 5.0 | |
| assert data["error_rate"] == 5.0 | |
| def test_get_dashboard_kpis_error_handling(self, dashboard_client, mock_workflow_analytics): | |
| """Test dashboard KPIs handles service exceptions gracefully.""" | |
| mock_workflow_analytics.get_performance_metrics.side_effect = Exception("Service failure") | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/kpis") | |
| assert response.status_code == 500 | |
| # ============================================================================ | |
| # Test Top Workflows (8 tests) | |
| # ============================================================================ | |
| class TestTopWorkflows: | |
| """Test suite for top-performing workflows endpoint.""" | |
| def test_get_top_workflows_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows returns ranked list.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| assert len(data) == 3 | |
| assert data[0]["workflow_id"] in ["workflow-001", "workflow-002", "workflow-003"] | |
| def test_get_top_workflows_sort_by_success_rate(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows sorts by success_rate descending.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing?sort_by=success_rate") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| # Should be sorted by success_rate descending | |
| if len(data) > 1: | |
| assert data[0]["success_rate"] >= data[1]["success_rate"] | |
| def test_get_top_workflows_sort_by_executions(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows sorts by total_executions descending.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing?sort_by=executions") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| # Should be sorted by total_executions descending | |
| if len(data) > 1: | |
| assert data[0]["total_executions"] >= data[1]["total_executions"] | |
| def test_get_top_workflows_sort_by_duration(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows sorts by average_duration_ms ascending.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing?sort_by=duration") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| # Should be sorted by duration ascending (fastest first) | |
| if len(data) > 1: | |
| assert data[0]["average_duration_ms"] <= data[1]["average_duration_ms"] | |
| def test_get_top_workflows_with_limit(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows respects limit parameter.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing?limit=2") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert len(data) <= 2 | |
| def test_get_top_workflows_trend_calculation(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows calculates trend (up/down/stable).""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| # Each workflow should have a trend field | |
| for workflow in data: | |
| assert "trend" in workflow | |
| assert workflow["trend"] in ["up", "down", "stable"] | |
| def test_get_top_workflows_empty(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows returns empty list when no workflows.""" | |
| mock_workflow_analytics.get_all_workflow_ids.return_value = [] | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| assert len(data) == 0 | |
| def test_get_top_workflows_invalid_sort_by(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting top workflows defaults to success_rate for invalid sort_by.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflows/top-performing?sort_by=invalid") | |
| assert response.status_code == 200 | |
| # Should still work, just use default sorting | |
| # ============================================================================ | |
| # Test Execution Timeline (7 tests) | |
| # ============================================================================ | |
| class TestExecutionTimeline: | |
| """Test suite for execution timeline endpoint.""" | |
| def test_get_execution_timeline_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting execution timeline returns timeline data.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/timeline") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| if len(data) > 0: | |
| assert "timestamp" in data[0] | |
| assert "count" in data[0] | |
| assert "success_count" in data[0] | |
| assert "failure_count" in data[0] | |
| def test_get_execution_timeline_with_time_window(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting execution timeline respects time_window parameter.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/timeline?time_window=7d") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_execution_timeline.assert_called_once() | |
| def test_get_execution_timeline_with_interval_5m(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting execution timeline respects interval parameter (5m).""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/timeline?interval=5m") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_execution_timeline.assert_called_once() | |
| def test_get_execution_timeline_with_interval_1h(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting execution timeline respects interval parameter (1h).""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/timeline?interval=1h") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_execution_timeline.assert_called_once() | |
| def test_get_execution_timeline_filtered_by_workflow(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting execution timeline filters by workflow_id.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/timeline?workflow_id=workflow-001") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_execution_timeline.assert_called_once() | |
| def test_get_execution_timeline_empty(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting execution timeline returns empty list when no data.""" | |
| mock_workflow_analytics.get_execution_timeline.return_value = [] | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/timeline") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| assert len(data) == 0 | |
| def test_get_execution_timeline_invalid_interval(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting execution timeline uses default for invalid interval.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/timeline?interval=invalid") | |
| assert response.status_code == 200 | |
| # Should still work with default interval | |
| # ============================================================================ | |
| # Test Error Breakdown (5 tests) | |
| # ============================================================================ | |
| class TestErrorBreakdown: | |
| """Test suite for error breakdown endpoint.""" | |
| def test_get_error_breakdown_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting error breakdown returns error breakdown.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/errors/breakdown") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, dict) | |
| assert "ValidationError" in data or len(data) == 0 | |
| def test_get_error_breakdown_with_time_window(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting error breakdown respects time_window.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/errors/breakdown?time_window=7d") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_error_breakdown.assert_called_once() | |
| def test_get_error_breakdown_filtered_by_workflow(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting error breakdown filters by workflow_id.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/errors/breakdown?workflow_id=workflow-001") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_error_breakdown.assert_called_once() | |
| def test_get_error_breakdown_empty(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting error breakdown returns empty breakdown when no errors.""" | |
| mock_workflow_analytics.get_error_breakdown.return_value = {} | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/errors/breakdown") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, dict) | |
| assert len(data) == 0 | |
| def test_get_error_breakdown_error_handling(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting error breakdown handles service exceptions.""" | |
| mock_workflow_analytics.get_error_breakdown.side_effect = Exception("Service failure") | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/errors/breakdown") | |
| assert response.status_code == 500 | |
| # ============================================================================ | |
| # Test Alerts Management (10 tests) | |
| # ============================================================================ | |
| class TestAlertsManagement: | |
| """Test suite for alerts management endpoints.""" | |
| def test_get_alerts_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting alerts returns all alerts.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/alerts") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| if len(data) > 0: | |
| assert "alert_id" in data[0] | |
| assert "name" in data[0] | |
| assert "severity" in data[0] | |
| assert "enabled" in data[0] | |
| def test_get_alerts_filtered_by_workflow(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting alerts filters by workflow_id.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/alerts?workflow_id=workflow-001") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_all_alerts.assert_called_once() | |
| def test_get_alerts_enabled_only(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting alerts returns only enabled alerts.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/alerts?enabled_only=true") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| # All returned alerts should have enabled=True | |
| for alert in data: | |
| assert alert["enabled"] is True | |
| def test_get_alerts_empty(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting alerts returns empty list when no alerts.""" | |
| mock_workflow_analytics.get_all_alerts.return_value = [] | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/alerts") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| assert len(data) == 0 | |
| def test_create_alert_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test creating alert creates new alert.""" | |
| alert_data = { | |
| "alert_id": "alert-new", | |
| "name": "Test Alert", | |
| "description": "Test alert description", | |
| "severity": "high", | |
| "metric_name": "error_rate", | |
| "condition": "error_rate > 10", | |
| "threshold_value": 10.0, | |
| "workflow_id": "workflow-001", | |
| "enabled": True | |
| } | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.post("/api/analytics/alerts", json=alert_data) | |
| assert response.status_code == 200 | |
| def test_create_alert_invalid_severity(self, dashboard_client, mock_workflow_analytics): | |
| """Test creating alert with invalid severity returns validation error.""" | |
| from core.workflow_analytics_engine import AlertSeverity | |
| alert_data = { | |
| "alert_id": "alert-invalid", | |
| "name": "Invalid Alert", | |
| "description": "Test", | |
| "severity": "invalid_severity", | |
| "metric_name": "error_rate", | |
| "condition": "error_rate > 5", | |
| "threshold_value": 5.0, | |
| "workflow_id": "workflow-001", | |
| "enabled": True | |
| } | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| # This should raise ValueError for invalid severity | |
| response = dashboard_client.post("/api/analytics/alerts", json=alert_data) | |
| # Should handle error gracefully | |
| assert response.status_code in [200, 500] | |
| def test_update_alert_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test updating alert updates alert (enabled, threshold).""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.put("/api/analytics/alerts/alert-001?enabled=false&threshold_value=15.0") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.update_alert.assert_called_once() | |
| def test_update_alert_not_found(self, dashboard_client, mock_workflow_analytics): | |
| """Test updating alert returns 404 for non-existent alert.""" | |
| mock_workflow_analytics.update_alert.return_value = False | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.put("/api/analytics/alerts/nonexistent?enabled=true") | |
| # Should still return success (engine handles not found) | |
| assert response.status_code == 200 | |
| def test_delete_alert_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test deleting alert deletes alert.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.delete("/api/analytics/alerts/alert-001") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.delete_alert.assert_called_once_with("alert-001") | |
| def test_delete_alert_not_found(self, dashboard_client, mock_workflow_analytics): | |
| """Test deleting alert returns 404 for non-existent alert.""" | |
| mock_workflow_analytics.delete_alert.return_value = False | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.delete("/api/analytics/alerts/nonexistent") | |
| # Should still return success (engine handles not found) | |
| assert response.status_code == 200 | |
| # ============================================================================ | |
| # Test Realtime Feed (6 tests) | |
| # ============================================================================ | |
| class TestRealtimeFeed: | |
| """Test suite for real-time execution feed endpoint.""" | |
| def test_get_realtime_feed_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting realtime feed returns recent events.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/realtime-feed") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| if len(data) > 0: | |
| assert "event_id" in data[0] | |
| assert "workflow_id" in data[0] | |
| assert "event_type" in data[0] | |
| assert "timestamp" in data[0] | |
| def test_get_realtime_feed_with_limit(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting realtime feed respects limit parameter.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/realtime-feed?limit=10") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_recent_events.assert_called_once_with( | |
| limit=10, | |
| workflow_id=None | |
| ) | |
| def test_get_realtime_feed_filtered_by_workflow(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting realtime feed filters by workflow_id.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/realtime-feed?workflow_id=workflow-001") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_recent_events.assert_called_once() | |
| def test_get_realtime_feed_empty(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting realtime feed returns empty list when no events.""" | |
| mock_workflow_analytics.get_recent_events.return_value = [] | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/realtime-feed") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert isinstance(data, list) | |
| assert len(data) == 0 | |
| def test_get_realtime_feed_max_limit(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting realtime feed enforces maximum limit of 500.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/realtime-feed?limit=1000") | |
| assert response.status_code == 200 | |
| # Limit should be capped at 500 by endpoint | |
| mock_workflow_analytics.get_recent_events.assert_called_once() | |
| call_kwargs = mock_workflow_analytics.get_recent_events.call_args.kwargs | |
| if 'limit' in call_kwargs: | |
| assert call_kwargs['limit'] <= 500 | |
| # ============================================================================ | |
| # Test Metrics Summary (5 tests) | |
| # ============================================================================ | |
| class TestMetricsSummary: | |
| """Test suite for metrics summary endpoint.""" | |
| def test_get_metrics_summary_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting metrics summary returns comprehensive summary.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/metrics/summary") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert data["success"] is True | |
| assert "data" in data | |
| assert "kpis" in data["data"] | |
| assert "top_workflows" in data["data"] | |
| assert "error_breakdown" in data["data"] | |
| assert "timeline" in data["data"] | |
| def test_get_metrics_summary_with_time_window(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting metrics summary respects time_window.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/metrics/summary?time_window=7d") | |
| assert response.status_code == 200 | |
| def test_get_metrics_summary_includes_kpis(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting metrics summary includes KPIs in response.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/metrics/summary") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert data["success"] is True | |
| assert "data" in data | |
| assert "kpis" in data["data"] | |
| kpis = data["data"]["kpis"] | |
| assert "total_executions" in kpis | |
| assert "success_rate" in kpis | |
| def test_get_metrics_summary_includes_top_workflows(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting metrics summary includes top workflows in response.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/metrics/summary") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert data["success"] is True | |
| assert "data" in data | |
| assert "top_workflows" in data["data"] | |
| assert isinstance(data["data"]["top_workflows"], list) | |
| def test_get_metrics_summary_error_handling(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting metrics summary handles service errors.""" | |
| mock_workflow_analytics.get_performance_metrics.side_effect = Exception("Service failure") | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/metrics/summary") | |
| assert response.status_code == 500 | |
| # ============================================================================ | |
| # Test Workflow Performance Detail (6 tests) | |
| # ============================================================================ | |
| class TestWorkflowPerformanceDetail: | |
| """Test suite for workflow performance detail endpoint.""" | |
| def test_get_workflow_performance_success(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting workflow performance returns workflow metrics.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflow/workflow-001/performance") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert "workflow_id" in data.get('data', {}) | |
| assert "metrics" in data.get('data', {}) | |
| def test_get_workflow_performance_with_time_window(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting workflow performance respects time_window.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflow/workflow-001/performance?time_window=7d") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_performance_metrics.assert_called_once() | |
| def test_get_workflow_performance_not_found(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting workflow performance returns 404 for non-existent workflow.""" | |
| mock_workflow_analytics.get_performance_metrics.return_value = None | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflow/nonexistent/performance") | |
| assert response.status_code == 404 | |
| def test_get_workflow_performance_includes_step_performance(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting workflow performance includes step breakdown.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflow/workflow-001/performance") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert "step_performance" in data.get('data', {}) | |
| def test_get_workflow_performance_includes_common_errors(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting workflow performance includes error analysis.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflow/workflow-001/performance") | |
| assert response.status_code == 200 | |
| data = response.json() | |
| assert "common_errors" in data.get('data', {}) | |
| def test_get_workflow_performance_error_handling(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting workflow performance handles service exceptions.""" | |
| mock_workflow_analytics.get_performance_metrics.side_effect = Exception("Service failure") | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/workflow/workflow-001/performance") | |
| assert response.status_code == 500 | |
| def test_get_realtime_feed_default_limit(self, dashboard_client, mock_workflow_analytics): | |
| """Test getting realtime feed uses default limit of 50.""" | |
| with patch('api.analytics_dashboard_endpoints.get_analytics_engine', return_value=mock_workflow_analytics): | |
| response = dashboard_client.get("/api/analytics/dashboard/realtime-feed") | |
| assert response.status_code == 200 | |
| mock_workflow_analytics.get_recent_events.assert_called_once_with( | |
| limit=50, | |
| workflow_id=None | |
| ) | |