mockforge_core/ab_testing/
analytics.rs

1//! Analytics for A/B testing
2//!
3//! This module provides analytics and reporting functionality for A/B tests.
4
5use crate::ab_testing::types::{ABTestConfig, VariantAnalytics};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Summary report for an A/B test
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ABTestReport {
12    /// Test configuration
13    pub test_config: ABTestConfig,
14    /// Analytics for each variant
15    pub variant_analytics: HashMap<String, VariantAnalytics>,
16    /// Total requests across all variants
17    pub total_requests: u64,
18    /// Test start time
19    pub start_time: Option<chrono::DateTime<chrono::Utc>>,
20    /// Test end time (if ended)
21    pub end_time: Option<chrono::DateTime<chrono::Utc>>,
22    /// Whether the test is currently active
23    pub is_active: bool,
24}
25
26impl ABTestReport {
27    /// Create a new A/B test report
28    pub fn new(
29        test_config: ABTestConfig,
30        variant_analytics: HashMap<String, VariantAnalytics>,
31    ) -> Self {
32        let total_requests: u64 = variant_analytics.values().map(|a| a.request_count).sum();
33        let is_active = test_config.enabled
34            && test_config.start_time.map_or(true, |t| t <= chrono::Utc::now())
35            && test_config.end_time.map_or(true, |t| t >= chrono::Utc::now());
36
37        Self {
38            test_config,
39            variant_analytics,
40            total_requests,
41            start_time: None,
42            end_time: None,
43            is_active,
44        }
45    }
46
47    /// Get the best performing variant (highest success rate)
48    pub fn best_variant(&self) -> Option<&VariantAnalytics> {
49        self.variant_analytics.values().max_by(|a, b| {
50            a.success_rate()
51                .partial_cmp(&b.success_rate())
52                .unwrap_or(std::cmp::Ordering::Equal)
53        })
54    }
55
56    /// Get the worst performing variant (lowest success rate)
57    pub fn worst_variant(&self) -> Option<&VariantAnalytics> {
58        self.variant_analytics.values().min_by(|a, b| {
59            a.success_rate()
60                .partial_cmp(&b.success_rate())
61                .unwrap_or(std::cmp::Ordering::Equal)
62        })
63    }
64
65    /// Calculate statistical significance (simplified - would need proper statistical test in production)
66    pub fn statistical_significance(&self) -> f64 {
67        // Simplified calculation - in production, use proper statistical tests
68        // like chi-square or t-test
69        if self.variant_analytics.len() < 2 {
70            return 0.0;
71        }
72
73        let variants: Vec<&VariantAnalytics> = self.variant_analytics.values().collect();
74        if variants.len() < 2 {
75            return 0.0;
76        }
77
78        // Simple comparison of success rates
79        let success_rates: Vec<f64> = variants.iter().map(|v| v.success_rate()).collect();
80        let max_rate = success_rates.iter().fold(0.0f64, |a, &b| a.max(b));
81        let min_rate = success_rates.iter().fold(1.0f64, |a, &b| a.min(b));
82
83        // Difference between best and worst
84        (max_rate - min_rate) * 100.0
85    }
86}
87
88/// Comparison between two variants
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct VariantComparison {
91    /// First variant ID
92    pub variant_a_id: String,
93    /// Second variant ID
94    pub variant_b_id: String,
95    /// Success rate difference (A - B)
96    pub success_rate_diff: f64,
97    /// Response time difference in milliseconds (A - B)
98    pub response_time_diff_ms: f64,
99    /// Error rate difference (A - B)
100    pub error_rate_diff: f64,
101    /// Request count difference (A - B)
102    pub request_count_diff: i64,
103}
104
105impl VariantComparison {
106    /// Create a comparison between two variants
107    pub fn new(variant_a: &VariantAnalytics, variant_b: &VariantAnalytics) -> Self {
108        Self {
109            variant_a_id: variant_a.variant_id.clone(),
110            variant_b_id: variant_b.variant_id.clone(),
111            success_rate_diff: variant_a.success_rate() - variant_b.success_rate(),
112            response_time_diff_ms: variant_a.avg_response_time_ms - variant_b.avg_response_time_ms,
113            error_rate_diff: variant_a.error_rate() - variant_b.error_rate(),
114            request_count_diff: variant_a.request_count as i64 - variant_b.request_count as i64,
115        }
116    }
117}