#![cfg_attr(coverage_nightly, coverage(off))]
use std::fs;
use tempfile::TempDir;
#[tokio::test]
async fn test_satd_only_appears_when_debt_found() {
let temp_dir = TempDir::new().unwrap();
let clean_file = temp_dir.path().join("clean.rs");
fs::write(
&clean_file,
r#"
fn add_numbers(a: i32, b: i32) -> i32 {
a + b
}
fn multiply(x: i32, y: i32) -> i32 {
x * y
}
"#,
)
.unwrap();
let output_file = temp_dir.path().join("context.md");
let result = crate::cli::handlers::utility_handlers::handle_context(
Some("rust".to_string()),
temp_dir.path().to_path_buf(),
Some(output_file.clone()),
crate::cli::ContextFormat::Markdown,
false,
false,
None,
None,
)
.await;
assert!(result.is_ok());
let output = fs::read_to_string(output_file).unwrap();
assert!(
!output.contains("[SATD:"),
"Clean code should not have SATD annotations. Found in output: {}",
output
.lines()
.filter(|line| line.contains("SATD"))
.collect::<Vec<_>>()
.join("\n")
);
}
#[tokio::test]
async fn test_satd_appears_with_count_when_debt_exists() {
let temp_dir = TempDir::new().unwrap();
let debt_file = temp_dir.path().join("debt.rs");
fs::write(
&debt_file,
r#"
// TODO: Optimize this algorithm
// FIXME: Handle edge cases properly
fn bad_function() -> i32 {
// HACK: This is a temporary solution
42
}
"#,
)
.unwrap();
let output_file = temp_dir.path().join("context.md");
let result = crate::cli::handlers::utility_handlers::handle_context(
Some("rust".to_string()),
temp_dir.path().to_path_buf(),
Some(output_file.clone()),
crate::cli::ContextFormat::Markdown,
false,
false,
None,
None,
)
.await;
assert!(result.is_ok());
let output = fs::read_to_string(output_file).unwrap();
assert!(
output.contains("[satd: 3 items]")
|| output.contains("[SATD: 3 items]")
|| output.contains("[satd: 3]"),
"Should show SATD count of 3. Output: {}",
output
);
}
#[tokio::test]
async fn test_churn_annotations_appear() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.rs");
fs::write(&test_file, "fn test() {}").unwrap();
std::process::Command::new("git")
.arg("init")
.current_dir(temp_dir.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["add", "."])
.current_dir(temp_dir.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["commit", "-m", "initial"])
.current_dir(temp_dir.path())
.output()
.unwrap();
let output_file = temp_dir.path().join("context.md");
let result = crate::cli::handlers::utility_handlers::handle_context(
Some("rust".to_string()),
temp_dir.path().to_path_buf(),
Some(output_file.clone()),
crate::cli::ContextFormat::Markdown,
false,
false,
None,
None,
)
.await;
assert!(result.is_ok());
let output = fs::read_to_string(output_file).unwrap();
assert!(
output.contains("[churn:") || output.contains("[CHURN:"),
"Churn annotations missing from output"
);
}
#[tokio::test]
async fn test_overall_health_is_normalized_tdg_score() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.rs");
fs::write(&test_file, "fn simple() -> i32 { 42 }").unwrap();
let output_file = temp_dir.path().join("context.md");
let result = crate::cli::handlers::utility_handlers::handle_context(
Some("rust".to_string()),
temp_dir.path().to_path_buf(),
Some(output_file.clone()),
crate::cli::ContextFormat::Markdown,
false,
false,
None,
None,
)
.await;
assert!(result.is_ok());
let output = fs::read_to_string(output_file).unwrap();
let health_line = output
.lines()
.find(|line| line.contains("Overall Health"))
.expect("Overall Health line not found");
let health_value =
extract_percentage(health_line).expect("Could not extract percentage from health line");
assert!(
(0.0..=100.0).contains(&health_value),
"Overall Health should be 0-100, got: {}%. Line: {}",
health_value,
health_line
);
}
#[tokio::test]
async fn test_coverage_is_meaningful_or_absent() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.rs");
fs::write(&test_file, "fn simple() -> i32 { 42 }").unwrap();
let output_file = temp_dir.path().join("context.md");
let result = crate::cli::handlers::utility_handlers::handle_context(
Some("rust".to_string()),
temp_dir.path().to_path_buf(),
Some(output_file.clone()),
crate::cli::ContextFormat::Markdown,
false,
false,
None,
None,
)
.await;
assert!(result.is_ok());
let output = fs::read_to_string(output_file).unwrap();
if let Some(coverage_line) = output.lines().find(|line| line.contains("Test Coverage")) {
let coverage_value = extract_percentage(coverage_line);
if let Some(value) = coverage_value {
assert!(
(0.0..=100.0).contains(&value),
"Test Coverage should be 0-100% if present, got: {}%. Line: {}",
value,
coverage_line
);
}
}
}
#[tokio::test]
async fn test_pagerank_only_for_highly_connected() {
let temp_dir = TempDir::new().unwrap();
let isolated_file = temp_dir.path().join("isolated.rs");
fs::write(&isolated_file, "fn isolated() -> i32 { 42 }").unwrap();
let connected_file = temp_dir.path().join("connected.rs");
fs::write(
&connected_file,
r#"
use std::collections::HashMap;
use std::fs;
use std::path::Path;
pub fn highly_connected() -> Vec<String> {
vec![]
}
"#,
)
.unwrap();
let output_file = temp_dir.path().join("context.md");
let result = crate::cli::handlers::utility_handlers::handle_context(
Some("rust".to_string()),
temp_dir.path().to_path_buf(),
Some(output_file.clone()),
crate::cli::ContextFormat::Markdown,
false,
false,
None,
None,
)
.await;
assert!(result.is_ok());
let output = fs::read_to_string(output_file).unwrap();
let pagerank_count =
output.matches("[pagerank:").count() + output.matches("[PageRank:").count();
let total_functions = output.matches("**Function**").count();
if pagerank_count > 0 {
let pagerank_ratio = pagerank_count as f64 / total_functions as f64;
assert!(
pagerank_ratio < 0.3, "PageRank should be selective, not appear for every function. Ratio: {:.1}%",
pagerank_ratio * 100.0
);
}
}
#[tokio::test]
async fn test_auto_scaling_concurrency() {
use crate::services::deep_context::{DeepContextAnalyzer, DeepContextConfig};
let config = DeepContextConfig::with_auto_scaling();
let _analyzer = DeepContextAnalyzer::new(config.clone());
let logical_cores = num_cpus::get();
if logical_cores > 1 {
assert!(
config.parallel > 1,
"Auto-scaling should use multiple cores. Detected {} cores, using {} threads",
logical_cores,
config.parallel
);
assert!(
config.parallel <= logical_cores + 1,
"Should not exceed logical cores + 1. Got {} for {} cores",
config.parallel,
logical_cores
);
}
}
#[tokio::test]
#[ignore = "RED test - shell script analysis not yet implemented"]
async fn test_shell_script_analysis() {
let temp_dir = TempDir::new().unwrap();
let shell_script = temp_dir.path().join("script.sh");
fs::write(
&shell_script,
r#"#!/bin/bash
set -euo pipefail
# Function definition
process_files() {
local dir="$1"
# Pipeline with complexity
find "$dir" -name "*.log" | grep -v error | head -10 | while read -r file; do
# Subshell complexity
(
wc -l "$file"
if [ -f "$file.backup" ]; then
echo "Backup exists"
fi
)
done
}
main() {
process_files "/var/log"
}
main "$@"
"#,
)
.unwrap();
let output_file = temp_dir.path().join("context.md");
let result = crate::cli::handlers::utility_handlers::handle_context(
None, temp_dir.path().to_path_buf(),
Some(output_file.clone()),
crate::cli::ContextFormat::Markdown,
false,
false,
None,
None,
)
.await;
assert!(result.is_ok());
let output = fs::read_to_string(output_file).unwrap();
assert!(
output.contains("script.sh"),
"Shell script should be included in analysis"
);
assert!(
output.contains("process_files") || output.contains("main"),
"Shell functions should be detected"
);
let script_section = extract_file_section(&output, "script.sh");
if let Some(section) = script_section {
assert!(
section.contains("[complexity:")
|| section.contains("Functions:")
|| section.contains("process_files")
|| section.contains("main"),
"Shell script should have complexity analysis or function detection. Section: {}",
section
);
}
}
fn extract_percentage(line: &str) -> Option<f64> {
if let Some(percent_pos) = line.find('%') {
let before_percent = &line[..percent_pos];
if let Some(colon_pos) = before_percent.rfind(':') {
let value_str = before_percent[colon_pos + 1..].trim();
return value_str.parse::<f64>().ok();
}
if let Some(space_pos) = before_percent.rfind(' ') {
let value_str = before_percent[space_pos + 1..].trim();
return value_str.parse::<f64>().ok();
}
}
None
}
fn extract_file_section(output: &str, filename: &str) -> Option<String> {
let lines: Vec<&str> = output.lines().collect();
let mut start_idx = None;
for (i, line) in lines.iter().enumerate() {
if line.contains(filename) && (line.starts_with("### ") || line.starts_with("##")) {
start_idx = Some(i);
break;
}
}
if let Some(start) = start_idx {
let mut end_idx = lines.len();
for i in (start + 1)..lines.len() {
if lines[i].starts_with("### ") && !lines[i].contains(filename) {
end_idx = i;
break;
}
}
let section_lines = &lines[start..end_idx];
return Some(section_lines.join("\n"));
}
None
}