| """ |
| 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 |
|
|
|
|
| |
| |
| |
|
|
| @pytest.fixture |
| 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) |
|
|
|
|
| |
| |
| |
|
|
| 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 |
| |
|
|
| 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() |
| |
| assert data["success_rate"] == 95.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 |
|
|
|
|
| |
| |
| |
|
|
| 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() |
| |
| 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() |
| |
| 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() |
| |
| 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() |
| |
| 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 |
| |
|
|
|
|
| |
| |
| |
|
|
| 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 |
| |
|
|
|
|
| |
| |
| |
|
|
| 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 |
|
|
|
|
| |
| |
| |
|
|
| 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() |
| |
| 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): |
| |
| response = dashboard_client.post("/api/analytics/alerts", json=alert_data) |
| |
| |
| 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") |
| |
| |
| 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") |
| |
| |
| assert response.status_code == 200 |
|
|
|
|
| |
| |
| |
|
|
| 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 |
| |
| 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 |
|
|
|
|
| |
| |
| |
|
|
| 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 |
|
|
|
|
| |
| |
| |
|
|
| 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 |
| ) |
|
|