| """
|
| 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, AsyncMock, 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/api/analytics/dashboard/metrics/summary")
|
|
|
| assert response.status_code == 200
|
| data = response.json()
|
| assert "kpis" in data
|
| assert "top_workflows" in data
|
| assert "error_breakdown" in data
|
| assert "timeline" in 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/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/api/analytics/dashboard/metrics/summary")
|
|
|
| assert response.status_code == 200
|
| data = response.json()
|
| assert "kpis" in data
|
| kpis = 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/api/analytics/dashboard/metrics/summary")
|
|
|
| assert response.status_code == 200
|
| data = response.json()
|
| assert "top_workflows" in data
|
| assert isinstance(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/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/api/analytics/dashboard/workflow/workflow-001/performance")
|
|
|
| assert response.status_code == 200
|
| data = response.json()
|
| assert "workflow_id" in data
|
| assert "metrics" in 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/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/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/api/analytics/dashboard/workflow/workflow-001/performance")
|
|
|
| assert response.status_code == 200
|
| data = response.json()
|
| assert "step_performance" in 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/api/analytics/dashboard/workflow/workflow-001/performance")
|
|
|
| assert response.status_code == 200
|
| data = response.json()
|
| assert "common_errors" in 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/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/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
|
| )
|
|
|