use serde::{Deserialize, Serialize};
use crate::metric::{MetricClass, MetricDecision};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CompareReportsRequest {
pub report_id: String,
pub baseline_report_id: String,
#[serde(default)]
pub minimum_pass_rate: f64,
#[serde(default = "default_max_infra_rate")]
pub maximum_infra_failure_rate: f64,
#[serde(default = "default_significance")]
pub significance_level: f64,
#[serde(default)]
pub minimum_effect: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub policy: Option<GatePolicy>,
}
impl CompareReportsRequest {
pub fn effective_policy(&self) -> GatePolicy {
self.policy.clone().unwrap_or_else(|| GatePolicy {
minimum_pass_rate: self.minimum_pass_rate,
maximum_infra_failure_rate: self.maximum_infra_failure_rate,
significance_level: self.significance_level,
minimum_effect: self.minimum_effect,
..GatePolicy::default()
})
}
}
fn default_max_infra_rate() -> f64 {
1.0
}
fn default_significance() -> f64 {
0.05
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GatePolicy {
#[serde(default)]
pub minimum_pass_rate: f64,
#[serde(default = "default_max_infra_rate")]
pub maximum_infra_failure_rate: f64,
#[serde(default = "default_significance")]
pub significance_level: f64,
#[serde(default)]
pub minimum_effect: f64,
#[serde(default)]
pub goal_minimum_rate: f64,
#[serde(default)]
pub overrides: Vec<MetricThreshold>,
}
impl Default for GatePolicy {
fn default() -> Self {
Self {
minimum_pass_rate: 0.0,
maximum_infra_failure_rate: 1.0,
significance_level: 0.05,
minimum_effect: 0.0,
goal_minimum_rate: 0.0,
overrides: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct MetricThreshold {
pub evaluator_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub minimum_rate: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub maximum_regression: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ViolationReason {
GuardrailFailed,
BelowMinimumRate,
SignificantRegression,
InfrastructureFailureRate,
AggregatePassRate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricViolation {
pub evaluator_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub evaluator_version: Option<String>,
pub class: MetricClass,
pub reason: ViolationReason,
pub decision: MetricDecision,
pub observed_rate: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub baseline_rate: Option<f64>,
pub delta: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub p_value: Option<f64>,
pub detail: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GateDecision {
pub passed: bool,
#[serde(default)]
pub blocking_metrics: Vec<MetricViolation>,
#[serde(default)]
pub warnings: Vec<MetricViolation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CaseOutcomeDiff {
pub case_id: String,
pub case_version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub variant_id: Option<String>,
pub baseline_passed: bool,
pub current_passed: bool,
pub flaky_signal: bool,
#[serde(default)]
pub historical_flake_rate: f64,
#[serde(default)]
pub historical_samples: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MetricComparison {
pub evaluator_id: String,
pub evaluator_version: String,
pub class: MetricClass,
pub current_rate: f64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub baseline_rate: Option<f64>,
pub delta: f64,
pub p_value: f64,
pub statistically_significant: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReportComparison {
pub report_id: String,
pub baseline_report_id: String,
pub pass_rate_delta: f64,
pub infra_failure_rate_delta: f64,
pub pass_rate_confidence_low: f64,
pub pass_rate_confidence_high: f64,
pub effect_confidence_low: f64,
pub effect_confidence_high: f64,
pub p_value: f64,
pub statistically_significant: bool,
pub changed_cases: Vec<CaseOutcomeDiff>,
#[serde(default)]
pub metric_comparisons: Vec<MetricComparison>,
pub gate_passed: bool,
#[serde(default)]
pub gate: GateDecision,
}