use crate::organization::GodObjectAnalysis;
use crate::priority::architecture_recognition::calculate_instability;
use crate::priority::call_graph::CallGraph;
use crate::priority::caller_classification::{classify_callers, ClassifiedCallers};
use crate::priority::file_metrics::FileDebtMetrics;
use crate::priority::god_object_aggregation::GodObjectAggregatedMetrics;
use crate::priority::{
ActionableRecommendation, DebtType, FunctionRole, ImpactMetrics, TransitiveCoverage,
UnifiedDebtItem, UnifiedScore,
};
use crate::risk::context::ContextualRisk;
use crate::risk::lcov::LcovData;
use std::path::Path;
pub fn create_god_object_debt_item(
file_path: &Path,
file_metrics: &FileDebtMetrics,
god_analysis: &GodObjectAnalysis,
mut aggregated_metrics: GodObjectAggregatedMetrics,
coverage_data: Option<&LcovData>,
call_graph: Option<&CallGraph>,
) -> UnifiedDebtItem {
if aggregated_metrics.weighted_coverage.is_none() {
if let Some(coverage) = coverage_data {
if let Some(file_coverage) = coverage.get_file_coverage(file_path) {
aggregated_metrics.weighted_coverage = Some(TransitiveCoverage {
direct: file_coverage,
transitive: 0.0,
propagated_from: vec![],
uncovered_lines: vec![],
});
}
}
}
let classified_callers =
classify_god_object_callers(&aggregated_metrics.unique_upstream_callers, call_graph);
let coupling_classification = crate::output::unified::classify_coupling_pattern(
calculate_instability(
aggregated_metrics.upstream_dependencies,
aggregated_metrics.downstream_dependencies,
),
classified_callers.production_count,
classified_callers.test_count,
aggregated_metrics.downstream_dependencies,
);
let mut unified_score = calculate_god_object_score(god_analysis, &aggregated_metrics);
let score_multiplier = coupling_classification.score_multiplier();
if score_multiplier < 1.0 {
unified_score.final_score *= score_multiplier;
}
let debt_type = create_god_object_debt_type(god_analysis);
let (display_name, line_number) = determine_display_info(file_path, god_analysis);
let expected_impact = calculate_god_object_impact(god_analysis, file_metrics);
let recommendation = create_god_object_recommendation(god_analysis);
let dampened_score = unified_score.final_score;
let tier = if dampened_score >= 50.0 {
crate::priority::RecommendationTier::T1CriticalArchitecture
} else {
crate::priority::RecommendationTier::T2ComplexUntested
};
let function_role = classify_god_object_role(god_analysis);
let production_blast_radius =
classified_callers.production_count + aggregated_metrics.downstream_dependencies;
UnifiedDebtItem {
location: crate::priority::unified_scorer::Location {
file: file_path.to_path_buf(),
function: display_name,
line: line_number,
},
debt_type,
unified_score,
function_role,
recommendation,
expected_impact,
transitive_coverage: aggregated_metrics.weighted_coverage,
upstream_dependencies: aggregated_metrics.upstream_dependencies,
downstream_dependencies: aggregated_metrics.downstream_dependencies,
upstream_callers: aggregated_metrics.unique_upstream_callers.clone(),
downstream_callees: aggregated_metrics.unique_downstream_callees.clone(),
upstream_production_callers: classified_callers.production,
upstream_test_callers: classified_callers.test,
production_blast_radius,
nesting_depth: aggregated_metrics.max_nesting_depth,
function_length: god_analysis.lines_of_code,
cyclomatic_complexity: aggregated_metrics.total_cyclomatic,
cognitive_complexity: aggregated_metrics.total_cognitive,
entropy_analysis: aggregated_metrics.aggregated_entropy.clone(),
is_pure: None,
purity_confidence: None,
purity_level: None,
god_object_indicators: Some(god_analysis.clone()),
tier: Some(tier),
function_context: None,
context_confidence: None,
contextual_recommendation: None,
pattern_analysis: None,
file_context: None,
context_multiplier: None,
context_type: None,
language_specific: None,
detected_pattern: None,
contextual_risk: aggregated_metrics.aggregated_contextual_risk,
file_line_count: Some(file_metrics.total_lines),
responsibility_category: god_analysis.responsibilities.first().cloned(),
error_swallowing_count: None,
error_swallowing_patterns: None,
context_suggestion: None,
}
}
fn calculate_god_object_score(
god_analysis: &GodObjectAnalysis,
aggregated_metrics: &GodObjectAggregatedMetrics,
) -> UnifiedScore {
let coverage_factor = aggregated_metrics
.weighted_coverage
.as_ref()
.map(|cov| (1.0 - cov.direct) * 10.0)
.unwrap_or(0.0);
let total_complexity = aggregated_metrics.total_cyclomatic + aggregated_metrics.total_cognitive;
let complexity_factor = total_complexity as f64 / 10.0;
let dependency_factor = calculate_god_object_risk(god_analysis) / 10.0;
let base_score = complexity_factor + coverage_factor + dependency_factor;
let has_coverage_data = aggregated_metrics.weighted_coverage.is_some();
let mut unified_score = UnifiedScore {
final_score: base_score.max(0.0),
complexity_factor,
coverage_factor,
dependency_factor,
role_multiplier: 1.0,
base_score: Some(base_score),
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,
contextual_risk_multiplier: None,
pre_contextual_score: None,
debt_type_multiplier: None,
};
if let Some(ref ctx_risk) = aggregated_metrics.aggregated_contextual_risk {
unified_score = crate::priority::scoring::construction::apply_contextual_risk_to_score(
unified_score,
ctx_risk,
);
}
unified_score
}
fn create_god_object_debt_type(god_analysis: &GodObjectAnalysis) -> DebtType {
DebtType::GodObject {
methods: god_analysis.method_count as u32,
fields: match god_analysis.detection_type {
crate::organization::DetectionType::GodClass => Some(god_analysis.field_count as u32),
crate::organization::DetectionType::GodFile
| crate::organization::DetectionType::GodModule => None,
},
responsibilities: god_analysis.responsibility_count as u32,
god_object_score: god_analysis.god_object_score,
lines: god_analysis.lines_of_code as u32,
}
}
fn classify_god_object_role(god_analysis: &GodObjectAnalysis) -> FunctionRole {
if let Some(ref purity) = god_analysis.purity_distribution {
let total_methods = purity.pure_count + purity.probably_pure_count + purity.impure_count;
if total_methods > 0 {
let io_ratio = purity.impure_count as f64 / total_methods as f64;
if io_ratio > 0.4 {
return FunctionRole::IOWrapper;
}
}
}
if let Some(ref trait_summary) = god_analysis.trait_method_summary {
if trait_summary.mandated_ratio() > 0.5 {
return FunctionRole::PureLogic;
}
}
if let Some(weighted) = god_analysis.weighted_method_count {
let raw = god_analysis.method_count as f64;
if raw > 0.0 && weighted / raw < 0.4 {
return FunctionRole::PureLogic;
}
}
FunctionRole::PureLogic
}
#[allow(dead_code)]
pub fn calculate_role_confidence(god_analysis: &GodObjectAnalysis) -> f64 {
if let Some(ref purity) = god_analysis.purity_distribution {
let total_methods = purity.pure_count + purity.probably_pure_count + purity.impure_count;
if total_methods > 0 {
let io_ratio = purity.impure_count as f64 / total_methods as f64;
if io_ratio > 0.4 {
return 0.60 + ((io_ratio - 0.4) / 0.6) * 0.35;
}
}
}
if let Some(ref trait_summary) = god_analysis.trait_method_summary {
let mandated_ratio = trait_summary.mandated_ratio();
if mandated_ratio > 0.5 {
return 0.70 + ((mandated_ratio - 0.5) / 0.5) * 0.25;
}
}
if let Some(weighted) = god_analysis.weighted_method_count {
let raw = god_analysis.method_count as f64;
if raw > 0.0 {
let purity_ratio = weighted / raw;
if purity_ratio < 0.4 {
return 0.75 + ((0.4 - purity_ratio) / 0.4) * 0.20;
}
}
}
let method_score = (god_analysis.method_count as f64 / 20.0).min(1.0);
let responsibility_score = (god_analysis.responsibility_count as f64 / 5.0).min(1.0);
let size_score = (method_score + responsibility_score) / 2.0;
0.65 + size_score * 0.10
}
fn determine_display_info(file_path: &Path, god_analysis: &GodObjectAnalysis) -> (String, usize) {
match god_analysis.detection_type {
crate::organization::DetectionType::GodClass => {
let name = god_analysis.struct_name.as_deref().unwrap_or_else(|| {
file_path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("unknown")
});
let line = god_analysis.struct_line.unwrap_or(1);
(name.to_string(), line)
}
crate::organization::DetectionType::GodFile
| crate::organization::DetectionType::GodModule => {
("[file-scope]".to_string(), 1)
}
}
}
#[allow(dead_code)]
pub fn is_valid_function_name(function_name: &str) -> bool {
let filename_patterns = [".rs", ".py", ".js", ".ts", ".go", ".java", ".cpp", ".c"];
let is_filename = filename_patterns
.iter()
.any(|ext| function_name.ends_with(ext));
let is_placeholder = function_name == "[file-scope]";
!is_filename || is_placeholder
}
fn calculate_god_object_impact(
god_analysis: &GodObjectAnalysis,
file_metrics: &FileDebtMetrics,
) -> ImpactMetrics {
ImpactMetrics {
coverage_improvement: 0.0,
lines_reduction: god_analysis.lines_of_code as u32
/ god_analysis.recommended_splits.len().max(1) as u32,
complexity_reduction: file_metrics.total_complexity as f64
/ god_analysis.recommended_splits.len().max(1) as f64,
risk_reduction: calculate_god_object_risk(god_analysis),
}
}
pub fn calculate_god_object_risk(god_analysis: &GodObjectAnalysis) -> f64 {
let responsibility_risk = god_analysis.responsibility_count as f64 * 10.0;
let method_risk = (god_analysis.method_count as f64 / 10.0).min(50.0);
(responsibility_risk + method_risk).min(100.0)
}
pub fn create_god_object_recommendation(
god_analysis: &GodObjectAnalysis,
) -> ActionableRecommendation {
let role = classify_god_object_role(god_analysis);
create_god_object_recommendation_with_role(god_analysis, role)
}
fn create_god_object_recommendation_with_role(
god_analysis: &GodObjectAnalysis,
role: FunctionRole,
) -> ActionableRecommendation {
let split_count = if god_analysis.recommended_splits.len() >= 2 {
god_analysis.recommended_splits.len()
} else {
god_analysis.responsibility_count.clamp(2, 5)
};
let is_pure_utility_module = god_analysis
.weighted_method_count
.map(|w| w / (god_analysis.method_count as f64) < 0.4)
.unwrap_or(false);
let primary_action = match role {
FunctionRole::Orchestrator => format!(
"Extract {} sub-orchestrators to reduce coordination complexity",
split_count
),
FunctionRole::IOWrapper => format!(
"Separate {} I/O handlers from business logic",
god_analysis.responsibility_count
),
FunctionRole::PureLogic if is_pure_utility_module => {
format!(
"Review {} detected responsibilities - consider grouping related helpers into submodules",
god_analysis.responsibility_count
)
}
_ => format!("Split into {} modules by responsibility", split_count),
};
let rationale = match role {
FunctionRole::Orchestrator => format!(
"High coordination complexity: {} responsibilities with {} methods - \
extracting sub-orchestrators will reduce cognitive load and improve testability",
god_analysis.responsibility_count, god_analysis.method_count
),
FunctionRole::IOWrapper => format!(
"Mixed I/O concerns: {} responsibilities detected - \
separating I/O from pure logic enables better testing and reduces coupling",
god_analysis.responsibility_count
),
FunctionRole::PureLogic if is_pure_utility_module => {
let weighted = god_analysis.weighted_method_count.unwrap_or(0.0);
format!(
"Utility module with {} pure helper functions (weighted: {:.0}). \
High function count but low effective complexity - \
verify if detected responsibilities represent distinct concerns",
god_analysis.method_count, weighted
)
}
_ => format!(
"{} responsibilities detected with {} methods/functions - \
splitting will improve maintainability and enable focused testing",
god_analysis.responsibility_count, god_analysis.method_count
),
};
ActionableRecommendation {
primary_action,
rationale,
implementation_steps: Vec::new(),
related_items: Vec::new(),
steps: None,
estimated_effort_hours: None,
}
}
pub fn enrich_god_analysis_with_aggregates(
god_analysis: &GodObjectAnalysis,
aggregated_metrics: &GodObjectAggregatedMetrics,
) -> GodObjectAnalysis {
let mut enriched = god_analysis.clone();
enriched.aggregated_entropy = aggregated_metrics.aggregated_entropy.clone();
enriched.aggregated_error_swallowing_count =
if aggregated_metrics.total_error_swallowing_count > 0 {
Some(aggregated_metrics.total_error_swallowing_count)
} else {
None
};
enriched.aggregated_error_swallowing_patterns =
if !aggregated_metrics.error_swallowing_patterns.is_empty() {
Some(aggregated_metrics.error_swallowing_patterns.clone())
} else {
None
};
enriched
}
pub fn analyze_file_git_context(
file_path: &std::path::Path,
risk_analyzer: &crate::risk::RiskAnalyzer,
project_root: &std::path::Path,
) -> Option<ContextualRisk> {
if !risk_analyzer.has_context() {
return None;
}
let base_risk = 40.0;
risk_analyzer.analyze_file_context(
file_path.to_path_buf(),
base_risk,
project_root.to_path_buf(),
)
}
fn classify_god_object_callers(
upstream_callers: &[String],
call_graph: Option<&CallGraph>,
) -> ClassifiedCallers {
classify_callers(upstream_callers.iter(), call_graph)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::organization::god_object::ModuleSplit;
use crate::organization::{DetectionType, GodObjectConfidence, SplitAnalysisMethod};
use std::collections::HashMap;
fn create_test_god_analysis() -> GodObjectAnalysis {
GodObjectAnalysis {
is_god_object: true,
method_count: 50,
weighted_method_count: None,
field_count: 10,
responsibility_count: 5,
lines_of_code: 2000,
complexity_sum: 100,
god_object_score: 75.0,
recommended_splits: vec![
ModuleSplit {
suggested_name: "module_a".to_string(),
responsibility: "data".to_string(),
estimated_lines: 1000,
method_count: 25,
..Default::default()
},
ModuleSplit {
suggested_name: "module_b".to_string(),
responsibility: "io".to_string(),
estimated_lines: 1000,
method_count: 25,
..Default::default()
},
],
confidence: GodObjectConfidence::Probable,
responsibilities: vec!["data".to_string(), "io".to_string()],
responsibility_method_counts: HashMap::new(),
purity_distribution: None,
module_structure: None,
detection_type: DetectionType::GodFile,
struct_name: None,
struct_line: None,
struct_location: None,
visibility_breakdown: None,
domain_count: 2,
domain_diversity: 0.5,
struct_ratio: 0.0,
analysis_method: SplitAnalysisMethod::None,
cross_domain_severity: None,
domain_diversity_metrics: None,
aggregated_entropy: None,
aggregated_error_swallowing_count: None,
aggregated_error_swallowing_patterns: None,
layering_impact: None,
anti_pattern_report: None,
complexity_metrics: None, trait_method_summary: None, }
}
#[test]
fn test_calculate_god_object_risk() {
let analysis = create_test_god_analysis();
let risk = calculate_god_object_risk(&analysis);
assert!(risk > 0.0);
assert!(risk <= 100.0);
}
fn create_test_aggregated_metrics(
total_cyclomatic: u32,
total_cognitive: u32,
) -> GodObjectAggregatedMetrics {
GodObjectAggregatedMetrics {
total_cyclomatic,
total_cognitive,
max_nesting_depth: 3,
weighted_coverage: None,
unique_upstream_callers: Vec::new(),
unique_downstream_callees: Vec::new(),
upstream_dependencies: 0,
downstream_dependencies: 0,
aggregated_contextual_risk: None,
total_error_swallowing_count: 0,
error_swallowing_patterns: Vec::new(),
aggregated_entropy: None,
distribution_metrics: None,
}
}
#[test]
fn test_god_object_score_uses_composite_priority_not_raw_detector_score() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 39;
analysis.responsibility_count = 6;
analysis.god_object_score = 2_350.75;
let metrics = create_test_aggregated_metrics(168, 264);
let score = calculate_god_object_score(&analysis, &metrics);
let expected_dependency = calculate_god_object_risk(&analysis) / 10.0;
let expected_base = 43.2 + expected_dependency;
assert!((score.base_score.unwrap() - expected_base).abs() < 0.01);
assert!((score.final_score - expected_base).abs() < 0.01);
assert!(
score.final_score < 100.0,
"raw detector score should not become final priority score: {}",
score.final_score
);
}
#[test]
fn test_god_object_contextual_risk_stays_in_priority_scale() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 39;
analysis.responsibility_count = 6;
analysis.god_object_score = 2_350.75;
let mut metrics = create_test_aggregated_metrics(168, 264);
metrics.aggregated_contextual_risk = Some(ContextualRisk {
base_risk: 40.0,
contextual_risk: 79.6,
contexts: Vec::new(),
explanation: "test risk".to_string(),
});
let score = calculate_god_object_score(&analysis, &metrics);
assert!((score.contextual_risk_multiplier.unwrap() - 1.99).abs() < 0.01);
assert!(score.final_score > 90.0);
assert!(
score.final_score < 200.0,
"contextual risk should amplify the composite score, not the raw detector score: {}",
score.final_score
);
}
#[test]
fn test_god_object_item_uses_file_total_lines_for_context_bounds() {
let mut analysis = create_test_god_analysis();
analysis.lines_of_code = 1750;
let file_metrics = FileDebtMetrics {
total_lines: 1874,
..Default::default()
};
let aggregated_metrics = create_test_aggregated_metrics(157, 106);
let item = create_god_object_debt_item(
Path::new("src/builders/parallel_unified_analysis.rs"),
&file_metrics,
&analysis,
aggregated_metrics,
None,
None,
);
assert_eq!(item.function_length, 1750);
assert_eq!(
item.file_line_count,
Some(1874),
"Context bounds must use source file lines, not production LOC"
);
}
#[test]
fn test_create_god_object_recommendation() {
let analysis = create_test_god_analysis();
let rec = create_god_object_recommendation(&analysis);
assert!(
rec.primary_action.contains("Split into")
|| rec.primary_action.contains("modules by responsibility"),
"Expected pure logic recommendation (split by responsibility), got: {}",
rec.primary_action
);
assert!(rec.rationale.contains("responsibilities"));
}
#[test]
fn test_determine_display_info_god_file() {
let analysis = create_test_god_analysis();
let file_path = std::path::PathBuf::from("/path/to/large_file.rs");
let (name, line) = determine_display_info(&file_path, &analysis);
assert_eq!(name, "[file-scope]");
assert_eq!(line, 1);
}
#[test]
fn test_is_valid_function_name() {
assert!(is_valid_function_name("[file-scope]"));
assert!(is_valid_function_name("process_data"));
assert!(is_valid_function_name("MyStruct"));
assert!(is_valid_function_name("calculate_risk"));
assert!(!is_valid_function_name("large_file.rs"));
assert!(!is_valid_function_name("module.py"));
assert!(!is_valid_function_name("handler.ts"));
}
#[test]
fn test_determine_display_info_god_class() {
let mut analysis = create_test_god_analysis();
analysis.detection_type = DetectionType::GodClass;
analysis.struct_name = Some("MyLargeStruct".to_string());
analysis.struct_line = Some(42);
let file_path = std::path::PathBuf::from("/path/to/file.rs");
let (name, line) = determine_display_info(&file_path, &analysis);
assert_eq!(name, "MyLargeStruct");
assert_eq!(line, 42);
}
#[test]
fn test_classify_god_object_role_pure_logic_many_methods() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 20; analysis.responsibility_count = 2;
analysis.purity_distribution = None;
analysis.trait_method_summary = None;
let role = classify_god_object_role(&analysis);
assert_eq!(
role,
FunctionRole::PureLogic,
"God object with many methods should default to PureLogic (not Orchestrator)"
);
}
#[test]
fn test_classify_god_object_role_pure_logic_many_responsibilities() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 5;
analysis.responsibility_count = 10; analysis.purity_distribution = None;
analysis.trait_method_summary = None;
let role = classify_god_object_role(&analysis);
assert_eq!(
role,
FunctionRole::PureLogic,
"God object with many responsibilities should default to PureLogic (not Orchestrator)"
);
}
#[test]
fn test_classify_god_object_role_pure_logic_small() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 5; analysis.responsibility_count = 2; analysis.purity_distribution = None;
let role = classify_god_object_role(&analysis);
assert_eq!(
role,
FunctionRole::PureLogic,
"Small god object should be PureLogic"
);
}
#[test]
fn test_classify_god_object_role_pure_logic_trait_dominated() {
use crate::organization::god_object::TraitMethodSummary;
let mut analysis = create_test_god_analysis();
analysis.method_count = 30;
analysis.responsibility_count = 8;
analysis.purity_distribution = None;
analysis.trait_method_summary = Some(TraitMethodSummary {
mandated_count: 18,
by_trait: [("syn::Visit".into(), 18)].into_iter().collect(),
weighted_count: 13.8, extractable_count: 12,
total_methods: 30,
});
let role = classify_god_object_role(&analysis);
assert_eq!(
role,
FunctionRole::PureLogic,
"Trait-dominated struct (>50% trait-mandated) should be PureLogic"
);
}
#[test]
fn test_classify_god_object_role_io_wrapper_high_impure() {
use crate::organization::god_object::PurityDistribution;
let mut analysis = create_test_god_analysis();
analysis.method_count = 20;
analysis.responsibility_count = 4;
analysis.purity_distribution = Some(PurityDistribution {
pure_count: 2,
probably_pure_count: 2,
impure_count: 6, pure_weight_contribution: 0.2,
probably_pure_weight_contribution: 0.2,
impure_weight_contribution: 0.6,
});
let role = classify_god_object_role(&analysis);
assert_eq!(
role,
FunctionRole::IOWrapper,
"God object with high impure ratio should be IOWrapper"
);
}
#[test]
fn test_classify_god_object_role_pure_logic_low_impure() {
use crate::organization::god_object::PurityDistribution;
let mut analysis = create_test_god_analysis();
analysis.method_count = 20;
analysis.responsibility_count = 4;
analysis.trait_method_summary = None;
analysis.purity_distribution = Some(PurityDistribution {
pure_count: 6,
probably_pure_count: 2,
impure_count: 2, pure_weight_contribution: 0.6,
probably_pure_weight_contribution: 0.2,
impure_weight_contribution: 0.2,
});
let role = classify_god_object_role(&analysis);
assert_eq!(
role,
FunctionRole::PureLogic,
"God object with low impure ratio should be PureLogic (not IOWrapper, not Orchestrator)"
);
}
#[test]
fn test_recommendation_orchestrator_role() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 20;
analysis.responsibility_count = 4;
let rec = create_god_object_recommendation_with_role(&analysis, FunctionRole::Orchestrator);
assert!(
rec.primary_action.contains("sub-orchestrators"),
"Orchestrator recommendation should mention sub-orchestrators"
);
assert!(
rec.rationale.contains("coordination complexity"),
"Orchestrator rationale should mention coordination"
);
}
#[test]
fn test_recommendation_io_wrapper_role() {
let mut analysis = create_test_god_analysis();
analysis.responsibility_count = 3;
let rec = create_god_object_recommendation_with_role(&analysis, FunctionRole::IOWrapper);
assert!(
rec.primary_action.contains("I/O handlers"),
"IOWrapper recommendation should mention I/O handlers"
);
assert!(
rec.rationale.contains("I/O concerns"),
"IOWrapper rationale should mention I/O concerns"
);
}
#[test]
fn test_recommendation_pure_logic_role() {
let mut analysis = create_test_god_analysis();
analysis.responsibility_count = 3;
let rec = create_god_object_recommendation_with_role(&analysis, FunctionRole::PureLogic);
assert!(
rec.primary_action.contains("Split into"),
"PureLogic recommendation should mention splitting"
);
assert!(
rec.rationale.contains("maintainability"),
"PureLogic rationale should mention maintainability"
);
}
#[test]
fn test_confidence_io_wrapper_high() {
use crate::organization::god_object::PurityDistribution;
let mut analysis = create_test_god_analysis();
analysis.purity_distribution = Some(PurityDistribution {
pure_count: 1,
probably_pure_count: 1,
impure_count: 8, pure_weight_contribution: 0.1,
probably_pure_weight_contribution: 0.1,
impure_weight_contribution: 0.8,
});
let confidence = calculate_role_confidence(&analysis);
assert!(
confidence >= 0.80,
"High impure ratio should give high confidence: {:.2}%",
confidence * 100.0
);
}
#[test]
fn test_confidence_io_wrapper_threshold() {
use crate::organization::god_object::PurityDistribution;
let mut analysis = create_test_god_analysis();
analysis.purity_distribution = Some(PurityDistribution {
pure_count: 3,
probably_pure_count: 2,
impure_count: 5, pure_weight_contribution: 0.3,
probably_pure_weight_contribution: 0.2,
impure_weight_contribution: 0.5,
});
let confidence = calculate_role_confidence(&analysis);
assert!(
(0.60..0.70).contains(&confidence),
"At-threshold impure ratio should give moderate confidence: {:.2}%",
confidence * 100.0
);
}
#[test]
fn test_confidence_pure_logic_large_struct() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 25;
analysis.responsibility_count = 5;
analysis.purity_distribution = None;
analysis.trait_method_summary = None;
let confidence = calculate_role_confidence(&analysis);
assert!(
(0.65..0.80).contains(&confidence),
"Large struct should have moderate-high confidence: {:.2}%",
confidence * 100.0
);
}
#[test]
fn test_confidence_pure_logic_moderate_struct() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 12;
analysis.responsibility_count = 3;
analysis.purity_distribution = None;
analysis.trait_method_summary = None;
let confidence = calculate_role_confidence(&analysis);
assert!(
(0.65..0.75).contains(&confidence),
"Moderate struct should have moderate confidence: {:.2}%",
confidence * 100.0
);
}
#[test]
fn test_confidence_pure_logic_small_struct() {
let mut analysis = create_test_god_analysis();
analysis.method_count = 5;
analysis.responsibility_count = 2;
analysis.purity_distribution = None;
analysis.trait_method_summary = None;
let confidence = calculate_role_confidence(&analysis);
assert!(
(0.65..0.70).contains(&confidence),
"Small struct should have moderate confidence: {:.2}%",
confidence * 100.0
);
}
#[test]
fn test_confidence_trait_dominated() {
use crate::organization::god_object::TraitMethodSummary;
let mut analysis = create_test_god_analysis();
analysis.method_count = 30;
analysis.purity_distribution = None;
analysis.trait_method_summary = Some(TraitMethodSummary {
mandated_count: 24,
by_trait: [("syn::Visit".into(), 24)].into_iter().collect(),
weighted_count: 8.4,
extractable_count: 6,
total_methods: 30,
});
let confidence = calculate_role_confidence(&analysis);
assert!(
confidence >= 0.85,
"Trait-dominated struct should have high confidence: {:.2}%",
confidence * 100.0
);
}
}