pub mod error;
pub(crate) mod parse;
mod types;
pub use error::ThresholdError;
pub use parse::parse_thresholds;
pub use types::{EvaluateParams, Metric, Operator, Threshold, ThresholdReport, ThresholdResult};
pub fn evaluate(params: EvaluateParams<'_>) -> ThresholdReport {
let mut results = Vec::with_capacity(params.thresholds.len());
for threshold in params.thresholds {
let actual = threshold.metric.resolve(params.report);
let passed = threshold.operator.evaluate(actual, threshold.value);
results.push(ThresholdResult {
threshold: threshold.clone(),
actual,
passed,
});
}
let total = results.len();
let passed = results.iter().filter(|r| r.passed).count();
let failed = total - passed;
ThresholdReport {
total,
passed,
failed,
results,
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use crate::command::run::{RunMode, RunStats};
use crate::http::RequestResult;
use crate::output::{RunReport, RunReportParams};
use super::*;
fn make_report_with_latency(latency_ms: u64, error_rate: f64) -> RunReport {
let total = 100usize;
let failed = (total as f64 * error_rate).round() as usize;
let result = RequestResult::new(Duration::from_millis(latency_ms), true, Some(200), None);
let stats = RunStats {
elapsed: Duration::from_secs(10),
template_duration: None,
response_stats: None,
results: vec![result],
mode: RunMode::Fixed,
curve_duration: None,
curve_stages: None,
total_requests: total,
total_failures: failed,
sample_rate: 1.0,
min_sample_rate: 1.0,
};
RunReport::from_params(RunReportParams {
stats: &stats,
reservoir_size: 100_000,
run_start: std::time::Instant::now(),
})
}
#[test]
fn evaluate_all_pass() {
let report = make_report_with_latency(50, 0.01);
let thresholds = vec![
Threshold {
metric: Metric::LatencyP99,
operator: Operator::Lt,
value: 500.0,
},
Threshold {
metric: Metric::ErrorRate,
operator: Operator::Lte,
value: 0.05,
},
];
let result = evaluate(EvaluateParams {
report: &report,
thresholds: &thresholds,
});
assert_eq!(result.total, 2);
assert_eq!(result.passed, 2);
assert_eq!(result.failed, 0);
assert!(result.all_passed());
}
#[test]
fn evaluate_all_fail() {
let report = make_report_with_latency(500, 0.5);
let thresholds = vec![
Threshold {
metric: Metric::LatencyP99,
operator: Operator::Lt,
value: 10.0,
},
Threshold {
metric: Metric::ErrorRate,
operator: Operator::Lt,
value: 0.01,
},
];
let result = evaluate(EvaluateParams {
report: &report,
thresholds: &thresholds,
});
assert_eq!(result.total, 2);
assert_eq!(result.passed, 0);
assert_eq!(result.failed, 2);
assert!(!result.all_passed());
}
#[test]
fn evaluate_mixed_results() {
let report = make_report_with_latency(50, 0.5);
let thresholds = vec![
Threshold {
metric: Metric::LatencyP99,
operator: Operator::Lt,
value: 500.0,
},
Threshold {
metric: Metric::ErrorRate,
operator: Operator::Lt,
value: 0.01,
},
];
let result = evaluate(EvaluateParams {
report: &report,
thresholds: &thresholds,
});
assert_eq!(result.total, 2);
assert_eq!(result.passed, 1);
assert_eq!(result.failed, 1);
assert!(!result.all_passed());
}
#[test]
fn evaluate_empty_thresholds_returns_empty_report() {
let report = make_report_with_latency(50, 0.01);
let thresholds: Vec<Threshold> = vec![];
let result = evaluate(EvaluateParams {
report: &report,
thresholds: &thresholds,
});
assert_eq!(result.total, 0);
assert_eq!(result.passed, 0);
assert_eq!(result.failed, 0);
assert!(result.results.is_empty());
assert!(result.all_passed());
}
}