#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum InterpretationLevel {
Healthy,
Moderate,
High,
Critical,
}
pub const IIS_MODERATE: f64 = 2.0;
pub const IIS_HIGH: f64 = 5.0;
pub const IIS_CRITICAL: f64 = 10.0;
pub const WASTE_RATIO_MODERATE: f64 = 0.10;
pub const WASTE_RATIO_HIGH: f64 = 0.30;
pub const WASTE_RATIO_CRITICAL: f64 = 0.50;
impl InterpretationLevel {
#[inline]
fn classify(value: f64, moderate: f64, high: f64, critical: f64) -> Self {
if value >= critical {
Self::Critical
} else if value >= high {
Self::High
} else if value >= moderate {
Self::Moderate
} else {
Self::Healthy
}
}
#[must_use]
pub fn for_iis(iis: f64) -> Self {
Self::classify(iis, IIS_MODERATE, IIS_HIGH, IIS_CRITICAL)
}
#[must_use]
pub fn for_waste_ratio(ratio: f64) -> Self {
Self::classify(
ratio,
WASTE_RATIO_MODERATE,
WASTE_RATIO_HIGH,
WASTE_RATIO_CRITICAL,
)
}
#[must_use]
pub const fn short_label(self) -> &'static str {
match self {
Self::Healthy => "healthy",
Self::Moderate => "moderate",
Self::High => "high",
Self::Critical => "critical",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iis_healthy_below_moderate() {
assert_eq!(
InterpretationLevel::for_iis(0.0),
InterpretationLevel::Healthy
);
assert_eq!(
InterpretationLevel::for_iis(1.0),
InterpretationLevel::Healthy
);
assert_eq!(
InterpretationLevel::for_iis(1.99),
InterpretationLevel::Healthy
);
}
#[test]
fn iis_moderate_at_and_above_2() {
assert_eq!(
InterpretationLevel::for_iis(2.0),
InterpretationLevel::Moderate
);
assert_eq!(
InterpretationLevel::for_iis(3.5),
InterpretationLevel::Moderate
);
assert_eq!(
InterpretationLevel::for_iis(4.99),
InterpretationLevel::Moderate
);
}
#[test]
fn iis_high_at_and_above_5() {
assert_eq!(InterpretationLevel::for_iis(5.0), InterpretationLevel::High);
assert_eq!(InterpretationLevel::for_iis(7.5), InterpretationLevel::High);
assert_eq!(
InterpretationLevel::for_iis(9.99),
InterpretationLevel::High
);
}
#[test]
fn iis_critical_at_and_above_10() {
assert_eq!(
InterpretationLevel::for_iis(10.0),
InterpretationLevel::Critical
);
assert_eq!(
InterpretationLevel::for_iis(22.0),
InterpretationLevel::Critical
);
assert_eq!(
InterpretationLevel::for_iis(100.0),
InterpretationLevel::Critical
);
}
#[test]
fn iis_critical_matches_n_plus_one_detector_threshold() {
use crate::detect::n_plus_one::CRITICAL_OCCURRENCE_THRESHOLD;
#[allow(clippy::cast_precision_loss)]
let detector_threshold_f64 = CRITICAL_OCCURRENCE_THRESHOLD as f64;
assert!(
(IIS_CRITICAL - detector_threshold_f64).abs() < f64::EPSILON,
"IIS_CRITICAL ({IIS_CRITICAL}) drifted from \
detect::n_plus_one::CRITICAL_OCCURRENCE_THRESHOLD \
({CRITICAL_OCCURRENCE_THRESHOLD}); update both or neither"
);
}
#[test]
fn iis_high_matches_n_plus_one_threshold_default() {
let default_threshold = crate::config::Config::default()
.detection
.n_plus_one_threshold;
let threshold_f64 = f64::from(default_threshold);
assert!(
(IIS_HIGH - threshold_f64).abs() < f64::EPSILON,
"IIS_HIGH ({IIS_HIGH}) drifted away from \
Config::default().n_plus_one_threshold ({default_threshold}); \
update both or neither"
);
}
#[test]
fn waste_ratio_healthy_below_10_percent() {
assert_eq!(
InterpretationLevel::for_waste_ratio(0.0),
InterpretationLevel::Healthy
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.05),
InterpretationLevel::Healthy
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.09),
InterpretationLevel::Healthy
);
}
#[test]
fn waste_ratio_moderate_between_10_and_30_percent() {
assert_eq!(
InterpretationLevel::for_waste_ratio(0.10),
InterpretationLevel::Moderate
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.20),
InterpretationLevel::Moderate
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.29),
InterpretationLevel::Moderate
);
}
#[test]
fn waste_ratio_high_between_30_and_50_percent() {
assert_eq!(
InterpretationLevel::for_waste_ratio(0.30),
InterpretationLevel::High
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.40),
InterpretationLevel::High
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.49),
InterpretationLevel::High
);
}
#[test]
fn waste_ratio_critical_at_and_above_50_percent() {
assert_eq!(
InterpretationLevel::for_waste_ratio(0.50),
InterpretationLevel::Critical
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.75),
InterpretationLevel::Critical
);
assert_eq!(
InterpretationLevel::for_waste_ratio(0.9),
InterpretationLevel::Critical
);
assert_eq!(
InterpretationLevel::for_waste_ratio(1.0),
InterpretationLevel::Critical
);
}
#[test]
fn waste_ratio_high_matches_default_gate_threshold() {
assert!(
(WASTE_RATIO_HIGH - 0.30).abs() < f64::EPSILON,
"WASTE_RATIO_HIGH drifted from the default io_waste_ratio_max (0.30); \
update both or neither"
);
}
#[test]
fn short_label_returns_lowercase_string() {
assert_eq!(InterpretationLevel::Healthy.short_label(), "healthy");
assert_eq!(InterpretationLevel::Moderate.short_label(), "moderate");
assert_eq!(InterpretationLevel::High.short_label(), "high");
assert_eq!(InterpretationLevel::Critical.short_label(), "critical");
}
#[test]
fn nan_iis_classified_healthy() {
assert_eq!(
InterpretationLevel::for_iis(f64::NAN),
InterpretationLevel::Healthy
);
}
#[test]
fn nan_waste_ratio_classified_healthy() {
assert_eq!(
InterpretationLevel::for_waste_ratio(f64::NAN),
InterpretationLevel::Healthy
);
}
#[test]
fn positive_infinity_iis_classified_critical() {
assert_eq!(
InterpretationLevel::for_iis(f64::INFINITY),
InterpretationLevel::Critical
);
}
#[test]
fn negative_infinity_iis_classified_healthy() {
assert_eq!(
InterpretationLevel::for_iis(f64::NEG_INFINITY),
InterpretationLevel::Healthy
);
}
#[test]
fn positive_infinity_waste_ratio_classified_critical() {
assert_eq!(
InterpretationLevel::for_waste_ratio(f64::INFINITY),
InterpretationLevel::Critical
);
}
#[test]
fn negative_infinity_waste_ratio_classified_healthy() {
assert_eq!(
InterpretationLevel::for_waste_ratio(f64::NEG_INFINITY),
InterpretationLevel::Healthy
);
}
}