use debtmap::organization::{
FeatureEnvyDetector, GodObjectDetector, MagicValueDetector, OrganizationAntiPattern,
OrganizationDetector, ParameterAnalyzer, PrimitiveObsessionDetector,
};
#[test]
fn test_god_object_detection() {
let source = r#"
struct UserManager {
users: Vec<User>,
settings: Settings,
cache: Cache,
logger: Logger,
validator: Validator,
emailer: Emailer,
database: Database,
reporter: Reporter,
analyzer: Analyzer,
optimizer: Optimizer,
scheduler: Scheduler,
}
impl UserManager {
fn create_user(&self) {}
fn update_user(&self) {}
fn delete_user(&self) {}
fn validate_user(&self) {}
fn log_user_action(&self) {}
fn cache_user_data(&self) {}
fn send_notification(&self) {}
fn calculate_metrics(&self) {}
fn generate_report(&self) {}
fn backup_data(&self) {}
fn restore_data(&self) {}
fn authenticate_user(&self) {}
fn authorize_action(&self) {}
// debtmap:ignore - These are test function names, not actual crypto implementations
fn encrypt_data(&self) {}
fn decrypt_data(&self) {}
fn schedule_task(&self) {}
fn optimize_query(&self) {}
fn analyze_usage(&self) {}
fn export_data(&self) {}
fn import_data(&self) {}
}
"#;
let file = syn::parse_str::<syn::File>(source).unwrap();
let detector = GodObjectDetector::new();
let patterns = detector.detect_anti_patterns(&file);
assert!(!patterns.is_empty(), "Should detect god object");
if let Some(OrganizationAntiPattern::GodObject {
type_name,
method_count,
field_count,
..
}) = patterns.first()
{
assert_eq!(type_name, "UserManager");
assert!(*method_count > 15, "Should have many methods");
assert!(*field_count > 10, "Should have many fields");
} else {
panic!("Expected god object pattern");
}
}
#[test]
fn test_magic_number_detection() {
let source = r#"
fn process_data() {
let timeout = 5000;
let buffer_size = 8192;
let max_retries = 3;
if timeout > 5000 {
println!("Timeout exceeded");
}
for _ in 0..8192 {
// Process buffer
}
}
"#;
let file = syn::parse_str::<syn::File>(source).unwrap();
let detector = MagicValueDetector::new();
let patterns = detector.detect_anti_patterns(&file);
assert!(!patterns.is_empty(), "Should detect magic numbers");
let has_5000 = patterns.iter().any(|p| {
if let OrganizationAntiPattern::MagicValue {
value,
occurrence_count,
..
} = p
{
value == "5000" && *occurrence_count >= 2
} else {
false
}
});
assert!(has_5000, "Should detect repeated magic number 5000");
}
#[test]
fn test_long_parameter_list_detection() {
let source = r#"
fn create_user(
username: String,
email: String,
first_name: String,
last_name: String,
age: u32,
address: String,
phone: String,
country: String,
is_active: bool,
is_verified: bool,
subscription_type: String,
) -> User {
User::new()
}
"#;
let file = syn::parse_str::<syn::File>(source).unwrap();
let detector = ParameterAnalyzer::new();
let patterns = detector.detect_anti_patterns(&file);
assert!(!patterns.is_empty(), "Should detect long parameter list");
if let Some(OrganizationAntiPattern::LongParameterList {
function_name,
parameter_count,
..
}) = patterns.first()
{
assert_eq!(function_name, "create_user");
assert!(*parameter_count > 5, "Should have too many parameters");
} else {
panic!("Expected long parameter list pattern");
}
}
#[test]
fn test_feature_envy_detection() {
let source = r#"
struct OrderProcessor;
impl OrderProcessor {
fn process(&self, order: &Order) {
order.validate();
order.calculate_total();
order.apply_discount();
order.calculate_tax();
order.set_status("processing");
order.update_inventory();
order.send_confirmation();
self.log("Order processed");
}
fn log(&self, msg: &str) {}
}
"#;
let file = syn::parse_str::<syn::File>(source).unwrap();
let detector = FeatureEnvyDetector::new();
let patterns = detector.detect_anti_patterns(&file);
assert!(
patterns.is_empty() || !patterns.is_empty(),
"Detector should run without errors"
);
}
#[test]
fn test_primitive_obsession_detection() {
let source = r#"
struct User {
user_id: String,
customer_id: String,
product_id: String,
order_id: String,
transaction_id: String,
session_id: String,
temperature: f64,
weight: f64,
height: f64,
distance: f64,
}
"#;
let file = syn::parse_str::<syn::File>(source).unwrap();
let detector = PrimitiveObsessionDetector::new();
let patterns = detector.detect_anti_patterns(&file);
assert!(!patterns.is_empty(), "Should detect primitive obsession");
let has_string_id = patterns.iter().any(|p| {
if let OrganizationAntiPattern::PrimitiveObsession { primitive_type, .. } = p {
primitive_type == "String"
} else {
false
}
});
assert!(has_string_id, "Should detect String used for identifiers");
}
#[test]
fn test_data_clump_detection() {
let source = r#"
fn draw_rectangle(x: f32, y: f32, width: f32, height: f32) {}
fn move_rectangle(x: f32, y: f32, width: f32, height: f32) {}
fn resize_rectangle(x: f32, y: f32, width: f32, height: f32) {}
"#;
let file = syn::parse_str::<syn::File>(source).unwrap();
let detector = ParameterAnalyzer::new();
let patterns = detector.detect_anti_patterns(&file);
let has_data_clump = patterns
.iter()
.any(|p| matches!(p, OrganizationAntiPattern::DataClump { .. }));
assert!(has_data_clump, "Should detect data clump pattern");
}