""" Unit tests for visualization and reporting system. Tests the comprehensive visualization and reporting system to analyze performance data and generate actionable insights. """ import json import pytest import tempfile from pathlib import Path from unittest.mock import MagicMock, patch, mock_open from typing import Dict, List, Any from datetime import datetime, timezone import pandas as pd import plotly.graph_objects as go from plotly.subplots import make_subplots from src.services.performance import PerformanceMetrics from src.services.performance_profiling import BenchmarkData class TestVisualizationReportingSystem: """Test the visualization and reporting system.""" @pytest.fixture def sample_benchmark_data(self): """Create sample benchmark data for testing.""" return [ BenchmarkData( operation_name="transcription", batch_size=1, duration_seconds=10.5, peak_memory_mb=1024.0, throughput_items_per_second=2.5, timestamp=datetime.now(timezone.utc) ), BenchmarkData( operation_name="transcription", batch_size=2, duration_seconds=8.2, peak_memory_mb=1536.0, throughput_items_per_second=3.1, timestamp=datetime.now(timezone.utc) ), BenchmarkData( operation_name="transcription", batch_size=4, duration_seconds=6.8, peak_memory_mb=2048.0, throughput_items_per_second=3.7, timestamp=datetime.now(timezone.utc) ) ] @pytest.fixture def sample_performance_metrics(self): """Create sample performance metrics for testing.""" return [ PerformanceMetrics( operation="transcription", duration_seconds=10.0, memory_peak_mb=1024.0, cpu_peak_percent=50.0, throughput_items_per_second=2.0, error_count=0, success_count=10, total_count=10 ), PerformanceMetrics( operation="diarization", duration_seconds=15.0, memory_peak_mb=1536.0, cpu_peak_percent=60.0, throughput_items_per_second=1.5, error_count=1, success_count=9, total_count=10 ) ] def test_interactive_chart_creation(self, sample_benchmark_data): """Test creation of interactive charts using Plotly.""" from src.services.visualization_reporting import InteractiveChartGenerator generator = InteractiveChartGenerator() # Test throughput chart fig = generator.create_throughput_chart(sample_benchmark_data) assert isinstance(fig, go.Figure) assert len(fig.data) > 0 assert fig.layout.title.text == "Transcription Throughput by Batch Size" # Test memory usage chart fig = generator.create_memory_chart(sample_benchmark_data) assert isinstance(fig, go.Figure) assert len(fig.data) > 0 assert fig.layout.title.text == "Memory Usage by Batch Size" # Test combined chart fig = generator.create_combined_chart(sample_benchmark_data) assert isinstance(fig, go.Figure) assert len(fig.data) > 0 def test_bottleneck_identification(self, sample_performance_metrics): """Test bottleneck identification algorithms.""" from src.services.visualization_reporting import BottleneckAnalyzer analyzer = BottleneckAnalyzer() # Test bottleneck identification bottlenecks = analyzer.identify_bottlenecks(sample_performance_metrics) assert isinstance(bottlenecks, list) assert len(bottlenecks) > 0 # Check that bottlenecks have required fields for bottleneck in bottlenecks: assert 'component' in bottleneck assert 'severity' in bottleneck assert 'description' in bottleneck assert 'recommendation' in bottleneck def test_report_generation_formats(self, sample_benchmark_data, tmp_path): """Test report generation in various formats.""" from src.services.visualization_reporting import ReportGenerator generator = ReportGenerator() # Test HTML report html_path = tmp_path / "report.html" generator.generate_html_report(sample_benchmark_data, html_path) assert html_path.exists() # Test PDF report pdf_path = tmp_path / "report.pdf" generator.generate_pdf_report(sample_benchmark_data, pdf_path) assert pdf_path.exists() # Test CSV export csv_path = tmp_path / "data.csv" generator.export_csv(sample_benchmark_data, csv_path) assert csv_path.exists() # Test JSON export json_path = tmp_path / "data.json" generator.export_json(sample_benchmark_data, json_path) assert json_path.exists() def test_comparison_views(self, sample_benchmark_data): """Test before/after optimization analysis.""" from src.services.visualization_reporting import ComparisonAnalyzer analyzer = ComparisonAnalyzer() # Create "before" and "after" data before_data = sample_benchmark_data[:2] # First two entries after_data = sample_benchmark_data[1:] # Last two entries # Test comparison analysis comparison = analyzer.compare_performance(before_data, after_data) assert isinstance(comparison, dict) assert 'improvements' in comparison assert 'regressions' in comparison assert 'summary' in comparison # Test comparison chart fig = analyzer.create_comparison_chart(before_data, after_data) assert isinstance(fig, go.Figure) assert len(fig.data) > 0 def test_trend_analysis(self, sample_benchmark_data): """Test trend analysis for performance metrics over time.""" from src.services.visualization_reporting import TrendAnalyzer analyzer = TrendAnalyzer() # Test trend calculation trends = analyzer.calculate_trends(sample_benchmark_data) assert isinstance(trends, dict) assert 'throughput_trend' in trends assert 'memory_trend' in trends assert 'duration_trend' in trends # Test trend visualization fig = analyzer.create_trend_chart(sample_benchmark_data) assert isinstance(fig, go.Figure) assert len(fig.data) > 0 def test_report_templates(self, sample_benchmark_data, tmp_path): """Test different report templates.""" from src.services.visualization_reporting import ReportTemplateManager manager = ReportTemplateManager() # Test executive summary template exec_path = tmp_path / "executive_summary.html" manager.generate_executive_summary(sample_benchmark_data, exec_path) assert exec_path.exists() # Test detailed technical report template tech_path = tmp_path / "technical_report.html" manager.generate_technical_report(sample_benchmark_data, tech_path) assert tech_path.exists() # Test custom template custom_path = tmp_path / "custom_report.html" template_vars = {"title": "Custom Report", "author": "Test User"} manager.generate_custom_report(sample_benchmark_data, custom_path, template_vars) assert custom_path.exists() def test_export_functionality(self, sample_benchmark_data, tmp_path): """Test export functionality for various formats.""" from src.services.visualization_reporting import DataExporter exporter = DataExporter() # Test CSV export csv_path = tmp_path / "export.csv" exporter.export_to_csv(sample_benchmark_data, csv_path) assert csv_path.exists() # Verify CSV content df = pd.read_csv(csv_path) assert len(df) == len(sample_benchmark_data) assert 'operation_name' in df.columns assert 'batch_size' in df.columns # Test JSON export json_path = tmp_path / "export.json" exporter.export_to_json(sample_benchmark_data, json_path) assert json_path.exists() # Verify JSON content with open(json_path, 'r') as f: data = json.load(f) assert isinstance(data, list) assert len(data) == len(sample_benchmark_data) def test_chart_customization(self, sample_benchmark_data): """Test chart customization options.""" from src.services.visualization_reporting import InteractiveChartGenerator generator = InteractiveChartGenerator() # Test custom colors custom_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1'] fig = generator.create_throughput_chart( sample_benchmark_data, colors=custom_colors ) assert isinstance(fig, go.Figure) # Test custom layout custom_layout = { 'title': 'Custom Title', 'xaxis_title': 'Custom X Axis', 'yaxis_title': 'Custom Y Axis' } fig = generator.create_memory_chart( sample_benchmark_data, layout=custom_layout ) assert fig.layout.title.text == 'Custom Title' assert fig.layout.xaxis.title.text == 'Custom X Axis' def test_performance_insights(self, sample_performance_metrics): """Test performance insights generation.""" from src.services.visualization_reporting import PerformanceInsightsGenerator generator = PerformanceInsightsGenerator() # Test insights generation insights = generator.generate_insights(sample_performance_metrics) assert isinstance(insights, list) assert len(insights) > 0 # Check insight structure for insight in insights: assert 'type' in insight assert 'message' in insight assert 'severity' in insight assert 'actionable' in insight def test_report_validation(self, sample_benchmark_data, tmp_path): """Test report validation and error handling.""" from src.services.visualization_reporting import ReportValidator validator = ReportValidator() # Test valid data is_valid = validator.validate_data(sample_benchmark_data) assert is_valid # Test invalid data invalid_data = [{"invalid": "data"}] is_valid = validator.validate_data(invalid_data) assert not is_valid # Test report file validation report_path = tmp_path / "test_report.html" with open(report_path, 'w') as f: f.write("Test Report") is_valid = validator.validate_report_file(report_path) assert is_valid class TestVisualizationReportingIntegration: """Integration tests for visualization and reporting system.""" @pytest.mark.asyncio async def test_complete_reporting_workflow(self, tmp_path): """Test the complete reporting workflow.""" from src.services.visualization_reporting import ( ReportGenerator, InteractiveChartGenerator, BottleneckAnalyzer, ComparisonAnalyzer, TrendAnalyzer ) # Create sample data sample_data = [ BenchmarkData( operation_name="transcription", batch_size=1, duration_seconds=10.0, peak_memory_mb=1024.0, throughput_items_per_second=2.0, timestamp=datetime.now(timezone.utc) ), BenchmarkData( operation_name="transcription", batch_size=2, duration_seconds=8.0, peak_memory_mb=1536.0, throughput_items_per_second=2.5, timestamp=datetime.now(timezone.utc) ) ] # Initialize components generator = ReportGenerator() chart_generator = InteractiveChartGenerator() bottleneck_analyzer = BottleneckAnalyzer() comparison_analyzer = ComparisonAnalyzer() trend_analyzer = TrendAnalyzer() # Generate charts throughput_chart = chart_generator.create_throughput_chart(sample_data) memory_chart = chart_generator.create_memory_chart(sample_data) combined_chart = chart_generator.create_combined_chart(sample_data) # Analyze bottlenecks bottlenecks = bottleneck_analyzer.identify_bottlenecks(sample_data) # Generate trends trends = trend_analyzer.calculate_trends(sample_data) # Generate comprehensive report report_path = tmp_path / "comprehensive_report.html" generator.generate_comprehensive_report( sample_data, report_path, charts=[throughput_chart, memory_chart, combined_chart], bottlenecks=bottlenecks, trends=trends ) # Verify report was generated assert report_path.exists() # Verify report content with open(report_path, 'r') as f: content = f.read() assert "Performance Report" in content assert "Bottlenecks" in content assert "Trends" in content def test_multi_format_export(self, tmp_path): """Test exporting data in multiple formats.""" from src.services.visualization_reporting import MultiFormatExporter exporter = MultiFormatExporter() # Create sample data sample_data = [ BenchmarkData( operation_name="test", batch_size=1, duration_seconds=5.0, peak_memory_mb=512.0, throughput_items_per_second=1.0, timestamp=datetime.now(timezone.utc) ) ] # Export in multiple formats export_paths = exporter.export_all_formats(sample_data, tmp_path) # Verify all formats were created assert 'html' in export_paths assert 'pdf' in export_paths assert 'csv' in export_paths assert 'json' in export_paths # Verify files exist for format_type, path in export_paths.items(): assert Path(path).exists() def test_report_scheduling(self): """Test automated report scheduling.""" from src.services.visualization_reporting import ReportScheduler scheduler = ReportScheduler() # Test schedule creation schedule = scheduler.create_schedule( frequency="daily", time="09:00", report_type="executive_summary" ) assert schedule['frequency'] == "daily" assert schedule['time'] == "09:00" assert schedule['report_type'] == "executive_summary" # Test schedule validation is_valid = scheduler.validate_schedule(schedule) assert is_valid # Test invalid schedule invalid_schedule = { 'frequency': "invalid", 'time': "25:00", 'report_type': "unknown" } is_valid = scheduler.validate_schedule(invalid_schedule) assert not is_valid