#[tokio::test]
async fn test_check_duplicates_unique_files() -> anyhow::Result<()> {
let temp_dir = TempDir::new()?;
let file1 = temp_dir.path().join("unique1.rs");
let file2 = temp_dir.path().join("unique2.rs");
tokio::fs::write(&file1, "fn unique_function_one() { println!(\"one\"); }").await?;
tokio::fs::write(&file2, "fn unique_function_two() { println!(\"two\"); }").await?;
let violations = check_duplicates(temp_dir.path()).await?;
assert_eq!(violations.len(), 0);
Ok(())
}
#[tokio::test]
async fn test_check_duplicates_ignores_small_files() -> anyhow::Result<()> {
let temp_dir = TempDir::new()?;
let small_content = "x";
let small1 = temp_dir.path().join("small1.rs");
let small2 = temp_dir.path().join("small2.rs");
tokio::fs::write(&small1, small_content).await?;
tokio::fs::write(&small2, small_content).await?;
let violations = check_duplicates(temp_dir.path()).await?;
assert_eq!(violations.len(), 0);
Ok(())
}
#[test]
fn test_is_build_artifact() {
use std::path::Path;
assert!(is_build_artifact(Path::new(
"./target/debug/build/pmat-123/out/tool_registry.rs"
)));
assert!(is_build_artifact(Path::new("target/debug/deps/pmat.rs")));
assert!(is_build_artifact(Path::new("./build/generated.rs")));
assert!(is_build_artifact(Path::new("./out/alias_table.rs")));
assert!(is_build_artifact(Path::new(
"./.cargo/registry/src/github.com/file.rs"
)));
assert!(is_build_artifact(Path::new(
"./node_modules/package/lib.js"
)));
assert!(is_build_artifact(Path::new("./dist/bundle.js")));
assert!(is_build_artifact(Path::new("./.git/objects/ab/cd1234")));
assert!(is_build_artifact(Path::new("./generated/proto.rs")));
assert!(!is_build_artifact(Path::new("./server/src/lib.rs")));
assert!(!is_build_artifact(Path::new("./src/main.rs")));
assert!(!is_build_artifact(Path::new(
"./server/src/handlers/tools.rs"
)));
}
#[test]
fn test_is_excluded_directory() {
assert!(is_excluded_directory("./target"));
assert!(is_excluded_directory("target"));
assert!(is_excluded_directory("target/"));
assert!(is_excluded_directory("./target/"));
assert!(is_excluded_directory("./build"));
assert!(is_excluded_directory("build"));
assert!(is_excluded_directory("./project/target/"));
assert!(is_excluded_directory("./project/target/debug"));
assert!(is_excluded_directory("./target/debug/build"));
assert!(is_excluded_directory("./foo/node_modules/"));
assert!(is_excluded_directory("./bar/.git/"));
assert!(is_excluded_directory(
"./server/target/debug/build/unicode_names2-c78072d37d9beb66/out/generated.rs"
));
assert!(is_excluded_directory(
"./target/debug/build/rustpython-parser-5d3dfbfd27d1a200/out/keywords.rs"
));
assert!(!is_excluded_directory("server"));
assert!(!is_excluded_directory("src"));
assert!(!is_excluded_directory("./server/src"));
assert!(!is_excluded_directory("./server/src/cli"));
}
#[tokio::test]
async fn test_check_single_file_complexity_violations() -> anyhow::Result<()> {
let temp_dir = TempDir::new()?;
let rust_file = temp_dir.path().join("complex.rs");
tokio::fs::write(
&rust_file,
r#"
fn high_complexity_function(x: i32) -> i32 {
if x > 10 {
if x > 20 {
if x > 30 {
if x > 40 {
if x > 50 {
100
} else {
90
}
} else {
80
}
} else {
70
}
} else {
60
}
} else {
50
}
}
"#,
)
.await?;
let violations = check_single_file_complexity(
temp_dir.path(),
&rust_file,
5, )
.await?;
assert!(!violations.is_empty());
assert!(violations.iter().any(|v| v.check_type == "complexity"));
assert!(violations.iter().any(|v| v.severity == "error"));
Ok(())
}
#[tokio::test]
async fn test_check_single_file_complexity_missing_file() -> anyhow::Result<()> {
let temp_dir = TempDir::new()?;
let missing_file = temp_dir.path().join("missing.rs");
let result = check_single_file_complexity(temp_dir.path(), &missing_file, 10).await;
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("File not found"));
Ok(())
}
#[tokio::test]
async fn test_check_single_file_complexity_no_violations() -> anyhow::Result<()> {
let temp_dir = TempDir::new()?;
let simple_file = temp_dir.path().join("simple.rs");
tokio::fs::write(
&simple_file,
r#"
fn simple_function(x: i32) -> i32 {
x * 2
}
fn another_simple(y: i32) -> i32 {
y + 1
}
"#,
)
.await?;
let violations = check_single_file_complexity(
temp_dir.path(),
&simple_file,
10, )
.await?;
assert_eq!(violations.len(), 0);
Ok(())
}
#[test]
fn test_write_markdown_summary_table() -> anyhow::Result<()> {
use crate::models::churn::ChurnSummary;
use std::collections::HashMap;
let mut output = String::new();
let summary = ChurnSummary {
total_commits: 42,
total_files_changed: 15,
hotspot_files: vec!["file1.rs".into(), "file2.rs".into()],
stable_files: vec!["lib.rs".into()],
author_contributions: {
let mut map = HashMap::new();
map.insert("alice".to_string(), 5);
map.insert("bob".to_string(), 3);
map
},
mean_churn_score: 0.0,
variance_churn_score: 0.0,
stddev_churn_score: 0.0,
};
write_markdown_summary_table(&mut output, &summary)?;
assert!(output.contains("## Summary Statistics"));
assert!(output.contains("| Metric | Value |"));
assert!(output.contains("| Total Commits | 42 |"));
assert!(output.contains("| Files Changed | 15 |"));
assert!(output.contains("| Hotspot Files | 2 |"));
assert!(output.contains("| Stable Files | 1 |"));
assert!(output.contains("| Contributing Authors | 2 |"));
Ok(())
}
#[test]
fn test_write_markdown_summary_table_empty() -> anyhow::Result<()> {
use crate::models::churn::ChurnSummary;
use std::collections::HashMap;
let mut output = String::new();
let empty_summary = ChurnSummary {
total_commits: 0,
total_files_changed: 0,
hotspot_files: vec![],
stable_files: vec![],
author_contributions: HashMap::new(),
mean_churn_score: 0.0,
variance_churn_score: 0.0,
stddev_churn_score: 0.0,
};
write_markdown_summary_table(&mut output, &empty_summary)?;
assert!(output.contains("## Summary Statistics"));
assert!(output.contains("| Total Commits | 0 |"));
assert!(output.contains("| Hotspot Files | 0 |"));
Ok(())
}
#[test]
fn test_write_markdown_summary_table_format() -> anyhow::Result<()> {
use crate::models::churn::ChurnSummary;
use std::collections::HashMap;
let mut output = String::new();
let summary = ChurnSummary {
total_commits: 1,
total_files_changed: 1,
hotspot_files: vec!["test.rs".into()],
stable_files: vec!["mod.rs".into()],
author_contributions: {
let mut map = HashMap::new();
map.insert("dev".to_string(), 1);
map
},
mean_churn_score: 0.0,
variance_churn_score: 0.0,
stddev_churn_score: 0.0,
};
write_markdown_summary_table(&mut output, &summary)?;
assert!(output.contains("|--------|-------|"));
let lines: Vec<&str> = output.lines().collect();
let table_lines: Vec<&str> = lines
.iter()
.filter(|line| line.contains("|"))
.cloned()
.collect();
assert!(table_lines.len() >= 3);
Ok(())
}
#[test]
fn test_print_single_check_all_types() {
use crate::cli::enums::QualityCheckType;
print_single_check(&QualityCheckType::Complexity);
print_single_check(&QualityCheckType::DeadCode);
print_single_check(&QualityCheckType::Satd);
print_single_check(&QualityCheckType::Security);
print_single_check(&QualityCheckType::Entropy);
print_single_check(&QualityCheckType::Duplicates);
print_single_check(&QualityCheckType::Coverage);
}
#[test]
fn test_print_single_check_all_and_wildcard() {
use crate::cli::enums::QualityCheckType;
print_single_check(&QualityCheckType::All);
}
#[tokio::test]
async fn test_handle_analyze_makefile_basic() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
let mut file = std::fs::File::create(&makefile_path).unwrap();
writeln!(file, "all:").unwrap();
writeln!(file, "\techo 'Hello World'").unwrap();
let result = handle_analyze_makefile(
makefile_path.clone(),
vec![], MakefileOutputFormat::Human,
false,
None,
10, )
.await;
assert!(
result.is_ok(),
"Makefile analysis failed: {:?}",
result.err()
);
}
#[tokio::test]
async fn test_handle_analyze_makefile_with_rules() {
let temp_dir = TempDir::new().unwrap();
let makefile_path = temp_dir.path().join("Makefile");
let mut file = std::fs::File::create(&makefile_path).unwrap();
writeln!(file, "test:").unwrap();
writeln!(file, "\tcargo test").unwrap();
let result = handle_analyze_makefile(
makefile_path,
vec!["phonytargets".to_string()],
MakefileOutputFormat::Json,
false,
Some("3.82".to_string()),
10, )
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_handle_analyze_provability() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path().to_path_buf();
let src_dir = project_path.join("src");
std::fs::create_dir_all(&src_dir).unwrap();
let rust_file = src_dir.join("lib.rs");
let mut file = std::fs::File::create(&rust_file).unwrap();
writeln!(file, "pub fn add(a: i32, b: i32) -> i32 {{").unwrap();
writeln!(file, " a + b").unwrap();
writeln!(file, "}}").unwrap();
let result = handle_analyze_provability(
project_path,
vec!["add".to_string()], 10, ProvabilityOutputFormat::Json,
false, false, None, 10, )
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_handle_analyze_defect_prediction() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path().to_path_buf();
let src_dir = project_path.join("src");
std::fs::create_dir_all(&src_dir).unwrap();
let rust_file = src_dir.join("main.rs");
let mut file = std::fs::File::create(&rust_file).unwrap();
writeln!(file, "fn main() {{").unwrap();
writeln!(file, " println!(\"Hello, world!\");").unwrap();
writeln!(file, "}}").unwrap();
let result = handle_analyze_defect_prediction(
project_path,
0.5, 10, false, DefectPredictionOutputFormat::Summary,
false, false, None, None, None, false, 10, )
.await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_handle_analyze_proof_annotations() {
let temp_dir = TempDir::new().unwrap();
let project_path = temp_dir.path().to_path_buf();
let result = handle_analyze_proof_annotations(
project_path,
ProofAnnotationOutputFormat::Json,
false, false, None, None, None, false, false, )
.await;
assert!(result.is_ok());
}