use debtmap::extraction::UnifiedFileExtractor;
use debtmap::extraction::adapters::god_object::analyze_god_objects;
use debtmap::organization::{
BehaviorCategory, BehavioralCategorizer, FieldAccessTracker, MethodCluster,
cluster_methods_by_behavior, suggest_trait_extraction,
};
use std::collections::HashMap;
use std::fs;
use std::path::Path;
#[test]
fn test_identifies_rendering_method_group() {
let fixture_path = "tests/fixtures/zed_editor_fixture.rs";
let code = fs::read_to_string(fixture_path).expect("Failed to read fixture");
let file = syn::parse_file(&code).expect("Failed to parse fixture");
let impl_block = file
.items
.iter()
.filter_map(|item| {
if let syn::Item::Impl(item_impl) = item {
Some(item_impl)
} else {
None
}
})
.find(|impl_item| {
if let syn::Type::Path(type_path) = &*impl_item.self_ty {
type_path
.path
.segments
.last()
.map(|seg| seg.ident == "Editor")
.unwrap_or(false)
} else {
false
}
})
.expect("Should find Editor impl block");
let mut method_categories: HashMap<BehaviorCategory, Vec<String>> = HashMap::new();
for item in &impl_block.items {
if let syn::ImplItem::Fn(method) = item {
let method_name = method.sig.ident.to_string();
let category = BehavioralCategorizer::categorize_method(&method_name);
method_categories
.entry(category)
.or_default()
.push(method_name);
}
}
let rendering_methods = method_categories
.get(&BehaviorCategory::Rendering)
.expect("Should identify rendering category");
assert!(
!rendering_methods.is_empty(),
"Should find rendering methods"
);
let expected_rendering = vec![
"render",
"render_gutter",
"paint_highlighted_ranges",
"draw_cursor",
"paint_background",
"show_completions",
"display_diagnostics",
"format_line",
];
for expected in &expected_rendering {
assert!(
rendering_methods.contains(&expected.to_string()),
"Should identify '{}' as rendering method",
expected
);
}
assert!(
rendering_methods.len() >= 5,
"Should identify at least 5 rendering methods, found: {}",
rendering_methods.len()
);
}
#[test]
fn test_identifies_event_handling_group() {
let fixture_path = "tests/fixtures/zed_editor_fixture.rs";
let code = fs::read_to_string(fixture_path).expect("Failed to read fixture");
let file = syn::parse_file(&code).expect("Failed to parse fixture");
let impl_block = file
.items
.iter()
.filter_map(|item| {
if let syn::Item::Impl(item_impl) = item {
Some(item_impl)
} else {
None
}
})
.find(|impl_item| {
if let syn::Type::Path(type_path) = &*impl_item.self_ty {
type_path
.path
.segments
.last()
.map(|seg| seg.ident == "Editor")
.unwrap_or(false)
} else {
false
}
})
.expect("Should find Editor impl block");
let mut method_categories: HashMap<BehaviorCategory, Vec<String>> = HashMap::new();
for item in &impl_block.items {
if let syn::ImplItem::Fn(method) = item {
let method_name = method.sig.ident.to_string();
let category = BehavioralCategorizer::categorize_method(&method_name);
method_categories
.entry(category)
.or_default()
.push(method_name);
}
}
let event_methods = method_categories
.get(&BehaviorCategory::EventHandling)
.expect("Should identify event handling category");
assert!(
!event_methods.is_empty(),
"Should find event handling methods"
);
let expected_events = vec![
"handle_keypress",
"on_mouse_down",
"on_scroll",
"handle_input_event",
"dispatch_action",
"trigger_completion",
"process_event",
"handle_paste",
"on_focus",
"on_blur",
];
for expected in &expected_events {
assert!(
event_methods.contains(&expected.to_string()),
"Should identify '{}' as event handling method",
expected
);
}
assert!(
event_methods.len() >= 5,
"Should identify at least 5 event handling methods, found: {}",
event_methods.len()
);
}
#[test]
fn test_shows_field_dependencies() {
let fixture_path = "tests/fixtures/zed_editor_fixture.rs";
let code = fs::read_to_string(fixture_path).expect("Failed to read fixture");
let file = syn::parse_file(&code).expect("Failed to parse fixture");
let editor_struct = file
.items
.iter()
.filter_map(|item| {
if let syn::Item::Struct(s) = item {
Some(s)
} else {
None
}
})
.find(|s| s.ident == "Editor")
.expect("Should find Editor struct");
let field_names: Vec<String> = if let syn::Fields::Named(fields) = &editor_struct.fields {
fields
.named
.iter()
.map(|f| f.ident.as_ref().unwrap().to_string())
.collect()
} else {
Vec::new()
};
assert!(
!field_names.is_empty(),
"Should extract field names from struct"
);
let impl_block = file
.items
.iter()
.filter_map(|item| {
if let syn::Item::Impl(item_impl) = item {
Some(item_impl)
} else {
None
}
})
.find(|impl_item| {
if let syn::Type::Path(type_path) = &*impl_item.self_ty {
type_path
.path
.segments
.last()
.map(|seg| seg.ident == "Editor")
.unwrap_or(false)
} else {
false
}
})
.expect("Should find Editor impl block");
let mut field_tracker = FieldAccessTracker::new();
field_tracker.analyze_impl(impl_block);
let _render_fields = field_tracker.get_method_fields("render");
let _keypress_fields = field_tracker.get_method_fields("handle_keypress");
let all_rendering_methods: Vec<String> = impl_block
.items
.iter()
.filter_map(|item| {
if let syn::ImplItem::Fn(method) = item {
let name = method.sig.ident.to_string();
if BehavioralCategorizer::categorize_method(&name) == BehaviorCategory::Rendering {
Some(name)
} else {
None
}
} else {
None
}
})
.collect();
if !all_rendering_methods.is_empty() {
let rendering_field_set = field_tracker.get_minimal_field_set(&all_rendering_methods);
let _ = rendering_field_set; }
let display_fields = vec!["display_map", "style", "scroll_manager", "cursor", "gutter"];
let mut found_display_fields = 0;
for field in display_fields {
if field_names.contains(&field.to_string()) {
found_display_fields += 1;
}
}
assert!(
found_display_fields > 0,
"Should have display-related fields in Editor struct"
);
}
#[test]
fn test_provides_specific_method_extraction_recommendations() {
let fixture_path = "tests/fixtures/zed_editor_fixture.rs";
let code = fs::read_to_string(fixture_path).expect("Failed to read fixture");
let extracted = UnifiedFileExtractor::extract(Path::new(fixture_path), &code)
.expect("Failed to extract fixture");
let analyses = analyze_god_objects(Path::new(fixture_path), &extracted);
if analyses.is_empty() {
eprintln!(
"Note: Fixture not detected as god object with per-struct analysis. Skipping test."
);
return;
}
let analysis = &analyses[0];
if analysis.recommended_splits.is_empty() {
eprintln!(
"Note: No splits recommended for this fixture. \
This may be expected for simple test cases."
);
return;
}
let mut has_method_based_split = false;
let mut has_rendering_split = false;
let mut has_event_handling_split = false;
for split in &analysis.recommended_splits {
if split.method_count > 0 {
has_method_based_split = true;
}
if let Some(ref category) = split.behavior_category {
if category.contains("rendering") || category.contains("Rendering") {
has_rendering_split = true;
}
if category.contains("event") || category.contains("Event") {
has_event_handling_split = true;
}
}
if !split.representative_methods.is_empty() {
if let Some(ref category) = split.behavior_category {
if category.contains("rendering") || category.contains("Rendering") {
let rendering_method_names: Vec<&str> = split
.representative_methods
.iter()
.map(|s| s.as_str())
.collect();
let expected_methods = ["render", "paint", "draw", "display", "show"];
let has_rendering_method = expected_methods.iter().any(|expected| {
rendering_method_names
.iter()
.any(|name| name.contains(expected))
});
if has_rendering_method {
println!("✓ Rendering split has appropriate representative methods");
}
}
if category.contains("event") || category.contains("Event") {
let event_method_names: Vec<&str> = split
.representative_methods
.iter()
.map(|s| s.as_str())
.collect();
let expected_methods = ["handle", "on_", "dispatch", "process", "trigger"];
let has_event_method = expected_methods.iter().any(|expected| {
event_method_names
.iter()
.any(|name| name.contains(expected))
});
if has_event_method {
println!("✓ Event handling split has appropriate representative methods");
}
}
}
}
assert!(
!split.suggested_name.to_lowercase().contains("misc"),
"Should not use 'misc' in module name, got: {}",
split.suggested_name
);
assert!(
!split.responsibility.to_lowercase().contains("misc"),
"Should not use 'misc' in responsibility, got: {}",
split.responsibility
);
}
assert!(
has_method_based_split,
"Should have at least one method-based split recommendation"
);
if !has_rendering_split {
eprintln!(
"Warning: No rendering split found - behavioral categorization may need enhancement"
);
}
if !has_event_handling_split {
eprintln!(
"Warning: No event handling split found - behavioral categorization may need enhancement"
);
}
}
#[test]
fn test_suggests_trait_extraction_for_cohesive_groups() {
let fixture_path = "tests/fixtures/zed_editor_fixture.rs";
let code = fs::read_to_string(fixture_path).expect("Failed to read fixture");
let file = syn::parse_file(&code).expect("Failed to parse fixture");
let impl_block = file
.items
.iter()
.filter_map(|item| {
if let syn::Item::Impl(item_impl) = item {
Some(item_impl)
} else {
None
}
})
.find(|impl_item| {
if let syn::Type::Path(type_path) = &*impl_item.self_ty {
type_path
.path
.segments
.last()
.map(|seg| seg.ident == "Editor")
.unwrap_or(false)
} else {
false
}
})
.expect("Should find Editor impl block");
let method_names: Vec<String> = impl_block
.items
.iter()
.filter_map(|item| {
if let syn::ImplItem::Fn(method) = item {
Some(method.sig.ident.to_string())
} else {
None
}
})
.collect();
let clusters = cluster_methods_by_behavior(&method_names);
assert!(!clusters.is_empty(), "Should identify method clusters");
for (category, methods) in &clusters {
if methods.len() >= 3 {
let cluster = MethodCluster {
category: category.clone(),
methods: methods.clone(),
fields_accessed: Vec::new(),
internal_calls: 0,
external_calls: 0,
cohesion_score: 0.7, };
let trait_suggestion = suggest_trait_extraction(&cluster, "Editor");
assert!(
!trait_suggestion.is_empty(),
"Should generate trait suggestion for {:?}",
category
);
assert!(
!trait_suggestion.to_lowercase().contains("miscops"),
"Trait name should not be based on 'misc', got: {}",
trait_suggestion
);
assert!(
trait_suggestion.contains("trait"),
"Should contain 'trait' keyword"
);
}
}
}
#[test]
fn test_complete_zed_editor_analysis() {
let fixture_path = "tests/fixtures/zed_editor_fixture.rs";
let code = fs::read_to_string(fixture_path).expect("Failed to read fixture");
let extracted = UnifiedFileExtractor::extract(Path::new(fixture_path), &code)
.expect("Failed to extract fixture");
let analyses = analyze_god_objects(Path::new(fixture_path), &extracted);
if analyses.is_empty() {
eprintln!(
"Note: Fixture not detected as god object with per-struct analysis. Skipping test."
);
return;
}
let analysis = &analyses[0];
if !analysis.is_god_object {
eprintln!(
"Note: Editor not detected as god object. \
This may be expected with new thresholds."
);
return;
}
if analysis.recommended_splits.is_empty() {
eprintln!(
"Note: No splits recommended for this fixture. \
This may be expected for simple test cases."
);
return;
}
let behavioral_splits: Vec<_> = analysis
.recommended_splits
.iter()
.filter(|split| split.behavior_category.is_some())
.collect();
if !behavioral_splits.is_empty() {
println!(
"✓ Found {} behavioral split recommendations",
behavioral_splits.len()
);
} else {
eprintln!(
"Note: No behavioral categorization found yet - this indicates Spec 178 needs completion"
);
}
for split in &analysis.recommended_splits {
assert!(
!split.suggested_name.to_lowercase().contains("misc"),
"Should eliminate 'misc' category from recommendations"
);
}
println!("\n=== Zed Editor Analysis Summary ===");
println!("God object detected: {}", analysis.is_god_object);
println!(
"Number of recommendations: {}",
analysis.recommended_splits.len()
);
println!("\nRecommended splits:");
for split in &analysis.recommended_splits {
println!(
" - {} ({} methods) - Category: {:?}",
split.suggested_name, split.method_count, split.behavior_category
);
if !split.representative_methods.is_empty() {
println!(
" Representative methods: {}",
split.representative_methods.join(", ")
);
}
}
}