lmn_core/threshold/
mod.rs1pub mod error;
2pub(crate) mod parse;
3mod types;
4
5pub use error::ThresholdError;
6pub use parse::parse_thresholds;
7pub use types::{EvaluateParams, Metric, Operator, Threshold, ThresholdReport, ThresholdResult};
8
9pub fn evaluate(params: EvaluateParams<'_>) -> ThresholdReport {
14 let mut results = Vec::with_capacity(params.thresholds.len());
15
16 for threshold in params.thresholds {
17 let actual = threshold.metric.resolve(params.report);
18 let passed = threshold.operator.evaluate(actual, threshold.value);
19 results.push(ThresholdResult {
20 threshold: threshold.clone(),
21 actual,
22 passed,
23 });
24 }
25
26 let total = results.len();
27 let passed = results.iter().filter(|r| r.passed).count();
28 let failed = total - passed;
29
30 ThresholdReport {
31 total,
32 passed,
33 failed,
34 results,
35 }
36}
37
38#[cfg(test)]
41mod tests {
42 use std::time::Duration;
43
44 use crate::execution::{RunMode, RunStats};
45 use crate::histogram::{LatencyHistogram, StatusCodeHistogram};
46 use crate::output::{RunReport, RunReportParams};
47
48 use super::*;
49
50 fn make_report_with_latency(latency_ms: u64, error_rate: f64) -> RunReport {
51 let total = 100u64;
52 let failed = (total as f64 * error_rate).round() as u64;
53 let mut latency = LatencyHistogram::new();
54 latency.record(Duration::from_millis(latency_ms));
55 let mut status_codes = StatusCodeHistogram::new();
56 status_codes.record(Some(200));
57
58 let stats = RunStats {
59 elapsed: Duration::from_secs(10),
60 mode: RunMode::Fixed,
61 latency,
62 status_codes,
63 total_requests: total,
64 total_failures: failed,
65 template_stats: None,
66 response_stats: None,
67 curve_stats: None,
68 };
69 RunReport::from_params(RunReportParams { stats: &stats })
70 }
71
72 #[test]
73 fn evaluate_all_pass() {
74 let report = make_report_with_latency(50, 0.01);
75 let thresholds = vec![
76 Threshold {
77 metric: Metric::LatencyP99,
78 operator: Operator::Lt,
79 value: 500.0,
80 },
81 Threshold {
82 metric: Metric::ErrorRate,
83 operator: Operator::Lte,
84 value: 0.05,
85 },
86 ];
87 let result = evaluate(EvaluateParams {
88 report: &report,
89 thresholds: &thresholds,
90 });
91 assert_eq!(result.total, 2);
92 assert_eq!(result.passed, 2);
93 assert_eq!(result.failed, 0);
94 assert!(result.all_passed());
95 }
96
97 #[test]
98 fn evaluate_all_fail() {
99 let report = make_report_with_latency(500, 0.5);
100 let thresholds = vec![
101 Threshold {
102 metric: Metric::LatencyP99,
103 operator: Operator::Lt,
104 value: 10.0,
105 },
106 Threshold {
107 metric: Metric::ErrorRate,
108 operator: Operator::Lt,
109 value: 0.01,
110 },
111 ];
112 let result = evaluate(EvaluateParams {
113 report: &report,
114 thresholds: &thresholds,
115 });
116 assert_eq!(result.total, 2);
117 assert_eq!(result.passed, 0);
118 assert_eq!(result.failed, 2);
119 assert!(!result.all_passed());
120 }
121
122 #[test]
123 fn evaluate_mixed_results() {
124 let report = make_report_with_latency(50, 0.5);
125 let thresholds = vec![
126 Threshold {
128 metric: Metric::LatencyP99,
129 operator: Operator::Lt,
130 value: 500.0,
131 },
132 Threshold {
134 metric: Metric::ErrorRate,
135 operator: Operator::Lt,
136 value: 0.01,
137 },
138 ];
139 let result = evaluate(EvaluateParams {
140 report: &report,
141 thresholds: &thresholds,
142 });
143 assert_eq!(result.total, 2);
144 assert_eq!(result.passed, 1);
145 assert_eq!(result.failed, 1);
146 assert!(!result.all_passed());
147 }
148
149 #[test]
150 fn evaluate_empty_thresholds_returns_empty_report() {
151 let report = make_report_with_latency(50, 0.01);
152 let thresholds: Vec<Threshold> = vec![];
153 let result = evaluate(EvaluateParams {
154 report: &report,
155 thresholds: &thresholds,
156 });
157 assert_eq!(result.total, 0);
158 assert_eq!(result.passed, 0);
159 assert_eq!(result.failed, 0);
160 assert!(result.results.is_empty());
161 assert!(result.all_passed());
162 }
163}