#[test]
fn test_alert_configuration_clone() {
let config = AlertConfiguration {
rules: vec![],
config: AlertManagerConfig::default(),
};
let cloned = config.clone();
assert!(cloned.rules.is_empty());
}
#[test]
fn test_alert_configuration_debug() {
let config = AlertConfiguration {
rules: default_tdg_alert_rules(),
config: AlertManagerConfig::default(),
};
let debug = format!("{:?}", config);
assert!(debug.contains("rules"));
}
#[test]
fn test_notification_channel_clone() {
let channel = NotificationChannel::Email {
recipients: vec!["test@test.com".to_string()],
};
let cloned = channel.clone();
assert!(matches!(cloned, NotificationChannel::Email { .. }));
}
#[test]
fn test_notification_channel_debug() {
let channel = NotificationChannel::Webhook {
url: "https://example.com".to_string(),
method: "POST".to_string(),
};
let debug = format!("{:?}", channel);
assert!(debug.contains("example.com"));
}
#[test]
fn test_alert_severity_serialization() {
let severity = AlertSeverity::Critical;
let json = serde_json::to_string(&severity).unwrap();
let deserialized: AlertSeverity = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, AlertSeverity::Critical);
}
#[test]
fn test_alert_condition_serialization() {
let condition = AlertCondition::GreaterThanOrEqual;
let json = serde_json::to_string(&condition).unwrap();
let deserialized: AlertCondition = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, AlertCondition::GreaterThanOrEqual);
}
#[test]
fn test_alert_state_serialization() {
let state = AlertState::Acknowledged;
let json = serde_json::to_string(&state).unwrap();
let deserialized: AlertState = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, AlertState::Acknowledged);
}
#[test]
fn test_alert_manager_config_serialization() {
let config = AlertManagerConfig::default();
let json = serde_json::to_string(&config).unwrap();
let deserialized: AlertManagerConfig = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.max_active_alerts, 100);
}
#[test]
fn test_notification_channel_dashboard_serialization() {
let channel = NotificationChannel::Dashboard;
let json = serde_json::to_string(&channel).unwrap();
let deserialized: NotificationChannel = serde_json::from_str(&json).unwrap();
assert!(matches!(deserialized, NotificationChannel::Dashboard));
}
#[test]
fn test_notification_channel_email_serialization() {
let channel = NotificationChannel::Email {
recipients: vec!["a@b.com".to_string(), "c@d.com".to_string()],
};
let json = serde_json::to_string(&channel).unwrap();
let deserialized: NotificationChannel = serde_json::from_str(&json).unwrap();
if let NotificationChannel::Email { recipients } = deserialized {
assert_eq!(recipients.len(), 2);
} else {
panic!("Expected Email variant");
}
}
#[test]
fn test_notification_channel_slack_serialization() {
let channel = NotificationChannel::Slack {
webhook_url: "https://hooks.slack.com/test".to_string(),
channel: "#alerts".to_string(),
};
let json = serde_json::to_string(&channel).unwrap();
let deserialized: NotificationChannel = serde_json::from_str(&json).unwrap();
if let NotificationChannel::Slack { channel, .. } = deserialized {
assert_eq!(channel, "#alerts");
} else {
panic!("Expected Slack variant");
}
}
#[test]
fn test_notification_channel_pagerduty_serialization() {
let channel = NotificationChannel::PagerDuty {
integration_key: "key123".to_string(),
};
let json = serde_json::to_string(&channel).unwrap();
let deserialized: NotificationChannel = serde_json::from_str(&json).unwrap();
if let NotificationChannel::PagerDuty { integration_key } = deserialized {
assert_eq!(integration_key, "key123");
} else {
panic!("Expected PagerDuty variant");
}
}
#[test]
fn test_notification_channel_log_serialization() {
let channel = NotificationChannel::Log {
level: "WARN".to_string(),
};
let json = serde_json::to_string(&channel).unwrap();
let deserialized: NotificationChannel = serde_json::from_str(&json).unwrap();
if let NotificationChannel::Log { level } = deserialized {
assert_eq!(level, "WARN");
} else {
panic!("Expected Log variant");
}
}
#[test]
fn test_metric_value_serialization() {
let mut tags = HashMap::new();
tags.insert("env".to_string(), "prod".to_string());
let metric = MetricValue {
value: 99.9,
timestamp: SystemTime::UNIX_EPOCH,
tags,
};
let json = serde_json::to_string(&metric).unwrap();
let deserialized: MetricValue = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.value, 99.9);
assert_eq!(deserialized.tags.get("env"), Some(&"prod".to_string()));
}
#[test]
fn test_alert_statistics_serialization() {
let stats = AlertStatistics {
total_triggered: 50,
total_resolved: 45,
total_acknowledged: 48,
alerts_by_severity: HashMap::new(),
mean_time_to_acknowledge_ms: 3000.0,
mean_time_to_resolve_ms: 8000.0,
false_positive_rate: 0.02,
};
let json = serde_json::to_string(&stats).unwrap();
let deserialized: AlertStatistics = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.total_triggered, 50);
assert_eq!(deserialized.false_positive_rate, 0.02);
}
#[test]
fn test_acknowledgement_serialization() {
let ack = Acknowledgement {
acknowledged_by: "admin".to_string(),
acknowledged_at: SystemTime::UNIX_EPOCH,
comment: Some("Fixed".to_string()),
};
let json = serde_json::to_string(&ack).unwrap();
let deserialized: Acknowledgement = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.acknowledged_by, "admin");
assert_eq!(deserialized.comment, Some("Fixed".to_string()));
}
#[test]
fn test_alert_rule_serialization() {
let rule = AlertRule {
id: "rule_ser".to_string(),
name: "Serialization Test".to_string(),
description: "Test".to_string(),
metric: "test_metric".to_string(),
condition: AlertCondition::Equal,
threshold: 42.0,
duration: Duration::from_secs(30),
severity: AlertSeverity::Info,
enabled: true,
notification_channels: vec![NotificationChannel::Dashboard],
cooldown_period: Duration::from_secs(120),
metadata: HashMap::new(),
};
let json = serde_json::to_string(&rule).unwrap();
let deserialized: AlertRule = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.id, "rule_ser");
assert_eq!(deserialized.threshold, 42.0);
}
#[test]
fn test_alert_serialization() {
let alert = Alert {
id: "alert_ser".to_string(),
rule_id: "rule1".to_string(),
rule_name: "Test".to_string(),
severity: AlertSeverity::Warning,
state: AlertState::Active,
triggered_at: SystemTime::UNIX_EPOCH,
resolved_at: None,
metric_value: 75.0,
threshold_value: 70.0,
message: "Alert!".to_string(),
context: HashMap::new(),
notification_sent: false,
acknowledgement: None,
};
let json = serde_json::to_string(&alert).unwrap();
let deserialized: Alert = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.id, "alert_ser");
assert_eq!(deserialized.metric_value, 75.0);
}
#[test]
fn test_alert_configuration_serialization() {
let config = AlertConfiguration {
rules: default_tdg_alert_rules(),
config: AlertManagerConfig::default(),
};
let json = serde_json::to_string(&config).unwrap();
let deserialized: AlertConfiguration = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.rules.len(), 4);
}
#[tokio::test]
#[ignore = "Complex async operation times out in coverage"]
async fn test_update_metric_no_matching_rules() {
let manager = AlertManager::new(AlertManagerConfig::default());
let rule = AlertRule {
id: "cpu_rule".to_string(),
name: "CPU Alert".to_string(),
description: "High CPU".to_string(),
metric: "cpu_usage".to_string(),
condition: AlertCondition::GreaterThan,
threshold: 80.0,
duration: Duration::from_secs(60),
severity: AlertSeverity::Warning,
enabled: true,
notification_channels: vec![],
cooldown_period: Duration::from_secs(300),
metadata: HashMap::new(),
};
manager.add_rule(rule).await.unwrap();
let result = manager
.update_metric("memory_usage".to_string(), 90.0)
.await;
assert!(result.is_ok());
let alerts = manager.get_active_alerts().await;
assert!(alerts.is_empty());
}
#[tokio::test]
#[ignore = "Complex async operation times out in coverage"]
async fn test_update_metric_disabled_rule() {
let manager = AlertManager::new(AlertManagerConfig::default());
let rule = AlertRule {
id: "disabled_rule".to_string(),
name: "Disabled Alert".to_string(),
description: "Should not fire".to_string(),
metric: "test_metric".to_string(),
condition: AlertCondition::GreaterThan,
threshold: 50.0,
duration: Duration::from_secs(60),
severity: AlertSeverity::Warning,
enabled: false, notification_channels: vec![],
cooldown_period: Duration::from_secs(300),
metadata: HashMap::new(),
};
manager.add_rule(rule).await.unwrap();
let result = manager
.update_metric("test_metric".to_string(), 100.0)
.await;
assert!(result.is_ok());
let alerts = manager.get_active_alerts().await;
assert!(alerts.is_empty());
}
#[tokio::test]
#[ignore = "Complex async operation times out in coverage"]
async fn test_remove_rule_with_active_alerts() {
let manager = AlertManager::new(AlertManagerConfig {
silence_duplicate_alerts: false,
..AlertManagerConfig::default()
});
let rule = AlertRule {
id: "to_remove".to_string(),
name: "Removable Alert".to_string(),
description: "Will be removed".to_string(),
metric: "test_metric".to_string(),
condition: AlertCondition::GreaterThan,
threshold: 50.0,
duration: Duration::from_secs(1),
severity: AlertSeverity::Info,
enabled: true,
notification_channels: vec![],
cooldown_period: Duration::from_millis(1),
metadata: HashMap::new(),
};
manager.add_rule(rule).await.unwrap();
manager
.update_metric("test_metric".to_string(), 100.0)
.await
.unwrap();
manager.remove_rule("to_remove").await.unwrap();
let stats = manager.get_statistics().await;
let _ = stats.total_triggered;
}
#[tokio::test]
async fn test_alert_manager_export_config_with_rules() {
let manager = AlertManager::new(AlertManagerConfig::default());
for i in 0..3 {
let rule = AlertRule {
id: format!("rule_{}", i),
name: format!("Rule {}", i),
description: "Test".to_string(),
metric: format!("metric_{}", i),
condition: AlertCondition::GreaterThan,
threshold: 50.0,
duration: Duration::from_secs(60),
severity: AlertSeverity::Warning,
enabled: true,
notification_channels: vec![],
cooldown_period: Duration::from_secs(300),
metadata: HashMap::new(),
};
manager.add_rule(rule).await.unwrap();
}
let config = manager.export_config().await;
assert_eq!(config.rules.len(), 3);
}
#[test]
fn test_default_tdg_rules_properties() {
let rules = default_tdg_alert_rules();
assert!(rules.iter().all(|r| r.enabled));
assert!(rules.iter().all(|r| r
.notification_channels
.contains(&NotificationChannel::Dashboard)));
let cpu_rule = rules.iter().find(|r| r.id == "high_cpu").unwrap();
assert_eq!(cpu_rule.threshold, 90.0);
assert_eq!(cpu_rule.severity, AlertSeverity::Critical);
let memory_rule = rules.iter().find(|r| r.id == "high_memory").unwrap();
assert_eq!(memory_rule.threshold, 8192.0);
assert_eq!(memory_rule.severity, AlertSeverity::Warning);
let cache_rule = rules.iter().find(|r| r.id == "low_cache_hit").unwrap();
assert_eq!(cache_rule.threshold, 0.7);
assert_eq!(cache_rule.condition, AlertCondition::LessThan);
}
#[test]
fn test_alert_rule_with_metadata() {
let mut metadata = HashMap::new();
metadata.insert("owner".to_string(), "team-a".to_string());
metadata.insert(
"runbook".to_string(),
"https://docs.example.com/runbook".to_string(),
);
let rule = AlertRule {
id: "meta_rule".to_string(),
name: "Rule with Metadata".to_string(),
description: "Has custom metadata".to_string(),
metric: "test".to_string(),
condition: AlertCondition::GreaterThan,
threshold: 100.0,
duration: Duration::from_secs(60),
severity: AlertSeverity::Warning,
enabled: true,
notification_channels: vec![],
cooldown_period: Duration::from_secs(300),
metadata,
};
assert_eq!(rule.metadata.get("owner"), Some(&"team-a".to_string()));
assert!(rule.metadata.contains_key("runbook"));
}
#[test]
fn test_alert_with_context() {
let mut context = HashMap::new();
context.insert("host".to_string(), "server-1".to_string());
context.insert("region".to_string(), "us-east-1".to_string());
let alert = Alert {
id: "ctx_alert".to_string(),
rule_id: "rule1".to_string(),
rule_name: "Context Alert".to_string(),
severity: AlertSeverity::Warning,
state: AlertState::Triggered,
triggered_at: SystemTime::now(),
resolved_at: None,
metric_value: 95.0,
threshold_value: 90.0,
message: "High usage".to_string(),
context,
notification_sent: false,
acknowledgement: None,
};
assert_eq!(alert.context.get("host"), Some(&"server-1".to_string()));
assert_eq!(alert.context.len(), 2);
}
#[test]
fn test_alert_with_acknowledgement() {
let alert = Alert {
id: "ack_alert".to_string(),
rule_id: "rule1".to_string(),
rule_name: "Acknowledged Alert".to_string(),
severity: AlertSeverity::Critical,
state: AlertState::Acknowledged,
triggered_at: SystemTime::now(),
resolved_at: None,
metric_value: 100.0,
threshold_value: 90.0,
message: "Critical threshold".to_string(),
context: HashMap::new(),
notification_sent: true,
acknowledgement: Some(Acknowledgement {
acknowledged_by: "oncall".to_string(),
acknowledged_at: SystemTime::now(),
comment: Some("Working on it".to_string()),
}),
};
assert!(alert.acknowledgement.is_some());
let ack = alert.acknowledgement.as_ref().unwrap();
assert_eq!(ack.acknowledged_by, "oncall");
}