use std::fs;
use tempfile::TempDir;
fn create_realistic_project() -> TempDir {
let temp_dir = TempDir::new().unwrap();
let base = temp_dir.path();
fs::create_dir_all(base.join("backend/src/api")).unwrap();
fs::write(
base.join("backend/src/main.rs"),
r#"use api::handlers::UserHandler;
use std::net::TcpListener;
pub struct Config {
port: u16,
database_url: String,
}
impl Config {
pub fn from_env() -> Self {
Config {
port: 8080,
database_url: std::env::var("DATABASE_URL").unwrap_or_default(),
}
}
}
fn main() {
let config = Config::from_env();
let listener = TcpListener::bind(format!("0.0.0.0:{}", config.port)).unwrap();
println!("Server running on port {}", config.port);
let handler = UserHandler::new();
// ... server loop
}
"#,
)
.unwrap();
fs::write(
base.join("backend/src/api/handlers.rs"),
r#"use crate::models::User;
use std::collections::HashMap;
pub struct UserHandler {
users: HashMap<u64, User>,
}
impl UserHandler {
pub fn new() -> Self {
UserHandler {
users: HashMap::new(),
}
}
pub fn get_user(&self, id: u64) -> Option<&User> {
self.users.get(&id)
}
pub fn create_user(&mut self, user: User) -> u64 {
let id = user.id;
self.users.insert(id, user);
id
}
}
// TODO: Add authentication middleware
fn authenticate(token: &str) -> bool {
!token.is_empty()
}
"#,
)
.unwrap();
fs::write(
base.join("backend/src/models.rs"),
r#"pub struct User {
pub id: u64,
pub name: String,
pub email: String,
}
impl User {
pub fn new(id: u64, name: &str, email: &str) -> Self {
User {
id,
name: name.to_string(),
email: email.to_string(),
}
}
}
pub struct Order {
pub id: u64,
pub user_id: u64,
pub total: f64,
}
"#,
)
.unwrap();
fs::create_dir_all(base.join("scripts/ml")).unwrap();
fs::write(
base.join("scripts/ml/preprocess.py"),
r#"import pandas as pd
import numpy as np
class DataPreprocessor:
def __init__(self, config):
self.config = config
self.scaler = None
def normalize(self, data):
mean = np.mean(data, axis=0)
std = np.std(data, axis=0)
return (data - mean) / std
def split_train_test(self, data, ratio=0.8):
split_idx = int(len(data) * ratio)
return data[:split_idx], data[split_idx:]
# FIXME: Handle missing values better
def handle_missing(data):
return data.fillna(0)
if __name__ == "__main__":
preprocessor = DataPreprocessor({"batch_size": 32})
# ...
"#,
)
.unwrap();
fs::write(
base.join("scripts/ml/model.py"),
r#"import torch
import torch.nn as nn
class NeuralNetwork(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(NeuralNetwork, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, output_size)
def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
def train_model(model, data, epochs=100):
optimizer = torch.optim.Adam(model.parameters())
for epoch in range(epochs):
# training loop
pass
# TODO: Add evaluation metrics
def evaluate(model, data):
pass
"#,
)
.unwrap();
fs::create_dir_all(base.join("frontend/src/components")).unwrap();
fs::write(
base.join("frontend/src/app.js"),
r#"import React, { useState, useEffect } from 'react';
import { UserList } from './components/UserList';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { users: [], loading: true };
}
async componentDidMount() {
try {
const response = await fetch('/api/users');
const users = await response.json();
this.setState({ users, loading: false });
} catch (error) {
console.error('Failed to fetch users:', error);
this.setState({ loading: false });
}
}
render() {
if (this.state.loading) {
return <div>Loading...</div>;
}
return <UserList users={this.state.users} />;
}
}
export default App;
"#,
)
.unwrap();
fs::write(
base.join("frontend/src/components/UserList.js"),
r#"import React from 'react';
export function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name} - {user.email}</li>
))}
</ul>
);
}
// TODO: Add pagination
function Pagination({ page, totalPages, onPageChange }) {
return <div>Page {page} of {totalPages}</div>;
}
"#,
)
.unwrap();
temp_dir
}
#[test]
fn test_symbol_extraction_cross_language() {
let dir = create_realistic_project();
let rust_file = dir.path().join("backend/src/api/handlers.rs");
let py_file = dir.path().join("scripts/ml/preprocess.py");
let js_file = dir.path().join("frontend/src/app.js");
let rust_symbols = codesearch::symbols::extract_symbols_from_file(&rust_file).unwrap();
let py_symbols = codesearch::symbols::extract_symbols_from_file(&py_file).unwrap();
let js_symbols = codesearch::symbols::extract_symbols_from_file(&js_file).unwrap();
assert!(!rust_symbols.is_empty(), "Should extract Rust symbols");
assert!(!py_symbols.is_empty(), "Should extract Python symbols");
assert!(!js_symbols.is_empty(), "Should extract JavaScript symbols");
let rust_has_struct = rust_symbols.iter().any(|s| {
s.kind == codesearch::symbols::SymbolKind::Struct
|| s.kind == codesearch::symbols::SymbolKind::Class
});
let py_has_class = py_symbols
.iter()
.any(|s| s.kind == codesearch::symbols::SymbolKind::Class);
let js_has_class = js_symbols
.iter()
.any(|s| s.kind == codesearch::symbols::SymbolKind::Class);
assert!(rust_has_struct, "Should find struct in Rust");
assert!(py_has_class, "Should find class in Python");
assert!(js_has_class, "Should find class in JavaScript");
}
#[test]
fn test_symbol_index_cross_file() {
let dir = create_realistic_project();
let index = codesearch::symbols::SymbolIndex::new();
for entry in walkdir::WalkDir::new(dir.path())
.into_iter()
.filter_map(|e| e.ok())
{
let path = entry.path();
if path.is_file() {
if let Some(ext) = path.extension() {
let ext = ext.to_string_lossy();
if ext == "rs" || ext == "py" || ext == "js" {
if let Ok(symbols) = codesearch::symbols::extract_symbols_from_file(path) {
for symbol in symbols {
index.add_symbol(symbol);
}
}
}
}
}
}
let config_symbols = index.find_by_name("Config");
let user_symbols = index.find_by_name("User");
assert!(
!config_symbols.is_empty() || !user_symbols.is_empty(),
"Should find common symbols across files"
);
let stats = index.get_stats();
assert!(stats.total_symbols > 0);
assert!(stats.total_files > 0);
}
#[test]
fn test_search_then_analyze_workflow() {
let dir = create_realistic_project();
let options = codesearch::types::SearchOptions::default();
let todo_results = codesearch::search_code("TODO", dir.path(), &options).unwrap();
assert!(!todo_results.is_empty(), "Should find TODO markers");
let todo_files: std::collections::HashSet<_> =
todo_results.iter().map(|r| r.file.clone()).collect();
for file_path in &todo_files {
let path = std::path::Path::new(file_path);
if path.exists() {
if let Ok(content) = std::fs::read_to_string(path) {
let metrics = codesearch::complexity::calculate_file_complexity(
&path.to_string_lossy(),
&content,
);
assert!(
metrics.cyclomatic_complexity >= 0,
"Complexity should be non-negative"
);
}
}
}
}
#[test]
fn test_find_symbol_cross_references() {
let dir = create_realistic_project();
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("new", dir.path(), &options).unwrap();
assert!(
!results.is_empty(),
"Should find constructors across languages"
);
let has_rust = results.iter().any(|r| r.file.ends_with(".rs"));
let has_python = results.iter().any(|r| r.file.ends_with(".py"));
let has_js = results.iter().any(|r| r.file.ends_with(".js"));
assert!(
has_rust || has_python || has_js,
"Should find constructors in at least one language"
);
}
#[test]
fn test_deadcode_in_realistic_project() {
let dir = create_realistic_project();
let ext = &["rs".to_string(), "py".to_string(), "js".to_string()];
let dead_items =
codesearch::deadcode::find_dead_code(dir.path(), Some(ext.as_slice()), None).unwrap();
assert!(
!dead_items.is_empty() || true,
"Dead code detection should run without errors"
);
}
#[test]
fn test_duplicate_detection_across_modules() {
let dir = create_realistic_project();
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("println!", dir.path(), &options).unwrap();
assert!(
!results.is_empty() || true,
"Should find logging patterns or complete without error"
);
}
#[test]
fn test_empty_directory_search() {
let empty_dir = TempDir::new().unwrap();
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("test", empty_dir.path(), &options).unwrap();
assert!(
results.is_empty(),
"Empty directory should return no results"
);
}
#[test]
fn test_very_long_line_search() {
let dir = TempDir::new().unwrap();
let long_line = "a".repeat(10000);
fs::write(
dir.path().join("long.rs"),
format!("fn test() {{ let x = \"{}\"; }}", long_line),
)
.unwrap();
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("fn test", dir.path(), &options).unwrap();
assert!(!results.is_empty(), "Should handle very long lines");
}
#[test]
fn test_special_characters_in_search() {
let dir = TempDir::new().unwrap();
fs::write(
dir.path().join("special.rs"),
r#"fn test() {
let regex = r"\d+";
let path = "/usr/local/bin";
let template = "Hello {{name}}!";
}"#,
)
.unwrap();
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code(r#"r""#, dir.path(), &options).unwrap();
assert!(!results.is_empty(), "Should find raw string literals");
}
#[test]
fn test_unicode_content_search() {
let dir = TempDir::new().unwrap();
fs::write(
dir.path().join("unicode.py"),
"# 这是一个测试\ndef hello_world():\n print('ä½ å¥½ä¸–ç•Œ')\n",
)
.unwrap();
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("hello_world", dir.path(), &options).unwrap();
assert!(!results.is_empty(), "Should handle Unicode content");
}
#[test]
fn test_binary_file_exclusion() {
let dir = TempDir::new().unwrap();
fs::write(dir.path().join("text.rs"), "fn main() {}").unwrap();
let binary_data: Vec<u8> = (0..256).map(|i| i as u8).collect();
fs::write(dir.path().join("data.bin"), &binary_data).unwrap();
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("fn main", dir.path(), &options).unwrap();
assert_eq!(results.len(), 1, "Should only find text file, not binary");
}
#[test]
fn test_health_score_realistic_project() {
let dir = create_realistic_project();
let report = codesearch::health::scan_health(dir.path(), None, None).unwrap();
assert!(report.score <= 100, "Health score should be at most 100");
assert!(report.total_files > 0, "Should analyze files");
}
#[test]
fn test_large_number_of_small_files() {
let dir = TempDir::new().unwrap();
for i in 0..100 {
fs::write(
dir.path().join(format!("file_{}.rs", i)),
format!("fn function_{}() {{ println!(\"{}\"); }}", i, i),
)
.unwrap();
}
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("fn function", dir.path(), &options).unwrap();
assert_eq!(results.len(), 100, "Should find all 100 functions");
}
#[test]
fn test_nested_directory_search() {
let dir = TempDir::new().unwrap();
let mut current = dir.path().to_path_buf();
for i in 0..5 {
current = current.join(format!("level_{}", i));
fs::create_dir_all(¤t).unwrap();
fs::write(
current.join("file.rs"),
format!("fn level_{}_func() {{}}", i),
)
.unwrap();
}
let options = codesearch::types::SearchOptions::default();
let results = codesearch::search_code("fn level", dir.path(), &options).unwrap();
assert_eq!(
results.len(),
5,
"Should find functions in all nested levels"
);
}
#[test]
fn test_symbol_relationships_in_project() {
let dir = create_realistic_project();
let graph = codesearch::symbols::RelationshipGraph::new();
let backend_dir = dir.path().join("backend/src");
for entry in walkdir::WalkDir::new(&backend_dir)
.into_iter()
.filter_map(|e| e.ok())
{
let path = entry.path();
if path.is_file() && path.extension().map(|e| e == "rs").unwrap_or(false) {
if let Ok(symbols) = codesearch::symbols::extract_symbols_from_file(path) {
for symbol in &symbols {
graph.add_symbol(symbol.clone());
}
}
}
}
let all = graph.all_symbols();
assert!(!all.is_empty(), "Should have indexed backend symbols");
}