use mockforge_foundation::failure_injection::{FailureConfig, FailureInjector, TagFailureConfig};
use mockforge_foundation::latency::LatencyProfile;
use std::collections::HashMap;
#[test]
fn test_latency_profile_zero_base() {
let profile = LatencyProfile::new(0, 10);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() <= 10);
}
#[test]
fn test_latency_profile_zero_jitter() {
let profile = LatencyProfile::new(100, 0);
let latency = profile.calculate_latency(&[]);
assert_eq!(latency.as_millis(), 100);
}
#[test]
fn test_latency_profile_large_values() {
let profile = LatencyProfile::new(10000, 5000);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() >= 5000);
assert!(latency.as_millis() <= 15000);
}
#[test]
fn test_latency_profile_max_bound() {
let profile = LatencyProfile::new(100, 50).with_max_ms(120);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() <= 120);
}
#[test]
fn test_latency_profile_min_bound() {
let profile = LatencyProfile::new(100, 200).with_min_ms(50);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() >= 50);
}
#[test]
fn test_latency_profile_tag_overrides() {
let profile = LatencyProfile::new(100, 10).with_tag_override("critical".to_string(), 500);
let latency_with_tag = profile.calculate_latency(&["critical".to_string()]);
assert_eq!(latency_with_tag.as_millis(), 500);
let latency_without_tag = profile.calculate_latency(&["normal".to_string()]);
assert!(latency_without_tag.as_millis() >= 90 && latency_without_tag.as_millis() <= 110);
}
#[test]
fn test_latency_profile_multiple_tag_overrides() {
let profile = LatencyProfile::new(100, 10)
.with_tag_override("critical".to_string(), 500)
.with_tag_override("important".to_string(), 300);
let latency = profile.calculate_latency(&["critical".to_string(), "important".to_string()]);
assert_eq!(latency.as_millis(), 500);
}
#[test]
fn test_latency_profile_normal_distribution() {
let profile = LatencyProfile::with_normal_distribution(100, 20.0);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() > 0);
assert!(latency.as_millis() < 500);
}
#[test]
fn test_latency_profile_pareto_distribution() {
let profile = LatencyProfile::with_pareto_distribution(100, 2.0);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() > 0);
}
#[test]
fn test_failure_injector_zero_rate() {
let config = FailureConfig {
global_error_rate: 0.0,
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
for _ in 0..100 {
assert!(!injector.should_inject_failure(&[]));
}
}
#[test]
fn test_failure_injector_full_rate() {
let config = FailureConfig {
global_error_rate: 1.0,
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
for _ in 0..100 {
assert!(injector.should_inject_failure(&[]));
}
}
#[test]
fn test_failure_injector_disabled() {
let config = FailureConfig {
global_error_rate: 1.0,
..Default::default()
};
let injector = FailureInjector::new(Some(config), false);
assert!(!injector.should_inject_failure(&[]));
}
#[test]
fn test_failure_injector_no_config() {
let injector = FailureInjector::new(None, true);
assert!(!injector.should_inject_failure(&[]));
}
#[test]
fn test_failure_injector_exclude_tags() {
let config = FailureConfig {
global_error_rate: 1.0,
exclude_tags: vec!["health".to_string()],
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
assert!(injector.should_inject_failure(&["api".to_string()]));
assert!(!injector.should_inject_failure(&["health".to_string()]));
}
#[test]
fn test_failure_injector_include_tags() {
let config = FailureConfig {
global_error_rate: 1.0,
include_tags: vec!["critical".to_string()],
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
assert!(injector.should_inject_failure(&["critical".to_string()]));
assert!(!injector.should_inject_failure(&["normal".to_string()]));
}
#[test]
fn test_failure_injector_tag_specific_config() {
let mut tag_configs = HashMap::new();
tag_configs.insert(
"critical".to_string(),
TagFailureConfig {
error_rate: 1.0,
status_codes: Some(vec![503]),
error_message: Some("Critical service failure".to_string()),
},
);
let config = FailureConfig {
global_error_rate: 0.0,
tag_configs,
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
assert!(injector.should_inject_failure(&["critical".to_string()]));
assert!(!injector.should_inject_failure(&["normal".to_string()]));
let response = injector.get_failure_response(&["critical".to_string()]);
assert!(response.is_some());
let (status, message) = response.unwrap();
assert_eq!(status, 503);
assert_eq!(message, "Critical service failure");
}
#[test]
fn test_failure_injector_empty_status_codes() {
let config = FailureConfig {
global_error_rate: 1.0,
default_status_codes: vec![],
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
let response = injector.get_failure_response(&[]);
assert!(response.is_some());
let (status, _) = response.unwrap();
assert_eq!(status, 500);
}
#[test]
fn test_failure_injector_probabilistic() {
let config = FailureConfig {
global_error_rate: 0.5,
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
let mut failures = 0;
let iterations = 1000;
for _ in 0..iterations {
if injector.should_inject_failure(&[]) {
failures += 1;
}
}
let failure_rate = failures as f64 / iterations as f64;
assert!(
failure_rate > 0.4 && failure_rate < 0.6,
"Failure rate was {}, expected ~0.5",
failure_rate
);
}
#[test]
fn test_failure_injector_tag_precedence() {
let mut tag_configs = HashMap::new();
tag_configs.insert(
"critical".to_string(),
TagFailureConfig {
error_rate: 1.0,
status_codes: Some(vec![503]),
error_message: Some("Critical".to_string()),
},
);
tag_configs.insert(
"important".to_string(),
TagFailureConfig {
error_rate: 1.0,
status_codes: Some(vec![502]),
error_message: Some("Important".to_string()),
},
);
let config = FailureConfig {
global_error_rate: 0.0,
tag_configs,
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
let response =
injector.get_failure_response(&["critical".to_string(), "important".to_string()]);
assert!(response.is_some());
let (status, _) = response.unwrap();
assert!(status == 503 || status == 502);
}
#[test]
fn test_latency_profile_all_distributions() {
let fixed = LatencyProfile::new(100, 20);
let latency_fixed = fixed.calculate_latency(&[]);
assert!(latency_fixed.as_millis() >= 80 && latency_fixed.as_millis() <= 120);
let normal = LatencyProfile::with_normal_distribution(100, 20.0);
let latency_normal = normal.calculate_latency(&[]);
assert!(latency_normal.as_millis() > 0);
let pareto = LatencyProfile::with_pareto_distribution(100, 2.0);
let latency_pareto = pareto.calculate_latency(&[]);
assert!(latency_pareto.as_millis() > 0);
}
#[test]
fn test_latency_profile_bounds_edge_cases() {
let profile = LatencyProfile::new(50, 10).with_min_ms(100);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() >= 100);
let profile = LatencyProfile::new(100, 10).with_max_ms(50);
let latency = profile.calculate_latency(&[]);
assert!(latency.as_millis() <= 50);
}
#[test]
fn test_failure_injector_include_exclude_precedence() {
let config = FailureConfig {
global_error_rate: 1.0,
include_tags: vec!["api".to_string()],
exclude_tags: vec!["health".to_string()],
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
assert!(!injector.should_inject_failure(&["health".to_string()]));
assert!(injector.should_inject_failure(&["api".to_string()]));
assert!(!injector.should_inject_failure(&["other".to_string()]));
}
#[test]
fn test_failure_injector_get_response_variations() {
let config = FailureConfig {
global_error_rate: 1.0,
default_status_codes: vec![500, 502, 503],
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
let response = injector.get_failure_response(&[]);
assert!(response.is_some());
let (status, _) = response.unwrap();
assert!([500, 502, 503].contains(&status));
let config = FailureConfig {
global_error_rate: 1.0,
default_status_codes: vec![500],
..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
let response = injector.get_failure_response(&[]);
assert!(response.is_some());
let (_, message) = response.unwrap();
assert_eq!(message, "Injected failure");
}
#[test]
fn test_latency_profile_consistency() {
let profile = LatencyProfile::new(100, 0);
let latency1 = profile.calculate_latency(&[]);
assert!(latency1.as_millis() >= 90 && latency1.as_millis() <= 110);
}
#[test]
fn test_failure_injector_small_rate() {
let config = FailureConfig {
global_error_rate: 0.01, ..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
let mut failures = 0;
let iterations = 10000;
for _ in 0..iterations {
if injector.should_inject_failure(&[]) {
failures += 1;
}
}
let failure_rate = failures as f64 / iterations as f64;
assert!(
failure_rate > 0.005 && failure_rate < 0.02,
"Failure rate was {}, expected ~0.01",
failure_rate
);
}
#[test]
fn test_failure_injector_high_rate() {
let config = FailureConfig {
global_error_rate: 0.99, ..Default::default()
};
let injector = FailureInjector::new(Some(config), true);
let mut failures = 0;
let iterations = 1000;
for _ in 0..iterations {
if injector.should_inject_failure(&[]) {
failures += 1;
}
}
let failure_rate = failures as f64 / iterations as f64;
assert!(
failure_rate > 0.95 && failure_rate < 1.0,
"Failure rate was {}, expected ~0.99",
failure_rate
);
}