use repopilot::audits::code_quality::complexity::{ComplexityAudit, count_branches};
use repopilot::audits::traits::FileAudit;
use repopilot::findings::types::Severity;
use repopilot::scan::config::ScanConfig;
use repopilot::scan::facts::FileFacts;
use std::path::PathBuf;
fn make_file(lines_of_code: usize, branch_count: usize) -> FileFacts {
FileFacts {
path: PathBuf::from("src/lib.rs"),
language: Some("Rust".to_string()),
lines_of_code,
branch_count,
imports: Vec::new(),
content: String::new(),
}
}
#[test]
fn no_finding_for_low_density() {
let file = make_file(100, 10);
let findings = ComplexityAudit.audit(&file, &ScanConfig::default());
assert!(findings.is_empty());
}
#[test]
fn medium_finding_at_medium_threshold() {
let file = make_file(100, 25);
let findings = ComplexityAudit.audit(&file, &ScanConfig::default());
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].rule_id, "code-quality.complex-file");
assert_eq!(findings[0].severity, Severity::Medium);
}
#[test]
fn high_finding_at_high_threshold() {
let file = make_file(100, 50);
let findings = ComplexityAudit.audit(&file, &ScanConfig::default());
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].rule_id, "code-quality.complex-file");
assert_eq!(findings[0].severity, Severity::High);
}
#[test]
fn high_severity_wins_over_medium() {
let file = make_file(100, 40);
let findings = ComplexityAudit.audit(&file, &ScanConfig::default());
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].severity, Severity::High);
}
#[test]
fn skips_files_under_ten_loc() {
let file = make_file(5, 50);
let findings = ComplexityAudit.audit(&file, &ScanConfig::default());
assert!(findings.is_empty());
}
#[test]
fn skips_unsupported_language() {
let file = FileFacts {
path: PathBuf::from("README.md"),
language: Some("Markdown".to_string()),
lines_of_code: 100,
branch_count: 50,
imports: Vec::new(),
content: String::new(),
};
let findings = ComplexityAudit.audit(&file, &ScanConfig::default());
assert!(findings.is_empty());
}
#[test]
fn config_override_raises_threshold() {
let file = make_file(100, 25);
let config = ScanConfig {
complexity_medium_threshold: 300,
..ScanConfig::default()
};
let findings = ComplexityAudit.audit(&file, &config);
assert!(findings.is_empty());
}
#[test]
fn config_override_lowers_threshold() {
let file = make_file(100, 10);
let config = ScanConfig {
complexity_medium_threshold: 50,
complexity_high_threshold: 400,
..ScanConfig::default()
};
let findings = ComplexityAudit.audit(&file, &config);
assert_eq!(findings.len(), 1);
assert_eq!(findings[0].severity, Severity::Medium);
}
#[test]
fn counts_if_for_while_match() {
let code = "if x { for y in z { while cond { match v { } } } }";
assert_eq!(count_branches(code), 4);
}
#[test]
fn counts_else_keyword() {
let code = "if x { } else { }";
assert_eq!(count_branches(code), 2);
}
#[test]
fn does_not_count_else_inside_longer_word() {
let code = "let elsewhere = true;";
assert_eq!(count_branches(code), 0);
}
#[test]
fn skips_comment_lines() {
let code = "// if this were counted it would be a bug\nif real_condition {";
assert_eq!(count_branches(code), 1);
}
#[test]
fn counts_logical_operators() {
let code = "if a && b || c {";
assert_eq!(count_branches(code), 3);
}