fix: all 4 archdoc issues — cycles, layout, integrations, usage examples

1. Module Cycles: properly format cycle paths as A → B → C → A
2. Repository layout: group by top-level directory with file counts
3. Integration detection: match patterns against import names (substring),
   add Storage and AI/ML categories to all templates and summary
4. Usage examples: extract __init__ required params for class constructors

Also fix golden test to use ends_with for module-prefixed symbol IDs.
This commit is contained in:
2026-02-15 11:14:42 +03:00
parent c095560e13
commit a3ee003947
10 changed files with 314 additions and 89 deletions

View File

@@ -1,6 +1,8 @@
//! Integration detection tests for ArchDoc
//!
//! These tests verify that the integration detection functionality works correctly.
//! Integration detection now happens at module level during resolve_symbols,
//! based on actual imports rather than AST body inspection.
use std::fs;
use tempfile::TempDir;
@@ -8,11 +10,12 @@ use archdoc_core::{Config, python_analyzer::PythonAnalyzer};
#[test]
fn test_http_integration_detection() {
let config = Config::default();
let mut config = Config::default();
let temp_dir = TempDir::new().expect("Failed to create temp dir");
config.project.root = temp_dir.path().to_string_lossy().to_string();
config.python.src_roots = vec![".".to_string()];
let analyzer = PythonAnalyzer::new(config);
// Create a temporary Python file with HTTP integration
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let temp_file = temp_dir.path().join("test.py");
let python_code = r#"
import requests
@@ -23,16 +26,16 @@ def fetch_data():
"#;
fs::write(&temp_file, python_code).expect("Failed to write test file");
// Parse the module
let parsed_module = analyzer.parse_module(&temp_file)
.expect("Failed to parse module");
// Check that we found the function
assert_eq!(parsed_module.symbols.len(), 1);
let symbol = &parsed_module.symbols[0];
assert_eq!(symbol.id, "fetch_data");
let model = analyzer.resolve_symbols(&[parsed_module])
.expect("Failed to resolve symbols");
// Find the symbol (now prefixed with module id)
let symbol = model.symbols.values().find(|s| s.qualname == "fetch_data")
.expect("fetch_data symbol not found");
// Check that HTTP integration is detected
assert!(symbol.integrations_flags.http);
assert!(!symbol.integrations_flags.db);
assert!(!symbol.integrations_flags.queue);
@@ -40,11 +43,12 @@ def fetch_data():
#[test]
fn test_db_integration_detection() {
let config = Config::default();
let mut config = Config::default();
let temp_dir = TempDir::new().expect("Failed to create temp dir");
config.project.root = temp_dir.path().to_string_lossy().to_string();
config.python.src_roots = vec![".".to_string()];
let analyzer = PythonAnalyzer::new(config);
// Create a temporary Python file with DB integration
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let temp_file = temp_dir.path().join("test.py");
let python_code = r#"
import sqlite3
@@ -57,16 +61,15 @@ def get_user(user_id):
"#;
fs::write(&temp_file, python_code).expect("Failed to write test file");
// Parse the module
let parsed_module = analyzer.parse_module(&temp_file)
.expect("Failed to parse module");
// Check that we found the function
assert_eq!(parsed_module.symbols.len(), 1);
let symbol = &parsed_module.symbols[0];
assert_eq!(symbol.id, "get_user");
let model = analyzer.resolve_symbols(&[parsed_module])
.expect("Failed to resolve symbols");
let symbol = model.symbols.values().find(|s| s.qualname == "get_user")
.expect("get_user symbol not found");
// Check that DB integration is detected
assert!(!symbol.integrations_flags.http);
assert!(symbol.integrations_flags.db);
assert!(!symbol.integrations_flags.queue);
@@ -74,11 +77,12 @@ def get_user(user_id):
#[test]
fn test_queue_integration_detection() {
let config = Config::default();
let mut config = Config::default();
let temp_dir = TempDir::new().expect("Failed to create temp dir");
config.project.root = temp_dir.path().to_string_lossy().to_string();
config.python.src_roots = vec![".".to_string()];
let analyzer = PythonAnalyzer::new(config);
// Create a temporary Python file with queue integration
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let temp_file = temp_dir.path().join("test.py");
let python_code = r#"
import redis
@@ -89,16 +93,15 @@ def process_job(job_data):
"#;
fs::write(&temp_file, python_code).expect("Failed to write test file");
// Parse the module
let parsed_module = analyzer.parse_module(&temp_file)
.expect("Failed to parse module");
// Check that we found the function
assert_eq!(parsed_module.symbols.len(), 1);
let symbol = &parsed_module.symbols[0];
assert_eq!(symbol.id, "process_job");
let model = analyzer.resolve_symbols(&[parsed_module])
.expect("Failed to resolve symbols");
let symbol = model.symbols.values().find(|s| s.qualname == "process_job")
.expect("process_job symbol not found");
// Check that queue integration is detected
assert!(!symbol.integrations_flags.http);
assert!(!symbol.integrations_flags.db);
assert!(symbol.integrations_flags.queue);
@@ -106,11 +109,12 @@ def process_job(job_data):
#[test]
fn test_no_integration_detection() {
let config = Config::default();
let mut config = Config::default();
let temp_dir = TempDir::new().expect("Failed to create temp dir");
config.project.root = temp_dir.path().to_string_lossy().to_string();
config.python.src_roots = vec![".".to_string()];
let analyzer = PythonAnalyzer::new(config);
// Create a temporary Python file with no integrations
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let temp_file = temp_dir.path().join("test.py");
let python_code = r#"
def calculate_sum(a, b):
@@ -118,17 +122,16 @@ def calculate_sum(a, b):
"#;
fs::write(&temp_file, python_code).expect("Failed to write test file");
// Parse the module
let parsed_module = analyzer.parse_module(&temp_file)
.expect("Failed to parse module");
// Check that we found the function
assert_eq!(parsed_module.symbols.len(), 1);
let symbol = &parsed_module.symbols[0];
assert_eq!(symbol.id, "calculate_sum");
let model = analyzer.resolve_symbols(&[parsed_module])
.expect("Failed to resolve symbols");
let symbol = model.symbols.values().find(|s| s.qualname == "calculate_sum")
.expect("calculate_sum symbol not found");
// Check that no integrations are detected
assert!(!symbol.integrations_flags.http);
assert!(!symbol.integrations_flags.db);
assert!(!symbol.integrations_flags.queue);
}
}