annator-command-center / tests /api /test_analytics_dashboard_endpoints.py.bak4
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 7)
cc036ff verified
Raw
History Blame Contribute Delete
37.8 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
# ============================================================================
@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)
# ============================================================================
# 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/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
# 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/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/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/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()
# 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/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/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/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()
# 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/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
# 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/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
# 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/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
# ============================================================================
# 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/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
# 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/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
# ============================================================================
# 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/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
)