#![cfg_attr(coverage_nightly, coverage(off))]
use crate::services::file_health::{
analyze_file, count_lines, scan_directory, thresholds, FileHealthBaseline, FileHealthMetrics,
FileHealthReport, FileSizeClass, HealthGrade, RatchetViolation,
};
use std::path::{Path, PathBuf};
use tempfile::TempDir;
#[test]
fn test_file_size_class_ideal() {
assert_eq!(FileSizeClass::from_lines(0), FileSizeClass::Ideal);
assert_eq!(FileSizeClass::from_lines(100), FileSizeClass::Ideal);
assert_eq!(FileSizeClass::from_lines(200), FileSizeClass::Ideal);
}
#[test]
fn test_file_size_class_acceptable() {
assert_eq!(FileSizeClass::from_lines(201), FileSizeClass::Acceptable);
assert_eq!(FileSizeClass::from_lines(350), FileSizeClass::Acceptable);
assert_eq!(FileSizeClass::from_lines(500), FileSizeClass::Acceptable);
}
#[test]
fn test_file_size_class_warning() {
assert_eq!(FileSizeClass::from_lines(501), FileSizeClass::Warning);
assert_eq!(FileSizeClass::from_lines(750), FileSizeClass::Warning);
assert_eq!(FileSizeClass::from_lines(1000), FileSizeClass::Warning);
}
#[test]
fn test_file_size_class_problem() {
assert_eq!(FileSizeClass::from_lines(1001), FileSizeClass::Problem);
assert_eq!(FileSizeClass::from_lines(1500), FileSizeClass::Problem);
assert_eq!(FileSizeClass::from_lines(2000), FileSizeClass::Problem);
}
#[test]
fn test_file_size_class_critical() {
assert_eq!(FileSizeClass::from_lines(2001), FileSizeClass::Critical);
assert_eq!(FileSizeClass::from_lines(5000), FileSizeClass::Critical);
assert_eq!(FileSizeClass::from_lines(100_000), FileSizeClass::Critical);
}
#[test]
fn test_file_size_class_as_str() {
assert_eq!(FileSizeClass::Ideal.as_str(), "ideal");
assert_eq!(FileSizeClass::Acceptable.as_str(), "acceptable");
assert_eq!(FileSizeClass::Warning.as_str(), "warning");
assert_eq!(FileSizeClass::Problem.as_str(), "problem");
assert_eq!(FileSizeClass::Critical.as_str(), "critical");
}
#[test]
fn test_file_size_class_debug() {
let _ = format!("{:?}", FileSizeClass::Ideal);
let _ = format!("{:?}", FileSizeClass::Acceptable);
let _ = format!("{:?}", FileSizeClass::Warning);
let _ = format!("{:?}", FileSizeClass::Problem);
let _ = format!("{:?}", FileSizeClass::Critical);
}
#[test]
fn test_file_size_class_clone_eq() {
let a = FileSizeClass::Warning;
let b = a;
assert_eq!(a, b);
assert_ne!(FileSizeClass::Ideal, FileSizeClass::Critical);
}
#[test]
fn test_health_grade_from_score_a() {
assert_eq!(HealthGrade::from_score(90), HealthGrade::A);
assert_eq!(HealthGrade::from_score(95), HealthGrade::A);
assert_eq!(HealthGrade::from_score(100), HealthGrade::A);
}
#[test]
fn test_health_grade_from_score_b() {
assert_eq!(HealthGrade::from_score(80), HealthGrade::B);
assert_eq!(HealthGrade::from_score(85), HealthGrade::B);
assert_eq!(HealthGrade::from_score(89), HealthGrade::B);
}
#[test]
fn test_health_grade_from_score_c() {
assert_eq!(HealthGrade::from_score(70), HealthGrade::C);
assert_eq!(HealthGrade::from_score(75), HealthGrade::C);
assert_eq!(HealthGrade::from_score(79), HealthGrade::C);
}
#[test]
fn test_health_grade_from_score_d() {
assert_eq!(HealthGrade::from_score(60), HealthGrade::D);
assert_eq!(HealthGrade::from_score(65), HealthGrade::D);
assert_eq!(HealthGrade::from_score(69), HealthGrade::D);
}
#[test]
fn test_health_grade_from_score_e() {
assert_eq!(HealthGrade::from_score(50), HealthGrade::E);
assert_eq!(HealthGrade::from_score(55), HealthGrade::E);
assert_eq!(HealthGrade::from_score(59), HealthGrade::E);
}
#[test]
fn test_health_grade_from_score_f() {
assert_eq!(HealthGrade::from_score(0), HealthGrade::F);
assert_eq!(HealthGrade::from_score(25), HealthGrade::F);
assert_eq!(HealthGrade::from_score(49), HealthGrade::F);
}
#[test]
fn test_health_grade_as_str() {
assert_eq!(HealthGrade::A.as_str(), "A");
assert_eq!(HealthGrade::B.as_str(), "B");
assert_eq!(HealthGrade::C.as_str(), "C");
assert_eq!(HealthGrade::D.as_str(), "D");
assert_eq!(HealthGrade::E.as_str(), "E");
assert_eq!(HealthGrade::F.as_str(), "F");
}
#[test]
fn test_health_grade_is_passing() {
assert!(HealthGrade::A.is_passing());
assert!(HealthGrade::B.is_passing());
assert!(HealthGrade::C.is_passing());
assert!(!HealthGrade::D.is_passing());
assert!(!HealthGrade::E.is_passing());
assert!(!HealthGrade::F.is_passing());
}
#[test]
fn test_health_grade_debug_clone_eq() {
let _ = format!("{:?}", HealthGrade::A);
let a = HealthGrade::B;
let b = a;
assert_eq!(a, b);
assert_ne!(HealthGrade::A, HealthGrade::F);
}
#[test]
fn test_thresholds_constants() {
assert_eq!(thresholds::IDEAL_MAX, 200);
assert_eq!(thresholds::ACCEPTABLE_MAX, 500);
assert_eq!(thresholds::WARNING_MAX, 1000);
assert_eq!(thresholds::PROBLEM_MAX, 2000);
}
#[test]
fn test_file_health_metrics_calculate_small_file() {
let metrics = FileHealthMetrics::calculate(
PathBuf::from("small.rs"),
100, 50, 5.0, 1, );
assert_eq!(metrics.lines, 100);
assert_eq!(metrics.test_lines, 50);
assert!(metrics.tlr > 0.0);
assert_eq!(metrics.size_class, FileSizeClass::Ideal);
assert!(metrics.health_score > 0);
}
#[test]
fn test_file_health_metrics_calculate_large_file() {
let metrics = FileHealthMetrics::calculate(
PathBuf::from("large.rs"),
3000, 100, 25.0, 15, );
assert_eq!(metrics.lines, 3000);
assert_eq!(metrics.size_class, FileSizeClass::Critical);
assert!(metrics.health_score < 50);
}
#[test]
fn test_file_health_metrics_calculate_zero_lines() {
let metrics = FileHealthMetrics::calculate(PathBuf::from("empty.rs"), 0, 0, 0.0, 0);
assert_eq!(metrics.lines, 0);
assert_eq!(metrics.tlr, 1.0); assert_eq!(metrics.size_class, FileSizeClass::Ideal);
}
#[test]
fn test_file_health_metrics_calculate_medium_file() {
let metrics = FileHealthMetrics::calculate(PathBuf::from("medium.rs"), 500, 250, 10.0, 5);
assert_eq!(metrics.size_class, FileSizeClass::Acceptable);
assert!(metrics.health_score >= 50);
}
#[test]
fn test_file_health_metrics_required_tlr_for_size() {
assert_eq!(FileHealthMetrics::required_tlr_for_size(50), 0.3);
assert_eq!(FileHealthMetrics::required_tlr_for_size(100), 0.3);
assert_eq!(FileHealthMetrics::required_tlr_for_size(200), 0.5);
assert_eq!(FileHealthMetrics::required_tlr_for_size(300), 0.5);
assert_eq!(FileHealthMetrics::required_tlr_for_size(400), 0.7);
assert_eq!(FileHealthMetrics::required_tlr_for_size(500), 0.7);
assert_eq!(FileHealthMetrics::required_tlr_for_size(750), 1.0);
assert_eq!(FileHealthMetrics::required_tlr_for_size(1000), 1.0);
assert_eq!(FileHealthMetrics::required_tlr_for_size(2000), 1.5);
}
#[test]
fn test_file_health_metrics_score_components() {
let metrics = FileHealthMetrics::calculate(
PathBuf::from("perfect.rs"),
50, 100, 3.0, 0, );
assert!(
metrics.health_score >= 90,
"Perfect file should score 90+, got {}",
metrics.health_score
);
assert_eq!(metrics.grade, HealthGrade::A);
}
#[test]
fn test_file_health_metrics_debug_clone() {
let metrics = FileHealthMetrics::calculate(PathBuf::from("test.rs"), 100, 50, 5.0, 1);
let _ = format!("{:?}", metrics);
let cloned = metrics.clone();
assert_eq!(cloned.lines, metrics.lines);
}
#[test]
fn test_file_health_report_from_empty_files() {
let report = FileHealthReport::from_files(PathBuf::from("."), vec![]);
assert_eq!(report.total_files, 0);
assert_eq!(report.total_lines, 0);
assert_eq!(report.average_health, 100);
assert!(report.is_compliant);
assert!(report.critical_files.is_empty());
assert!(report.problem_files.is_empty());
assert!(report.warning_files.is_empty());
}
#[test]
fn test_file_health_report_from_mixed_files() {
let files = vec![
FileHealthMetrics::calculate(PathBuf::from("good.rs"), 100, 50, 5.0, 1),
FileHealthMetrics::calculate(PathBuf::from("bad.rs"), 3000, 10, 25.0, 20),
FileHealthMetrics::calculate(PathBuf::from("medium.rs"), 500, 200, 12.0, 5),
];
let report = FileHealthReport::from_files(PathBuf::from("."), files);
assert_eq!(report.total_files, 3);
assert!(report.total_lines > 0);
assert!(report.average_health > 0);
let _ = format!("{:?}", report);
}
#[test]
fn test_file_health_report_recommendations() {
let files = vec![
FileHealthMetrics::calculate(PathBuf::from("critical1.rs"), 5000, 0, 30.0, 25),
FileHealthMetrics::calculate(PathBuf::from("critical2.rs"), 4000, 0, 28.0, 20),
];
let report = FileHealthReport::from_files(PathBuf::from("."), files);
assert!(!report.is_compliant);
assert!(!report.recommendations.is_empty());
assert!(!report.critical_files.is_empty());
}
#[test]
fn test_file_health_report_all_healthy() {
let files = vec![
FileHealthMetrics::calculate(PathBuf::from("a.rs"), 50, 25, 3.0, 0),
FileHealthMetrics::calculate(PathBuf::from("b.rs"), 80, 40, 4.0, 1),
];
let report = FileHealthReport::from_files(PathBuf::from("."), files);
assert!(report.is_compliant);
assert_eq!(report.healthy_files_count, 2);
assert!(report.critical_files.is_empty());
}
#[test]
fn test_file_health_baseline_new() {
let baseline = FileHealthBaseline::new();
assert_eq!(baseline.version, "1.0");
assert!(baseline.files.is_empty());
assert!(!baseline.generated.is_empty());
}
#[test]
fn test_file_health_baseline_default() {
let baseline = FileHealthBaseline::default();
assert_eq!(baseline.version, "1.0");
}
#[test]
fn test_file_health_baseline_add_file() {
let mut baseline = FileHealthBaseline::new();
let metrics = FileHealthMetrics::calculate(PathBuf::from("test.rs"), 100, 50, 5.0, 1);
baseline.add_file(&metrics);
assert_eq!(baseline.files.len(), 1);
let entry = baseline.files.get("test.rs").unwrap();
assert_eq!(entry.lines, 100);
assert_eq!(entry.test_lines, 50);
assert_eq!(entry.health, metrics.health_score);
}
#[test]
fn test_file_health_baseline_check_ratchet_no_violation() {
let mut baseline = FileHealthBaseline::new();
let metrics = FileHealthMetrics::calculate(PathBuf::from("test.rs"), 100, 50, 5.0, 1);
baseline.add_file(&metrics);
assert!(baseline.check_ratchet("test.rs", 100).is_none());
assert!(baseline.check_ratchet("test.rs", 50).is_none());
}
#[test]
fn test_file_health_baseline_check_ratchet_violation() {
let mut baseline = FileHealthBaseline::new();
let metrics = FileHealthMetrics::calculate(PathBuf::from("test.rs"), 100, 50, 5.0, 1);
baseline.add_file(&metrics);
let violation = baseline.check_ratchet("test.rs", 150).unwrap();
assert_eq!(violation.baseline_lines, 100);
assert_eq!(violation.current_lines, 150);
assert_eq!(violation.growth, 50);
assert_eq!(violation.path, "test.rs");
}
#[test]
fn test_file_health_baseline_check_ratchet_unknown_file() {
let baseline = FileHealthBaseline::new();
assert!(baseline.check_ratchet("unknown.rs", 100).is_none());
}
#[test]
fn test_file_health_baseline_save_load() {
let temp_dir = TempDir::new().unwrap();
let path = temp_dir.path().join("baseline.json");
let mut baseline = FileHealthBaseline::new();
let metrics = FileHealthMetrics::calculate(PathBuf::from("test.rs"), 200, 100, 8.0, 3);
baseline.add_file(&metrics);
baseline.save(&path).unwrap();
let loaded = FileHealthBaseline::load(&path).unwrap();
assert_eq!(loaded.version, "1.0");
assert_eq!(loaded.files.len(), 1);
let entry = loaded.files.get("test.rs").unwrap();
assert_eq!(entry.lines, 200);
}
#[test]
fn test_ratchet_violation_debug_clone() {
let violation = RatchetViolation {
path: "test.rs".to_string(),
baseline_lines: 100,
current_lines: 150,
growth: 50,
};
let _ = format!("{:?}", violation);
let cloned = violation.clone();
assert_eq!(cloned.growth, 50);
}
#[test]
fn test_analyze_file_real_file() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.rs");
std::fs::write(&file_path, "fn main() {\n println!(\"hello\");\n}\n").unwrap();
let metrics = analyze_file(&file_path, 5, 3.0, 1);
assert!(metrics.is_some());
let m = metrics.unwrap();
assert_eq!(m.lines, 3);
assert_eq!(m.test_lines, 5);
}
#[test]
fn test_analyze_file_nonexistent() {
let result = analyze_file(Path::new("/nonexistent/file.rs"), 0, 0.0, 0);
assert!(result.is_none());
}
#[test]
fn test_count_lines_real_file() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.rs");
std::fs::write(&file_path, "line1\nline2\nline3\nline4\nline5\n").unwrap();
assert_eq!(count_lines(&file_path), Some(5));
}
#[test]
fn test_count_lines_empty_file() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("empty.rs");
std::fs::write(&file_path, "").unwrap();
assert_eq!(count_lines(&file_path), Some(0));
}
#[test]
fn test_count_lines_nonexistent() {
assert!(count_lines(Path::new("/nonexistent")).is_none());
}
#[test]
fn test_scan_directory_with_files() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("a.rs"), "fn a() {}").unwrap();
std::fs::write(temp_dir.path().join("b.rs"), "fn b() {}").unwrap();
std::fs::write(temp_dir.path().join("c.txt"), "not rust").unwrap();
let files = scan_directory(temp_dir.path(), &["rs"], &[]);
assert_eq!(files.len(), 2);
}
#[test]
fn test_scan_directory_with_exclude() {
let temp_dir = TempDir::new().unwrap();
let sub_dir = temp_dir.path().join("target");
std::fs::create_dir_all(&sub_dir).unwrap();
std::fs::write(temp_dir.path().join("main.rs"), "fn main() {}").unwrap();
std::fs::write(sub_dir.join("build.rs"), "fn build() {}").unwrap();
let files = scan_directory(temp_dir.path(), &["rs"], &["target"]);
assert!(files.len() <= 2);
}
#[test]
fn test_scan_directory_empty() {
let temp_dir = TempDir::new().unwrap();
let files = scan_directory(temp_dir.path(), &["rs"], &[]);
assert!(files.is_empty());
}
#[test]
fn test_file_size_class_serde() {
let class = FileSizeClass::Warning;
let json = serde_json::to_string(&class).unwrap();
let back: FileSizeClass = serde_json::from_str(&json).unwrap();
assert_eq!(back, class);
}
#[test]
fn test_health_grade_serde() {
let grade = HealthGrade::B;
let json = serde_json::to_string(&grade).unwrap();
let back: HealthGrade = serde_json::from_str(&json).unwrap();
assert_eq!(back, grade);
}
#[test]
fn test_file_health_metrics_serde() {
let metrics = FileHealthMetrics::calculate(PathBuf::from("test.rs"), 100, 50, 5.0, 1);
let json = serde_json::to_string(&metrics).unwrap();
let back: FileHealthMetrics = serde_json::from_str(&json).unwrap();
assert_eq!(back.lines, metrics.lines);
assert_eq!(back.health_score, metrics.health_score);
}
#[test]
fn test_baseline_entry_serde() {
let mut baseline = FileHealthBaseline::new();
let metrics = FileHealthMetrics::calculate(PathBuf::from("test.rs"), 100, 50, 5.0, 1);
baseline.add_file(&metrics);
let json = serde_json::to_string(&baseline).unwrap();
let back: FileHealthBaseline = serde_json::from_str(&json).unwrap();
assert_eq!(back.files.len(), 1);
}