use super::*;
use crate::cluster::Severity;
use crate::sequence::ngram::{ngram_coverage, top_ngrams};
#[test]
fn test_decy_futex_anomaly() {
let baseline_syscalls =
vec!["mmap".to_string(), "read".to_string(), "write".to_string(), "close".to_string()];
let current_syscalls = vec![
"mmap".to_string(),
"read".to_string(),
"futex".to_string(), "write".to_string(),
"close".to_string(),
];
let baseline_ngrams = extract_ngrams(&baseline_syscalls, 3);
let current_ngrams = extract_ngrams(¤t_syscalls, 3);
let anomalies = detect_sequence_anomalies(&baseline_ngrams, ¤t_ngrams, 0.30);
let futex_anomalies: Vec<_> =
anomalies.iter().filter(|a| a.ngram.iter().any(|s| s == "futex")).collect();
assert!(!futex_anomalies.is_empty());
assert!(futex_anomalies.iter().any(|a| a.severity == Severity::High));
}
#[test]
fn test_depyler_telemetry_leak() {
let baseline_syscalls = vec!["mmap".to_string(), "read".to_string(), "write".to_string()];
let current_syscalls = vec![
"mmap".to_string(),
"socket".to_string(), "connect".to_string(), "send".to_string(), "read".to_string(),
"write".to_string(),
];
let baseline_ngrams = extract_ngrams(&baseline_syscalls, 3);
let current_ngrams = extract_ngrams(¤t_syscalls, 3);
let anomalies = detect_sequence_anomalies(&baseline_ngrams, ¤t_ngrams, 0.30);
let networking_anomalies: Vec<_> = anomalies
.iter()
.filter(|a| a.ngram.iter().any(|s| s.contains("socket") || s.contains("connect")))
.collect();
assert!(!networking_anomalies.is_empty());
assert!(networking_anomalies.iter().any(|a| a.severity == Severity::Critical));
}
#[test]
fn test_no_false_positive_on_order_preserving_change() {
let mut baseline_syscalls = Vec::new();
for _ in 0..10 {
baseline_syscalls.push("mmap".to_string());
baseline_syscalls.push("read".to_string());
baseline_syscalls.push("write".to_string());
}
let mut current_syscalls = Vec::new();
for _ in 0..15 {
current_syscalls.push("mmap".to_string());
current_syscalls.push("read".to_string());
current_syscalls.push("write".to_string());
}
let baseline_ngrams = extract_ngrams(&baseline_syscalls, 3);
let current_ngrams = extract_ngrams(¤t_syscalls, 3);
let anomalies = detect_sequence_anomalies(&baseline_ngrams, ¤t_ngrams, 0.30);
assert!(anomalies.iter().all(|a| a.anomaly_type == AnomalyType::FrequencyChange));
assert!(anomalies.iter().all(|a| a.severity != Severity::Critical));
}
#[test]
fn test_grammar_violation_reordering() {
let baseline_syscalls = vec!["open".to_string(), "read".to_string(), "close".to_string()];
let current_syscalls = vec![
"open".to_string(),
"close".to_string(), "read".to_string(), ];
let baseline_ngrams = extract_ngrams(&baseline_syscalls, 2);
let current_ngrams = extract_ngrams(¤t_syscalls, 2);
let anomalies = detect_sequence_anomalies(&baseline_ngrams, ¤t_ngrams, 0.30);
assert!(anomalies.len() >= 2);
}
#[test]
fn test_tight_loop_detection() {
let mut tight_loop_syscalls = Vec::new();
for _ in 0..1000 {
tight_loop_syscalls.push("futex".to_string());
tight_loop_syscalls.push("futex".to_string());
}
let ngrams = extract_ngrams(&tight_loop_syscalls, 2);
let coverage = ngram_coverage(&ngrams);
assert!(coverage < 0.01); }
#[test]
fn test_diverse_pattern_normal() {
let diverse_syscalls = vec![
"mmap".to_string(),
"read".to_string(),
"write".to_string(),
"mmap".to_string(),
"brk".to_string(),
"write".to_string(),
"fsync".to_string(),
"close".to_string(),
];
let ngrams = extract_ngrams(&diverse_syscalls, 3);
let coverage = ngram_coverage(&ngrams);
assert!(coverage > 0.5); }
#[test]
fn test_top_ngrams_profiling() {
let mut syscalls = vec!["mmap".to_string(), "read".to_string(), "write".to_string()];
for _ in 0..10 {
syscalls.push("mmap".to_string());
syscalls.push("read".to_string());
}
let ngrams = extract_ngrams(&syscalls, 2);
let top = top_ngrams(&ngrams, 1);
assert_eq!(top[0].0, vec!["mmap".to_string(), "read".to_string()]);
assert_eq!(top[0].1, 11); }
#[test]
fn test_empty_trace() {
let empty_syscalls: Vec<String> = Vec::new();
let ngrams = extract_ngrams(&empty_syscalls, 3);
assert!(ngrams.is_empty());
assert_eq!(ngram_coverage(&ngrams), 0.0);
}
#[test]
fn test_anomaly_report_format() {
let anomaly = SequenceAnomaly {
ngram: vec!["socket".to_string(), "connect".to_string(), "send".to_string()],
baseline_freq: 0,
current_freq: 5,
anomaly_type: AnomalyType::NewSequence,
severity: Severity::Critical,
};
let report = anomaly.to_report_string();
assert!(report.contains("NEW SEQUENCE"));
assert!(report.contains("socket"));
assert!(report.contains("connect"));
assert!(report.contains("send"));
assert!(report.contains("CRITICAL"));
assert!(report.contains("5 occurrences"));
}
#[test]
fn test_configurable_frequency_threshold() {
let mut baseline_ngrams = NGramMap::new();
baseline_ngrams.insert(vec!["a".to_string(), "b".to_string()], 100);
let mut current_ngrams = NGramMap::new();
current_ngrams.insert(vec!["a".to_string(), "b".to_string()], 120);
let strict_anomalies = detect_sequence_anomalies(&baseline_ngrams, ¤t_ngrams, 0.30);
assert!(strict_anomalies.iter().all(|a| a.anomaly_type != AnomalyType::FrequencyChange));
let loose_anomalies = detect_sequence_anomalies(&baseline_ngrams, ¤t_ngrams, 0.05);
assert!(loose_anomalies.iter().any(|a| a.anomaly_type == AnomalyType::FrequencyChange));
}