test(app): refactor test to parametrized tests
This commit is contained in:
@@ -89,57 +89,130 @@ impl AlertService {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::app::ports::driven::{MockForFormattingMessage, MockForMonitoringSystem, MockForSendingNotification};
|
||||
use crate::app::{
|
||||
ports::driven::{
|
||||
ForGettingViolationData, MockForFormattingMessage, MockForGettingViolationData,
|
||||
MockForMonitoringSystem, MockForSendingNotification,
|
||||
},
|
||||
services::alert_service,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
const TEST_VALUE: f32 = 75.0;
|
||||
const TEST_THRESHOLD: f32 = 70.0;
|
||||
|
||||
#[test]
|
||||
fn test_alert_on_threshold_violation() {
|
||||
let mut notifier = MockForSendingNotification::new();
|
||||
let mut monitor = MockForMonitoringSystem::new();
|
||||
let mut formatter = MockForFormattingMessage::new();
|
||||
#[rstest]
|
||||
// No violations
|
||||
#[case(HashMap::new(), HashMap::new(), vec![])]
|
||||
// Single critical violation
|
||||
#[case(HashMap::from([("CPU".to_string(), true)]), HashMap::new(), vec!["critical CPU".to_string(), "resolved CPU".to_string()])]
|
||||
// Single warning violation
|
||||
#[case(HashMap::from([("Memory".to_string(), false)]), HashMap::new(), vec!["warning Memory".to_string(), "resolved Memory".to_string()])]
|
||||
// Multiple violations with level changes and one resolution
|
||||
#[case(HashMap::from([("CPU".to_string(), true), ("Memory".to_string(), false), ("Disk".to_string(), false)]),
|
||||
HashMap::from([("CPU".to_string(), false), ("Disk".to_string(), true)]),
|
||||
vec![
|
||||
"critical CPU".to_string(),
|
||||
"warning Memory".to_string(),
|
||||
"warning Disk".to_string(),
|
||||
"resolved Memory".to_string(),
|
||||
"warning CPU".to_string(),
|
||||
"critical Disk".to_string()
|
||||
]
|
||||
)]
|
||||
// No level changes
|
||||
#[case(
|
||||
HashMap::from([("CPU".to_string(), true), ("Memory".to_string(), false)]),
|
||||
HashMap::from([("CPU".to_string(), true), ("Memory".to_string(), false)]),
|
||||
vec!["critical CPU".to_string(), "warning Memory".to_string()]
|
||||
)]
|
||||
fn test_alert_on_threshold_violation_scenarios(
|
||||
#[case] first_violations: HashMap<String, bool>,
|
||||
#[case] second_violations: HashMap<String, bool>,
|
||||
#[case] expected_notifications: Vec<String>,
|
||||
) {
|
||||
let mut mock_monitor = MockForMonitoringSystem::new();
|
||||
let mut mock_formatter = MockForFormattingMessage::new();
|
||||
let mut mock_notifier = MockForSendingNotification::new();
|
||||
|
||||
notifier.expect_send_notification().returning(|_| Ok(()));
|
||||
monitor.expect_check_thresholds().returning(|| vec![]);
|
||||
monitor.expect_get_metrics().returning(|| HashMap::new());
|
||||
formatter.expect_format_violation().returning(|_| "".to_string());
|
||||
formatter.expect_format_summary().returning(|_| "".to_string());
|
||||
// Helper to build violations from a HashMap
|
||||
fn build_violations(map: &HashMap<String, bool>) -> Vec<Box<dyn ForGettingViolationData>> {
|
||||
map.iter()
|
||||
.map(|(name, &is_critical)| {
|
||||
let mut violation = MockForGettingViolationData::new();
|
||||
let name = name.clone();
|
||||
violation.expect_is_critical().return_const(is_critical);
|
||||
violation
|
||||
.expect_get_metric_name()
|
||||
.return_const(name.clone());
|
||||
violation.expect_get_metric_value().return_const(TEST_VALUE);
|
||||
violation
|
||||
.expect_get_threshold()
|
||||
.return_const(TEST_THRESHOLD);
|
||||
Box::new(violation) as Box<dyn ForGettingViolationData>
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
let mut service = new(
|
||||
Box::new(notifier),
|
||||
Box::new(monitor),
|
||||
Box::new(formatter),
|
||||
);
|
||||
|
||||
assert!(service.alert_on_threshold_violation().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alert_on_threshold_violation_handles_send_error() {
|
||||
let mut notifier = MockForSendingNotification::new();
|
||||
let mut monitor = MockForMonitoringSystem::new();
|
||||
let mut formatter = MockForFormattingMessage::new();
|
||||
|
||||
|
||||
monitor.expect_check_thresholds().returning(|| {
|
||||
let mut violation = crate::app::ports::driven::MockForGettingViolationData::new();
|
||||
violation.expect_get_metric_name().return_const("cpu".to_string());
|
||||
violation.expect_is_critical().return_const(true);
|
||||
vec![Box::new(violation)]
|
||||
// The test will call check_thresholds twice: first and second run
|
||||
let mut call_count = 0;
|
||||
let first_violations_clone = first_violations.clone();
|
||||
let second_violations_clone = second_violations.clone();
|
||||
mock_monitor.expect_check_thresholds().returning(move || {
|
||||
call_count += 1;
|
||||
if call_count == 1 {
|
||||
build_violations(&first_violations_clone)
|
||||
} else {
|
||||
build_violations(&second_violations_clone)
|
||||
}
|
||||
});
|
||||
formatter.expect_format_violation().returning(|_| "Test violation".to_string());
|
||||
|
||||
notifier.expect_send_notification()
|
||||
.returning(|_| Err("Network error".into()));
|
||||
// Formatter returns a string based on the violation
|
||||
mock_formatter
|
||||
.expect_format_violation()
|
||||
.returning(|violation| {
|
||||
let level = if violation.is_critical() {
|
||||
"critical"
|
||||
} else {
|
||||
"warning"
|
||||
};
|
||||
format!("{} {}", level, violation.get_metric_name())
|
||||
});
|
||||
mock_formatter
|
||||
.expect_format_resolution()
|
||||
.returning(|metric_name| format!("resolved {}", metric_name));
|
||||
|
||||
let mut service = new(
|
||||
Box::new(notifier),
|
||||
Box::new(monitor),
|
||||
Box::new(formatter),
|
||||
// Collect notifications sent
|
||||
let notifications = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
|
||||
let notifications_clone = notifications.clone();
|
||||
mock_notifier
|
||||
.expect_send_notification()
|
||||
.returning(move |message| {
|
||||
notifications_clone
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(message.to_string());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let mut alert_service = alert_service::new(
|
||||
Box::new(mock_notifier),
|
||||
Box::new(mock_monitor),
|
||||
Box::new(mock_formatter),
|
||||
);
|
||||
|
||||
assert!(service.alert_on_threshold_violation().is_err());
|
||||
// First run
|
||||
alert_service.alert_on_threshold_violation().unwrap();
|
||||
// Second run
|
||||
alert_service.alert_on_threshold_violation().unwrap();
|
||||
|
||||
// Hashmap is not ordered, so we need to compare sets
|
||||
let sent = notifications.lock().unwrap();
|
||||
let sent_set: HashSet<_> = sent.iter().collect();
|
||||
let expected_set: HashSet<_> = expected_notifications.iter().collect();
|
||||
assert_eq!(sent_set, expected_set);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user