#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
use super::*;
#[test]
fn test_severity_valid_range() {
let s1 = Severity::new(1).unwrap();
let s5 = Severity::new(5).unwrap();
let s10 = Severity::new(10).unwrap();
assert_eq!(s1.value(), 1);
assert_eq!(s5.value(), 5);
assert_eq!(s10.value(), 10);
}
#[test]
fn test_severity_invalid_range() {
let result_0 = Severity::new(0);
let result_11 = Severity::new(11);
let result_255 = Severity::new(255);
assert!(result_0.is_err());
assert!(result_11.is_err());
assert!(result_255.is_err());
}
#[test]
fn test_severity_levels() {
let low = Severity::new(2).unwrap();
let medium = Severity::new(5).unwrap();
let high = Severity::new(7).unwrap();
let critical = Severity::new(9).unwrap();
assert_eq!(low.level(), "LOW");
assert_eq!(medium.level(), "MEDIUM");
assert_eq!(high.level(), "HIGH");
assert_eq!(critical.level(), "CRITICAL");
}
#[test]
fn test_occurrence_valid_range() {
let o1 = Occurrence::new(1).unwrap();
let o5 = Occurrence::new(5).unwrap();
let o10 = Occurrence::new(10).unwrap();
assert_eq!(o1.value(), 1);
assert_eq!(o5.value(), 5);
assert_eq!(o10.value(), 10);
}
#[test]
fn test_occurrence_invalid_range() {
let result_0 = Occurrence::new(0);
let result_11 = Occurrence::new(11);
assert!(result_0.is_err());
assert!(result_11.is_err());
}
#[test]
fn test_occurrence_levels() {
let remote = Occurrence::new(1).unwrap();
let low = Occurrence::new(3).unwrap();
let moderate = Occurrence::new(5).unwrap();
let high = Occurrence::new(7).unwrap();
let very_high = Occurrence::new(9).unwrap();
assert_eq!(remote.level(), "REMOTE");
assert_eq!(low.level(), "LOW");
assert_eq!(moderate.level(), "MODERATE");
assert_eq!(high.level(), "HIGH");
assert_eq!(very_high.level(), "VERY_HIGH");
}
#[test]
fn test_detection_valid_range() {
let d1 = Detection::new(1).unwrap();
let d5 = Detection::new(5).unwrap();
let d10 = Detection::new(10).unwrap();
assert_eq!(d1.value(), 1);
assert_eq!(d5.value(), 5);
assert_eq!(d10.value(), 10);
}
#[test]
fn test_detection_invalid_range() {
let result_0 = Detection::new(0);
let result_11 = Detection::new(11);
assert!(result_0.is_err());
assert!(result_11.is_err());
}
#[test]
fn test_detection_levels() {
let very_high = Detection::new(1).unwrap();
let high = Detection::new(3).unwrap();
let medium = Detection::new(5).unwrap();
let low = Detection::new(7).unwrap();
let very_low = Detection::new(9).unwrap();
assert_eq!(very_high.level(), "VERY_HIGH");
assert_eq!(high.level(), "HIGH");
assert_eq!(medium.level(), "MEDIUM");
assert_eq!(low.level(), "LOW");
assert_eq!(very_low.level(), "VERY_LOW");
}
#[test]
fn test_rpn_calculation() {
let s9 = Severity::new(9).unwrap();
let o7 = Occurrence::new(7).unwrap();
let d7 = Detection::new(7).unwrap();
let rpn = RPN::calculate(s9, o7, d7);
assert_eq!(rpn.value(), 441); }
#[test]
fn test_rpn_risk_levels() {
let s1 = Severity::new(1).unwrap();
let o1 = Occurrence::new(1).unwrap();
let d1 = Detection::new(1).unwrap();
let s5 = Severity::new(5).unwrap();
let o5 = Occurrence::new(5).unwrap();
let d5 = Detection::new(5).unwrap();
let s7 = Severity::new(7).unwrap();
let o7 = Occurrence::new(7).unwrap();
let d7 = Detection::new(7).unwrap();
let s10 = Severity::new(10).unwrap();
let o10 = Occurrence::new(10).unwrap();
let d10 = Detection::new(10).unwrap();
let rpn_low = RPN::calculate(s1, o1, d1); let rpn_medium = RPN::calculate(s5, o5, d5); let rpn_high = RPN::calculate(s7, o7, d7); let rpn_critical = RPN::calculate(s10, o10, d10);
assert_eq!(rpn_low.risk_level(), "LOW");
assert_eq!(rpn_medium.risk_level(), "MEDIUM");
assert_eq!(rpn_high.risk_level(), "HIGH");
assert_eq!(rpn_critical.risk_level(), "CRITICAL");
}
#[test]
fn test_failure_mode_builder_success() {
let mode = FailureMode::builder()
.id("test_mode")
.category(FailureCategory::FileIO)
.description("Test failure mode")
.severity(Severity::new(8).unwrap())
.occurrence(Occurrence::new(6).unwrap())
.detection(Detection::new(6).unwrap())
.effect("Effect 1")
.effect("Effect 2")
.cause("Cause 1")
.control("Control 1")
.action("Action 1")
.build()
.unwrap();
assert_eq!(mode.id, "test_mode");
assert_eq!(mode.category, FailureCategory::FileIO);
assert_eq!(mode.severity.value(), 8);
assert_eq!(mode.occurrence.value(), 6);
assert_eq!(mode.detection.value(), 6);
assert_eq!(mode.rpn.value(), 288); assert_eq!(mode.effects.len(), 2);
assert_eq!(mode.causes.len(), 1);
assert_eq!(mode.controls.len(), 1);
assert_eq!(mode.actions.len(), 1);
}
#[test]
fn test_failure_mode_builder_missing_required() {
let result = FailureMode::builder()
.id("test_mode")
.build();
assert!(result.is_err());
}
#[test]
fn test_failure_mode_builder_auto_rpn() {
let mode = FailureMode::builder()
.id("test_mode")
.category(FailureCategory::NetworkOps)
.description("Test")
.severity(Severity::new(9).unwrap())
.occurrence(Occurrence::new(7).unwrap())
.detection(Detection::new(7).unwrap())
.build()
.unwrap();
assert_eq!(mode.rpn.value(), 441);
assert_eq!(mode.rpn.risk_level(), "HIGH");
}
#[test]
fn test_registry_register_and_get() {
let mut registry = FmeaRegistry::new();
let mode = FailureMode::builder()
.id("test_mode")
.category(FailureCategory::FileIO)
.description("Test")
.severity(Severity::new(5).unwrap())
.occurrence(Occurrence::new(5).unwrap())
.detection(Detection::new(5).unwrap())
.build()
.unwrap();
registry.register(mode);
let retrieved = registry.get_failure_mode("test_mode");
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().id, "test_mode");
}
#[test]
fn test_registry_record_event() {
let mut registry = FmeaRegistry::new();
let event = FailureEvent::new(
"test_mode".to_string(),
"test_operation".to_string(),
"Test error".to_string(),
);
registry.record_event(event);
assert_eq!(registry.event_count(), 1);
let recent = registry.recent_events(10).collect::<Vec<_>>();
assert_eq!(recent.len(), 1);
assert_eq!(recent[0].mode_id, "test_mode");
}
#[test]
fn test_registry_ring_buffer() {
let mut registry = FmeaRegistry::new();
for i in 0..1500 {
let event = FailureEvent::new(
format!("mode_{}", i),
"test_op".to_string(),
"error".to_string(),
);
registry.record_event(event);
}
assert_eq!(registry.event_count(), 1000);
let all_events: Vec<_> = registry.recent_events(1000).collect();
assert_eq!(all_events.len(), 1000);
assert!(all_events[0].mode_id.contains("1499"));
}
#[test]
fn test_registry_filter_by_category() {
let mut registry = FmeaRegistry::new();
let mode1 = FailureMode::builder()
.id("file_mode")
.category(FailureCategory::FileIO)
.description("Test")
.severity(Severity::new(5).unwrap())
.occurrence(Occurrence::new(5).unwrap())
.detection(Detection::new(5).unwrap())
.build()
.unwrap();
let mode2 = FailureMode::builder()
.id("network_mode")
.category(FailureCategory::NetworkOps)
.description("Test")
.severity(Severity::new(5).unwrap())
.occurrence(Occurrence::new(5).unwrap())
.detection(Detection::new(5).unwrap())
.build()
.unwrap();
registry.register(mode1);
registry.register(mode2);
let file_modes: Vec<_> = registry
.failure_modes_by_category(FailureCategory::FileIO)
.collect();
let network_modes: Vec<_> = registry
.failure_modes_by_category(FailureCategory::NetworkOps)
.collect();
assert_eq!(file_modes.len(), 1);
assert_eq!(file_modes[0].id, "file_mode");
assert_eq!(network_modes.len(), 1);
assert_eq!(network_modes[0].id, "network_mode");
}
#[test]
fn test_registry_events_for_mode() {
let mut registry = FmeaRegistry::new();
registry.record_event(FailureEvent::new(
"mode_a".to_string(),
"op1".to_string(),
"error1".to_string(),
));
registry.record_event(FailureEvent::new(
"mode_b".to_string(),
"op2".to_string(),
"error2".to_string(),
));
registry.record_event(FailureEvent::new(
"mode_a".to_string(),
"op3".to_string(),
"error3".to_string(),
));
let mode_a_events: Vec<_> = registry.events_for_mode("mode_a").collect();
let mode_b_events: Vec<_> = registry.events_for_mode("mode_b").collect();
assert_eq!(mode_a_events.len(), 2);
assert_eq!(mode_b_events.len(), 1);
}
#[test]
fn test_catalog_critical_failures_registered() {
let mut registry = FmeaRegistry::new();
register_critical_failures(&mut registry);
assert_eq!(registry.failure_mode_count(), 8);
assert!(registry.get_failure_mode("path_traversal_attack").is_some());
assert!(registry.get_failure_mode("template_ssti").is_some());
assert!(registry.get_failure_mode("dep_cycle_detected").is_some());
assert!(registry.get_failure_mode("file_io_write_fail").is_some());
assert!(registry.get_failure_mode("lockfile_race_corrupt").is_some());
assert!(registry.get_failure_mode("network_timeout").is_some());
assert!(registry.get_failure_mode("mutex_poisoned").is_some());
assert!(registry.get_failure_mode("deser_invalid_format").is_some());
}
#[test]
fn test_catalog_rpn_values() {
let mut registry = FmeaRegistry::new();
register_critical_failures(&mut registry);
let path_traversal = registry.get_failure_mode("path_traversal_attack").unwrap();
assert_eq!(path_traversal.rpn.value(), 441);
let template_ssti = registry.get_failure_mode("template_ssti").unwrap();
assert_eq!(template_ssti.rpn.value(), 378);
let dep_cycle = registry.get_failure_mode("dep_cycle_detected").unwrap();
assert_eq!(dep_cycle.rpn.value(), 343);
let file_io = registry.get_failure_mode("file_io_write_fail").unwrap();
assert_eq!(file_io.rpn.value(), 288);
}
#[test]
fn test_catalog_pareto_distribution() {
let mut registry = FmeaRegistry::new();
register_critical_failures(&mut registry);
let mut rpns: Vec<_> = registry
.all_failure_modes()
.map(|m| m.rpn.value())
.collect();
rpns.sort_by(|a, b| b.cmp(a));
let total: u16 = rpns.iter().sum();
let top_4: u16 = rpns.iter().take(4).sum();
let percentage = (top_4 as f64 / total as f64) * 100.0;
assert!(
percentage >= 60.0,
"Top 4 RPN should be >= 60% of total, got {:.1}%",
percentage
);
}