#[cfg(test)]
mod tests {
use super::*;
fn make_result(name: &str, doc: Option<&str>) -> QueryResult {
QueryResult {
function_name: name.to_string(),
file_path: "src/test.rs".to_string(),
signature: format!("fn {}()", name),
definition_type: "function".to_string(),
doc_comment: doc.map(|s| s.to_string()),
start_line: 1,
end_line: 10,
language: "Rust".to_string(),
tdg_score: 80.0,
tdg_grade: "A".to_string(),
complexity: 5,
big_o: "O(1)".to_string(),
satd_count: 0,
loc: 10,
relevance_score: 0.95,
source: None,
calls: Vec::new(),
called_by: Vec::new(),
pagerank: 0.0,
in_degree: 0,
out_degree: 0,
commit_count: 0,
churn_score: 0.0,
clone_count: 0,
duplication_score: 0.0,
pattern_diversity: 0.0,
fault_annotations: Vec::new(),
line_coverage_pct: 0.0,
lines_covered: 0,
lines_total: 0,
missed_lines: 0,
impact_score: 0.0,
coverage_status: String::new(),
coverage_diff: 0.0,
coverage_exclusion: Default::default(),
coverage_excluded: false,
cross_project_callers: 0,
io_classification: String::new(),
io_patterns: Vec::new(),
suggested_module: String::new(),
contract_level: None,
contract_equation: None,
}
}
#[test]
fn test_format_text_with_code_multibyte_doc_comment() {
let result = make_result(
"verify_output",
Some("Verify output is correct: not empty, no garbage, contains expected answer (PMAT-QA-PROTOCOL-001 §7.5) Order of checks is CRITICAL for safety"),
);
let output = format_text_with_code(&[result], None);
assert!(output.contains("verify_output"));
assert!(output.contains("..."));
}
#[test]
fn test_format_text_short_doc_no_truncation() {
let result = make_result("foo", Some("Short doc"));
let output = format_text_with_code(&[result], None);
assert!(output.contains("Short doc"));
}
#[test]
fn test_highlight_matches_literal() {
let line = "let result = unwrap();";
let out = highlight_matches_in_line(line, "unwrap()", false);
assert!(out.contains("\x1b[1;43m"), "missing highlight start");
assert!(out.contains("unwrap()"), "missing matched text");
assert!(out.contains("\x1b[0m"), "missing highlight end");
}
#[test]
fn test_highlight_matches_literal_case_insensitive() {
let line = "fn HandleRequest() {}";
let out = highlight_matches_in_line(line, "handlerequest", false);
assert!(out.contains("HandleRequest"));
assert!(out.contains("\x1b[1;43m"));
}
#[test]
fn test_highlight_matches_regex() {
let line = "fn handle_request(ctx: Context) {}";
let out = highlight_matches_in_line(line, r"fn\s+handle_\w+", true);
assert!(out.contains("\x1b[1;43m"));
assert!(out.contains("fn handle_request"));
}
#[test]
fn test_highlight_matches_no_match() {
let line = "let x = 42;";
let out = highlight_matches_in_line(line, "nonexistent", false);
assert_eq!(out, line);
}
#[test]
fn test_highlight_matches_invalid_regex() {
let line = "some text here";
let out = highlight_matches_in_line(line, "[invalid", true);
assert_eq!(out, line);
}
#[test]
fn test_format_text_with_code_literal_highlight() {
let mut result = make_result("test_fn", None);
result.source = Some("fn test_fn() {\n unwrap();\n}".to_string());
let output = format_text_with_code(&[result], Some(("unwrap()", false)));
assert!(output.contains("\x1b[1;43m"), "missing highlight in output");
assert!(output.contains("\u{2502}"), "missing line number separator");
}
fn result_with_coverage(status: &str, pct: f32, total: u32) -> QueryResult {
let mut r = make_result("f", None);
r.coverage_status = status.to_string();
r.line_coverage_pct = pct;
r.lines_total = total;
r.lines_covered = ((pct as u32) * total) / 100;
r.missed_lines = total - r.lines_covered;
r
}
#[test]
fn test_format_coverage_metrics_md_uncovered_arm() {
let r = result_with_coverage("uncovered", 0.0, 50);
let mut out = String::new();
format_coverage_metrics_md(&r, &mut out);
assert!(out.contains("Uncovered (0/50 lines)"));
}
#[test]
fn test_format_coverage_metrics_md_partial_arm() {
let r = result_with_coverage("partial", 50.0, 100);
let mut out = String::new();
format_coverage_metrics_md(&r, &mut out);
assert!(out.contains("Coverage: 50%"));
assert!(out.contains("missed lines"));
}
#[test]
fn test_format_coverage_metrics_md_full_arm() {
let r = result_with_coverage("full", 100.0, 30);
let mut out = String::new();
format_coverage_metrics_md(&r, &mut out);
assert!(out.contains("Fully covered"));
assert!(out.contains("(30 lines)"));
}
#[test]
fn test_format_coverage_metrics_md_unknown_status_emits_nothing() {
let r = result_with_coverage("xyz", 0.0, 0);
let mut out = String::new();
format_coverage_metrics_md(&r, &mut out);
assert!(out.is_empty());
}
#[test]
fn test_format_coverage_metrics_md_high_impact_appends_marker() {
let mut r = result_with_coverage("full", 100.0, 10);
r.impact_score = 5.5;
let mut out = String::new();
format_coverage_metrics_md(&r, &mut out);
assert!(out.contains("Impact: 5.5"));
}
#[test]
fn test_format_coverage_diff_md_positive_arm() {
let mut out = String::new();
format_coverage_diff_md(2.5, &mut out);
assert!(out.contains("+2.5% coverage"));
}
#[test]
fn test_format_coverage_diff_md_negative_arm() {
let mut out = String::new();
format_coverage_diff_md(-1.0, &mut out);
assert!(out.contains("-1.0% coverage"));
}
#[test]
fn test_format_coverage_diff_md_zero_writes_nothing() {
let mut out = String::new();
format_coverage_diff_md(0.0, &mut out);
assert!(out.is_empty());
}
#[test]
fn test_format_coverage_metrics_text_partial_low_uses_red_color() {
let r = result_with_coverage("partial", 30.0, 100);
let mut out = String::new();
format_coverage_metrics_text(&r, &mut out);
assert!(out.contains("\x1b[1;31m"));
assert!(out.contains("Cov: 30%"));
}
#[test]
fn test_format_coverage_metrics_text_partial_mid_uses_yellow_color() {
let r = result_with_coverage("partial", 70.0, 100);
let mut out = String::new();
format_coverage_metrics_text(&r, &mut out);
assert!(out.contains("\x1b[33m"));
}
#[test]
fn test_format_coverage_metrics_text_partial_high_uses_green_color() {
let r = result_with_coverage("partial", 90.0, 100);
let mut out = String::new();
format_coverage_metrics_text(&r, &mut out);
assert!(out.contains("\x1b[32m"));
}
#[test]
fn test_truncate_doc_short_returns_unchanged() {
let doc = "short doc";
assert_eq!(truncate_doc(doc), "short doc");
}
#[test]
fn test_truncate_doc_long_truncates_with_ellipsis() {
let long = "x".repeat(150);
let r = truncate_doc(&long);
assert!(r.ends_with("..."));
assert!(r.len() <= 100);
}
#[test]
fn test_truncate_doc_takes_only_first_line() {
let multi = "first line is short\nsecond line is long";
let r = truncate_doc(multi);
assert_eq!(r, "first line is short");
}
#[test]
fn test_push_pagerank_metric_zero_no_metric() {
let r = make_result("f", None);
let mut metrics = vec![];
push_pagerank_metric(&r, &mut metrics);
assert!(metrics.is_empty());
}
#[test]
fn test_push_pagerank_metric_high_tier_emits_star() {
let mut r = make_result("f", None);
r.pagerank = 0.005; let mut metrics = vec![];
push_pagerank_metric(&r, &mut metrics);
assert_eq!(metrics.len(), 1);
assert!(metrics[0].contains("★"));
}
#[test]
fn test_push_pagerank_metric_low_tier_omits() {
let mut r = make_result("f", None);
r.pagerank = 0.00005; let mut metrics = vec![];
push_pagerank_metric(&r, &mut metrics);
assert!(metrics.is_empty());
}
#[test]
fn test_push_indegree_metric_zero_no_metric() {
let r = make_result("f", None);
let mut metrics = vec![];
push_indegree_metric(&r, &mut metrics);
assert!(metrics.is_empty());
}
#[test]
fn test_push_indegree_metric_low_uses_plain() {
let mut r = make_result("f", None);
r.in_degree = 3;
let mut metrics = vec![];
push_indegree_metric(&r, &mut metrics);
assert_eq!(metrics.len(), 1);
assert!(metrics[0].contains("↓3"));
}
#[test]
fn test_push_indegree_metric_high_uses_green() {
let mut r = make_result("f", None);
r.in_degree = 10;
let mut metrics = vec![];
push_indegree_metric(&r, &mut metrics);
assert_eq!(metrics.len(), 1);
assert!(metrics[0].contains("\x1b[1;32m"));
}
#[test]
fn test_push_churn_metric_rich_zero_commit_no_metric() {
let r = make_result("f", None);
let mut metrics = vec![];
push_churn_metric_rich(&r, &mut metrics);
assert!(metrics.is_empty());
}
#[test]
fn test_push_churn_metric_rich_high_tier_uses_fire() {
let mut r = make_result("f", None);
r.commit_count = 10;
r.churn_score = 0.8;
let mut metrics = vec![];
push_churn_metric_rich(&r, &mut metrics);
assert!(metrics[0].contains("🔥"));
}
#[test]
fn test_push_entropy_metric_zero_no_metric() {
let r = make_result("f", None);
let mut metrics = vec![];
push_entropy_metric(&r, &mut metrics);
assert!(metrics.is_empty());
}
#[test]
fn test_push_entropy_metric_low_diversity_emits_repetitive() {
let mut r = make_result("f", None);
r.pattern_diversity = 0.2;
let mut metrics = vec![];
push_entropy_metric(&r, &mut metrics);
assert!(metrics[0].contains("🔄"));
}
#[test]
fn test_push_entropy_metric_high_diversity_emits_h_marker() {
let mut r = make_result("f", None);
r.pattern_diversity = 0.9;
let mut metrics = vec![];
push_entropy_metric(&r, &mut metrics);
assert!(metrics[0].contains("H:"));
}
#[test]
fn test_push_entropy_metric_mid_diversity_omits() {
let mut r = make_result("f", None);
r.pattern_diversity = 0.5;
let mut metrics = vec![];
push_entropy_metric(&r, &mut metrics);
assert!(metrics.is_empty());
}
#[test]
fn test_build_rich_metrics_baseline_has_complexity_and_loc() {
let r = make_result("f", None);
let metrics = build_rich_metrics(&r);
assert!(metrics.iter().any(|m| m.starts_with("C:")));
assert!(metrics.iter().any(|m| m.starts_with("L:")));
}
#[test]
fn test_build_rich_metrics_with_satd_appends_warn_marker() {
let mut r = make_result("f", None);
r.satd_count = 3;
let metrics = build_rich_metrics(&r);
assert!(metrics.iter().any(|m| m.contains("⚠3")));
}
#[test]
fn test_build_rich_metrics_with_clone_count_appends_clipboard() {
let mut r = make_result("f", None);
r.clone_count = 2;
let metrics = build_rich_metrics(&r);
assert!(metrics.iter().any(|m| m.contains("📋2")));
}
}