use debtmap::analysis::io_detection::Language;
use debtmap::analysis::multi_signal_aggregation::{
AggregationConfig, ResponsibilityAggregator, ResponsibilityCategory, SignalSet,
};
#[test]
fn test_end_to_end_file_io_classification() {
let code = r#"
fn read_user_profile(user_id: &str) -> Result<Profile> {
let path = format!("profiles/{}.json", user_id);
let contents = fs::read_to_string(&path)?;
serde_json::from_str(&contents)
}
"#;
let aggregator = ResponsibilityAggregator::new();
let signals = SignalSet {
io_signal: aggregator.collect_io_signal(code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("read_user_profile")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert_eq!(result.primary, ResponsibilityCategory::FileIO);
assert!(
result.confidence > 0.30,
"Expected confidence > 0.30, got {}",
result.confidence
);
assert!(!result.evidence.is_empty());
}
#[test]
fn test_end_to_end_pure_computation() {
let code = r#"
fn calculate_fibonacci(n: u64) -> u64 {
if n <= 1 {
n
} else {
calculate_fibonacci(n - 1) + calculate_fibonacci(n - 2)
}
}
"#;
let aggregator = ResponsibilityAggregator::new();
let signals = SignalSet {
purity_signal: aggregator.collect_purity_signal(code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("calculate_fibonacci")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert_eq!(result.primary, ResponsibilityCategory::PureComputation);
assert!(
result.confidence > 0.10,
"Expected confidence > 0.10, got {}",
result.confidence
);
}
#[test]
fn test_end_to_end_http_handler() {
let code = r#"
async fn handle_create_user(
Json(payload): Json<CreateUser>,
State(db): State<Database>,
) -> Result<Json<User>, StatusCode> {
let user = db.create_user(payload).await?;
Ok(Json(user))
}
"#;
let aggregator = ResponsibilityAggregator::new();
let signals = SignalSet {
io_signal: aggregator.collect_io_signal(code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("handle_create_user")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert!(
result.primary == ResponsibilityCategory::HttpRequestHandler
|| result.primary == ResponsibilityCategory::DatabaseIO
|| result.primary == ResponsibilityCategory::Orchestration
|| result.primary == ResponsibilityCategory::Unknown,
"Expected HTTP Handler, Database I/O, Orchestration, or Unknown (for minimal context), got {:?}",
result.primary
);
}
#[test]
fn test_weighted_aggregation_prefers_io() {
let code = r#"
fn process_transactions(file_path: &Path) -> Result<Summary> {
let transactions = read_from_csv(file_path)?;
let total = transactions.iter().map(|t| t.amount).sum();
Summary { total }
}
"#;
let aggregator = ResponsibilityAggregator::new();
let signals = SignalSet {
io_signal: aggregator.collect_io_signal(code, Language::Rust),
purity_signal: aggregator.collect_purity_signal(code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("process_transactions")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert!(
result.primary == ResponsibilityCategory::FileIO
|| result.primary == ResponsibilityCategory::Orchestration
);
}
#[test]
fn test_evidence_collection() {
let code = r#"
fn validate_email_format(email: &str) -> bool {
let re = Regex::new(r"^[^@]+@[^@]+\.[^@]+$").unwrap();
re.is_match(email)
}
"#;
let aggregator = ResponsibilityAggregator::new();
let signals = SignalSet {
purity_signal: aggregator.collect_purity_signal(code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("validate_email_format")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert!(
result.primary == ResponsibilityCategory::Validation
|| result.primary == ResponsibilityCategory::Formatting,
"Expected Validation or Formatting, got {:?}",
result.primary
);
assert!(!result.evidence.is_empty(), "Evidence should not be empty");
let has_name_evidence = result
.evidence
.iter()
.any(|e| e.description.contains("Name pattern"));
assert!(
has_name_evidence,
"Expected name-based evidence in {:?}",
result.evidence
);
}
#[test]
fn test_alternative_classifications() {
let code = r#"
fn transform_user_data(user: User) -> UserDTO {
UserDTO {
id: user.id,
name: format!("{} {}", user.first, user.last),
}
}
"#;
let aggregator = ResponsibilityAggregator::new();
let signals = SignalSet {
purity_signal: aggregator.collect_purity_signal(code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("transform_user_data")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert_eq!(result.primary, ResponsibilityCategory::Transformation);
assert!(!result.alternatives.is_empty());
}
#[test]
fn test_custom_config() {
let code = r#"
fn calculate_discount(price: f64, rate: f64) -> f64 {
price * (1.0 - rate)
}
"#;
let mut config = AggregationConfig::default();
config.weights.name_heuristics = 0.30;
config.weights.purity_side_effects = 0.20;
config.weights.io_detection = 0.30;
config.weights.call_graph = 0.10;
config.weights.type_signatures = 0.05;
config.weights.framework_patterns = 0.05;
let aggregator = ResponsibilityAggregator::with_config(config);
let signals = SignalSet {
purity_signal: aggregator.collect_purity_signal(code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("calculate_discount")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert_eq!(result.primary, ResponsibilityCategory::PureComputation);
}
#[test]
fn test_multi_language_support() {
let python_code = r#"
def fetch_user_data(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
return response.json()
"#;
let aggregator = ResponsibilityAggregator::new();
let signals = SignalSet {
io_signal: aggregator.collect_io_signal(python_code, Language::Python),
name_signal: Some(aggregator.collect_name_signal("fetch_user_data")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert_eq!(result.primary, ResponsibilityCategory::NetworkIO);
let rust_code = r#"
fn fetch_user_data(user_id: u64) -> Result<UserData> {
let url = format!("https://api.example.com/users/{}", user_id);
reqwest::get(&url)?.json()
}
"#;
let signals = SignalSet {
io_signal: aggregator.collect_io_signal(rust_code, Language::Rust),
name_signal: Some(aggregator.collect_name_signal("fetch_user_data")),
..Default::default()
};
let result = aggregator.aggregate(&signals);
assert_eq!(result.primary, ResponsibilityCategory::NetworkIO);
}