use pmat::services::satd_detector::*;
use std::fs;
use std::path::PathBuf;
use tempfile::tempdir;
#[test]
fn test_severity_escalate_from_low() {
assert_eq!(Severity::Low.escalate(), Severity::Medium);
}
#[test]
fn test_severity_escalate_from_medium() {
assert_eq!(Severity::Medium.escalate(), Severity::High);
}
#[test]
fn test_severity_escalate_from_high() {
assert_eq!(Severity::High.escalate(), Severity::Critical);
}
#[test]
fn test_severity_escalate_from_critical() {
assert_eq!(Severity::Critical.escalate(), Severity::Critical);
}
#[test]
fn test_severity_reduce_from_critical() {
assert_eq!(Severity::Critical.reduce(), Severity::High);
}
#[test]
fn test_severity_reduce_from_high() {
assert_eq!(Severity::High.reduce(), Severity::Medium);
}
#[test]
fn test_severity_reduce_from_medium() {
assert_eq!(Severity::Medium.reduce(), Severity::Low);
}
#[test]
fn test_severity_reduce_from_low() {
assert_eq!(Severity::Low.reduce(), Severity::Low);
}
#[test]
fn test_severity_ordering() {
assert!(Severity::Low < Severity::Medium);
assert!(Severity::Medium < Severity::High);
assert!(Severity::High < Severity::Critical);
}
#[test]
fn test_debt_category_design_display() {
assert_eq!(DebtCategory::Design.to_string(), "Design");
}
#[test]
fn test_debt_category_defect_display() {
assert_eq!(DebtCategory::Defect.to_string(), "Defect");
}
#[test]
fn test_debt_category_requirement_display() {
assert_eq!(DebtCategory::Requirement.to_string(), "Requirement");
}
#[test]
fn test_debt_category_test_display() {
assert_eq!(DebtCategory::Test.to_string(), "Test");
}
#[test]
fn test_debt_category_performance_display() {
assert_eq!(DebtCategory::Performance.to_string(), "Performance");
}
#[test]
fn test_debt_category_security_display() {
assert_eq!(DebtCategory::Security.to_string(), "Security");
}
#[test]
fn test_classify_comment_todo() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("TODO: implement this feature");
assert_eq!(result, Some((DebtCategory::Requirement, Severity::Low)));
}
#[test]
fn test_classify_comment_fixme() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("FIXME: this crashes sometimes");
assert_eq!(result, Some((DebtCategory::Defect, Severity::High)));
}
#[test]
fn test_classify_comment_bug() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("BUG: memory leak here");
assert_eq!(result, Some((DebtCategory::Defect, Severity::High)));
}
#[test]
fn test_classify_comment_hack() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("HACK: workaround for library bug");
assert_eq!(result, Some((DebtCategory::Design, Severity::Medium)));
}
#[test]
fn test_classify_comment_security() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("SECURITY: vulnerable to XSS");
assert_eq!(result, Some((DebtCategory::Security, Severity::Critical)));
}
#[test]
fn test_classify_comment_performance() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("performance issue: O(n^2) complexity");
assert_eq!(result, Some((DebtCategory::Performance, Severity::Medium)));
}
#[test]
fn test_classify_comment_normal_comment() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("This is a regular comment");
assert_eq!(result, None);
}
#[test]
fn test_classify_comment_case_insensitive() {
let classifier = DebtClassifier::new();
let result = classifier.classify_comment("todo: implement feature");
assert_eq!(result, Some((DebtCategory::Requirement, Severity::Low)));
}
#[test]
fn test_strict_classifier_creation() {
let classifier = DebtClassifier::new_strict();
let result = classifier.classify_comment("// TODO: implement this");
assert!(result.is_some());
}
#[test]
fn test_strict_classifier_todo_format() {
let classifier = DebtClassifier::new_strict();
let result = classifier.classify_comment("// TODO: implement feature");
assert!(result.is_some());
}
#[test]
fn test_adjust_severity_security_function_escalates() {
let classifier = DebtClassifier::new();
let context = AstContext {
node_type: AstNodeType::SecurityFunction,
parent_function: "validate_auth".to_string(),
complexity: 5,
siblings_count: 3,
nesting_depth: 2,
surrounding_statements: vec![],
};
let adjusted = classifier.adjust_severity(Severity::Medium, &context);
assert_eq!(adjusted, Severity::High);
}
#[test]
fn test_adjust_severity_test_function_reduces() {
let classifier = DebtClassifier::new();
let context = AstContext {
node_type: AstNodeType::TestFunction,
parent_function: "test_something".to_string(),
complexity: 5,
siblings_count: 3,
nesting_depth: 2,
surrounding_statements: vec![],
};
let adjusted = classifier.adjust_severity(Severity::High, &context);
assert_eq!(adjusted, Severity::Medium);
}
#[test]
fn test_adjust_severity_high_complexity_escalates() {
let classifier = DebtClassifier::new();
let context = AstContext {
node_type: AstNodeType::Regular,
parent_function: "process_data".to_string(),
complexity: 25, siblings_count: 10,
nesting_depth: 5,
surrounding_statements: vec![],
};
let adjusted = classifier.adjust_severity(Severity::Low, &context);
assert_eq!(adjusted, Severity::Medium);
}
#[test]
fn test_adjust_severity_regular_unchanged() {
let classifier = DebtClassifier::new();
let context = AstContext {
node_type: AstNodeType::Regular,
parent_function: "helper".to_string(),
complexity: 5,
siblings_count: 3,
nesting_depth: 1,
surrounding_statements: vec![],
};
let adjusted = classifier.adjust_severity(Severity::Medium, &context);
assert_eq!(adjusted, Severity::Medium);
}
#[test]
fn test_satd_detector_default_creation() {
let detector = SATDDetector::new();
drop(detector);
}
#[test]
fn test_satd_detector_strict_creation() {
let detector = SATDDetector::new_strict();
drop(detector);
}
#[test]
fn test_satd_detector_default_impl() {
let detector = SATDDetector::default();
drop(detector);
}
#[test]
fn test_extract_from_content_empty_file() {
let detector = SATDDetector::new();
let path = PathBuf::from("test.rs");
let result = detector.extract_from_content("", &path);
match result {
Ok(debts) => assert_eq!(debts.len(), 0),
Err(_) => panic!("Should not error on empty file"),
}
}
#[test]
fn test_extract_from_content_with_todo() {
let detector = SATDDetector::new();
let path = PathBuf::from("test.rs");
let content = r#"
fn main() {
// TODO: implement error handling
println!("Hello");
}
"#;
let result = detector.extract_from_content(content, &path);
match result {
Ok(debts) => {
assert!(!debts.is_empty());
let has_todo = debts
.iter()
.any(|d| d.category == DebtCategory::Requirement);
assert!(has_todo);
}
Err(_) => panic!("Should not error"),
}
}
#[test]
fn test_extract_from_content_with_fixme() {
let detector = SATDDetector::new();
let path = PathBuf::from("test.rs");
let content = r#"
fn buggy_function() {
// FIXME: this panics on empty input
let x = input[0];
}
"#;
let result = detector.extract_from_content(content, &path);
if let Ok(debts) = result {
assert!(!debts.is_empty());
let has_defect = debts.iter().any(|d| d.category == DebtCategory::Defect);
assert!(has_defect);
}
}
#[test]
fn test_extract_from_content_multiple_debts() {
let detector = SATDDetector::new();
let path = PathBuf::from("test.rs");
let content = r#"
fn complex_function() {
// TODO: add validation
let x = input;
// FIXME: handle edge case
if x > 0 {
// HACK: temporary workaround
process(x);
}
}
"#;
let result = detector.extract_from_content(content, &path);
if let Ok(debts) = result {
assert!(debts.len() >= 3);
}
}
#[test]
fn test_extract_from_content_excludes_test_blocks() {
let detector = SATDDetector::new();
let path = PathBuf::from("test.rs");
let content = r#"
fn production_code() {
// TODO: important production task
}
#[cfg(test)]
mod tests {
// TODO: this should be excluded
fn test_something() {}
}
"#;
let result = detector.extract_from_content(content, &path);
if let Ok(debts) = result {
assert!(debts.len() <= 1);
}
}
#[tokio::test]
async fn test_analyze_directory_empty() {
let temp_dir = tempdir().unwrap();
let detector = SATDDetector::new();
let result = detector.analyze_project(temp_dir.path(), false).await;
if let Ok(analysis) = result {
assert_eq!(analysis.items.len(), 0);
assert_eq!(analysis.total_files_analyzed, 0);
}
}
#[tokio::test]
async fn test_analyze_directory_with_rust_file() {
let temp_dir = tempdir().unwrap();
let rust_file = temp_dir.path().join("main.rs");
fs::write(
&rust_file,
r#"
fn main() {
// TODO: add error handling
println!("Hello");
}
"#,
)
.unwrap();
let detector = SATDDetector::new();
let result = detector.analyze_project(temp_dir.path(), false).await;
if let Ok(analysis) = result {
assert!(analysis.total_files_analyzed > 0);
assert!(!analysis.items.is_empty());
}
}
#[tokio::test]
async fn test_analyze_directory_with_multiple_files() {
let temp_dir = tempdir().unwrap();
fs::write(temp_dir.path().join("file1.rs"), "// TODO: task 1").unwrap();
fs::write(temp_dir.path().join("file2.rs"), "// FIXME: bug here").unwrap();
fs::write(temp_dir.path().join("file3.rs"), "// No debt here").unwrap();
let detector = SATDDetector::new();
let result = detector.analyze_project(temp_dir.path(), false).await;
if let Ok(analysis) = result {
assert!(analysis.total_files_analyzed >= 3);
assert!(analysis.items.len() >= 2); }
}