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 total_skipped: 0,
66 template_stats: None,
67 response_stats: None,
68 curve_stats: None,
69 scenario_stats: None,
70 };
71 RunReport::from_params(RunReportParams { stats: &stats })
72 }
73
74 #[test]
75 fn evaluate_all_pass() {
76 let report = make_report_with_latency(50, 0.01);
77 let thresholds = vec![
78 Threshold {
79 metric: Metric::LatencyP99,
80 operator: Operator::Lt,
81 value: 500.0,
82 },
83 Threshold {
84 metric: Metric::ErrorRate,
85 operator: Operator::Lte,
86 value: 0.05,
87 },
88 ];
89 let result = evaluate(EvaluateParams {
90 report: &report,
91 thresholds: &thresholds,
92 });
93 assert_eq!(result.total, 2);
94 assert_eq!(result.passed, 2);
95 assert_eq!(result.failed, 0);
96 assert!(result.all_passed());
97 }
98
99 #[test]
100 fn evaluate_all_fail() {
101 let report = make_report_with_latency(500, 0.5);
102 let thresholds = vec![
103 Threshold {
104 metric: Metric::LatencyP99,
105 operator: Operator::Lt,
106 value: 10.0,
107 },
108 Threshold {
109 metric: Metric::ErrorRate,
110 operator: Operator::Lt,
111 value: 0.01,
112 },
113 ];
114 let result = evaluate(EvaluateParams {
115 report: &report,
116 thresholds: &thresholds,
117 });
118 assert_eq!(result.total, 2);
119 assert_eq!(result.passed, 0);
120 assert_eq!(result.failed, 2);
121 assert!(!result.all_passed());
122 }
123
124 #[test]
125 fn evaluate_mixed_results() {
126 let report = make_report_with_latency(50, 0.5);
127 let thresholds = vec![
128 Threshold {
130 metric: Metric::LatencyP99,
131 operator: Operator::Lt,
132 value: 500.0,
133 },
134 Threshold {
136 metric: Metric::ErrorRate,
137 operator: Operator::Lt,
138 value: 0.01,
139 },
140 ];
141 let result = evaluate(EvaluateParams {
142 report: &report,
143 thresholds: &thresholds,
144 });
145 assert_eq!(result.total, 2);
146 assert_eq!(result.passed, 1);
147 assert_eq!(result.failed, 1);
148 assert!(!result.all_passed());
149 }
150
151 #[test]
152 fn evaluate_empty_thresholds_returns_empty_report() {
153 let report = make_report_with_latency(50, 0.01);
154 let thresholds: Vec<Threshold> = vec![];
155 let result = evaluate(EvaluateParams {
156 report: &report,
157 thresholds: &thresholds,
158 });
159 assert_eq!(result.total, 0);
160 assert_eq!(result.passed, 0);
161 assert_eq!(result.failed, 0);
162 assert!(result.results.is_empty());
163 assert!(result.all_passed());
164 }
165}