use crate::priority::UnifiedDebtItem;
fn estimate_risk_reduction(coverage: f64) -> f64 {
(1.0 - coverage) * 0.3
}
pub fn collect_testing_gaps(items: &[UnifiedDebtItem]) -> Vec<&UnifiedDebtItem> {
items
.iter()
.filter(|item| matches!(item.debt_type, crate::priority::DebtType::TestingGap { .. }))
.take(10)
.collect()
}
fn format_testing_table_header() -> String {
"### ROI-Based Testing Priorities\n\n\
| Function | ROI | Complexity | Coverage | Risk Reduction |\n\
|----------|-----|------------|----------|----------------|\n"
.to_string()
}
fn format_testing_gap_row(item: &UnifiedDebtItem) -> Option<String> {
if let crate::priority::DebtType::TestingGap {
coverage,
cyclomatic,
cognitive: _,
} = &item.debt_type
{
let risk_reduction = estimate_risk_reduction(*coverage);
Some(format!(
"| `{}` | {:.1} | {} | {:.0}% | {:.0}% |\n",
item.location.function,
0.0,
cyclomatic,
coverage * 100.0,
risk_reduction * 100.0
))
} else {
None
}
}
pub fn format_testing_recommendations(testing_gaps: &[&UnifiedDebtItem]) -> String {
if testing_gaps.is_empty() {
return "_All critical functions have adequate test coverage._\n\n".to_string();
}
let mut output = format_testing_table_header();
for item in testing_gaps {
if let Some(row) = format_testing_gap_row(item) {
output.push_str(&row);
}
}
output.push('\n');
output
}
#[cfg(test)]
mod tests {
use super::*;
use crate::priority::unified_scorer::{Location, UnifiedScore};
use crate::priority::{ActionableRecommendation, DebtType, FunctionRole, ImpactMetrics};
fn create_testing_gap_item(
function_name: &str,
coverage: f64,
cyclomatic: u32,
) -> UnifiedDebtItem {
UnifiedDebtItem {
location: Location {
file: std::path::PathBuf::from("test.rs"),
line: 10,
function: function_name.to_string(),
},
unified_score: UnifiedScore {
final_score: 8.0,
complexity_factor: 0.8,
coverage_factor: 0.6,
dependency_factor: 0.5,
role_multiplier: 1.0,
base_score: None,
exponential_factor: None,
risk_boost: None,
pre_adjustment_score: None,
adjustment_applied: None,
purity_factor: None,
refactorability_factor: None,
pattern_factor: None,
debt_adjustment: None,
pre_normalization_score: None,
structural_multiplier: Some(1.0),
has_coverage_data: false,
contextual_risk_multiplier: None,
pre_contextual_score: None,
debt_type_multiplier: None,
},
debt_type: DebtType::TestingGap {
coverage,
cyclomatic,
cognitive: 20,
},
function_role: FunctionRole::PureLogic,
recommendation: ActionableRecommendation {
primary_action: "Add unit tests".to_string(),
rationale: "Increase test coverage".to_string(),
implementation_steps: vec![],
related_items: vec![],
steps: None,
estimated_effort_hours: None,
},
expected_impact: ImpactMetrics {
complexity_reduction: 0.0,
coverage_improvement: 0.5,
lines_reduction: 0,
risk_reduction: 0.3,
},
transitive_coverage: None,
file_context: None,
upstream_dependencies: 1,
downstream_dependencies: 2,
upstream_callers: vec![],
downstream_callees: vec![],
upstream_production_callers: vec![],
upstream_test_callers: vec![],
production_blast_radius: 0,
nesting_depth: 0,
function_length: 50,
cyclomatic_complexity: cyclomatic,
cognitive_complexity: 20,
is_pure: None,
purity_confidence: None,
purity_level: None,
god_object_indicators: None,
tier: None,
function_context: None,
context_confidence: None,
contextual_recommendation: None,
pattern_analysis: None,
context_multiplier: None,
context_type: None,
language_specific: None, detected_pattern: None,
contextual_risk: None, file_line_count: None,
responsibility_category: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
context_suggestion: None,
}
}
fn create_test_item(function_name: &str, final_score: f64) -> UnifiedDebtItem {
UnifiedDebtItem {
location: Location {
file: std::path::PathBuf::from("test.rs"),
line: 10,
function: function_name.to_string(),
},
unified_score: UnifiedScore {
final_score: final_score.max(0.0),
complexity_factor: 0.8,
coverage_factor: 0.6,
dependency_factor: 0.5,
role_multiplier: 1.0,
base_score: None,
exponential_factor: None,
risk_boost: None,
pre_adjustment_score: None,
adjustment_applied: None,
purity_factor: None,
refactorability_factor: None,
pattern_factor: None,
debt_adjustment: None,
pre_normalization_score: None,
structural_multiplier: Some(1.0),
has_coverage_data: false,
contextual_risk_multiplier: None,
pre_contextual_score: None,
debt_type_multiplier: None,
},
debt_type: DebtType::ComplexityHotspot {
cyclomatic: 15,
cognitive: 20,
},
function_role: FunctionRole::PureLogic,
recommendation: ActionableRecommendation {
primary_action: "Refactor to reduce complexity".to_string(),
rationale: "Test recommendation".to_string(),
implementation_steps: vec![],
related_items: vec![],
steps: None,
estimated_effort_hours: None,
},
expected_impact: ImpactMetrics {
complexity_reduction: 5.0,
coverage_improvement: 0.1,
lines_reduction: 10,
risk_reduction: 0.2,
},
transitive_coverage: None,
file_context: None,
upstream_dependencies: 1,
downstream_dependencies: 2,
upstream_callers: vec![],
downstream_callees: vec![],
upstream_production_callers: vec![],
upstream_test_callers: vec![],
production_blast_radius: 0,
nesting_depth: 0,
function_length: 50,
cyclomatic_complexity: 15,
cognitive_complexity: 20,
is_pure: None,
purity_confidence: None,
purity_level: None,
god_object_indicators: None,
tier: None,
function_context: None,
context_confidence: None,
contextual_recommendation: None,
pattern_analysis: None,
context_multiplier: None,
context_type: None,
language_specific: None, detected_pattern: None,
contextual_risk: None, file_line_count: None,
responsibility_category: None,
error_swallowing_count: None,
error_swallowing_patterns: None,
entropy_analysis: None,
context_suggestion: None,
}
}
#[test]
fn test_collect_testing_gaps() {
let items = vec![
create_testing_gap_item("func1", 0.2, 10),
create_test_item("func2", 7.0), create_testing_gap_item("func3", 0.4, 15),
create_testing_gap_item("func4", 0.1, 20),
];
let gaps = collect_testing_gaps(&items);
assert_eq!(gaps.len(), 3);
assert_eq!(gaps[0].location.function, "func1");
assert_eq!(gaps[1].location.function, "func3");
assert_eq!(gaps[2].location.function, "func4");
}
#[test]
fn test_collect_testing_gaps_empty() {
let items = vec![
create_test_item("func1", 7.0),
create_test_item("func2", 8.0),
];
let gaps = collect_testing_gaps(&items);
assert!(gaps.is_empty());
}
#[test]
fn test_collect_testing_gaps_limits_to_ten() {
let mut items = Vec::new();
for i in 0..15 {
items.push(create_testing_gap_item(&format!("func{}", i), 0.2, 10));
}
let gaps = collect_testing_gaps(&items);
assert_eq!(gaps.len(), 10);
}
#[test]
fn test_format_testing_table_header() {
let header = format_testing_table_header();
assert!(header.contains("### ROI-Based Testing Priorities"));
assert!(header.contains("| Function | ROI | Complexity | Coverage | Risk Reduction |"));
assert!(header.contains("|----------|-----|------------|----------|----------------|"));
}
#[test]
fn test_format_testing_gap_row() {
let item = create_testing_gap_item("test_function", 0.3, 12);
let row = format_testing_gap_row(&item).unwrap();
assert!(row.contains("`test_function`"));
assert!(row.contains("| 0.0 |")); assert!(row.contains("| 12 |")); assert!(row.contains("| 30% |")); assert!(row.contains("| 21% |")); }
#[test]
fn test_format_testing_gap_row_non_testing_gap() {
let item = create_test_item("test_function", 8.0);
let row = format_testing_gap_row(&item);
assert!(row.is_none());
}
#[test]
fn test_format_testing_recommendations_empty() {
let gaps: Vec<&UnifiedDebtItem> = vec![];
let result = format_testing_recommendations(&gaps);
assert!(result.contains("_All critical functions have adequate test coverage._"));
}
#[test]
fn test_format_testing_recommendations_with_gaps() {
let items = vec![
create_testing_gap_item("func1", 0.2, 10),
create_testing_gap_item("func2", 0.5, 15),
];
let gaps: Vec<&UnifiedDebtItem> = items.iter().collect();
let result = format_testing_recommendations(&gaps);
assert!(result.contains("### ROI-Based Testing Priorities"));
assert!(result.contains("`func1`"));
assert!(result.contains("`func2`"));
assert!(result.contains("| 10 |")); assert!(result.contains("| 15 |")); }
#[test]
fn test_estimate_risk_reduction() {
assert_eq!(estimate_risk_reduction(0.0), 0.3); assert_eq!(estimate_risk_reduction(0.5), 0.15); assert_eq!(estimate_risk_reduction(1.0), 0.0); }
}