From ecf0ead7c768681d73a5160dadddf4665e713899 Mon Sep 17 00:00:00 2001 From: Sienna Meridian Satterwhite Date: Tue, 24 Mar 2026 17:24:39 +0000 Subject: [PATCH] feat: project symbol extraction integration test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - extract_project_symbols against tempdir with Rust/TypeScript/Python sources — walk_directory, detect_language, hidden/vendor dir skipping, ProjectSymbol field population, docstring extraction --- src/integration_test.rs | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/integration_test.rs b/src/integration_test.rs index a6404f2..ad0c538 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -5884,6 +5884,96 @@ mod research_extended_tests { assert_eq!(total_calls, 3); } + #[test] + fn test_extract_project_symbols_from_sol_src() { + // Point at Sol's own src/context.rs — small, known content + use crate::code_index::symbols; + + let dir = tempfile::TempDir::new().unwrap(); + let src = dir.path().join("src"); + std::fs::create_dir_all(&src).unwrap(); + + // Write a small Rust file + std::fs::write(src.join("lib.rs"), r#" +/// A greeting function. +pub fn hello(name: &str) -> String { + format!("Hello, {name}!") +} + +/// A simple struct. +pub struct Config { + pub name: String, + pub port: u16, +} + +impl Config { + pub fn new() -> Self { + Self { name: "test".into(), port: 8080 } + } +} +"#).unwrap(); + + // Write a TypeScript file + std::fs::write(src.join("app.ts"), r#" +/** Main application entry point. */ +export function main(): void { + console.log("hello"); +} + +/** User interface. */ +export interface User { + name: string; + email: string; +} +"#).unwrap(); + + // Write a Python file + std::fs::write(src.join("util.py"), r#" +"""Utility functions.""" + +def add(a: int, b: int) -> int: + """Add two numbers.""" + return a + b + +class Calculator: + """A simple calculator.""" + def multiply(self, a: int, b: int) -> int: + return a * b +"#).unwrap(); + + // Also write a file that should be skipped (hidden dir, node_modules) + let hidden = dir.path().join(".hidden"); + std::fs::create_dir_all(&hidden).unwrap(); + std::fs::write(hidden.join("secret.rs"), "pub fn secret() {}").unwrap(); + + let nm = dir.path().join("node_modules"); + std::fs::create_dir_all(&nm).unwrap(); + std::fs::write(nm.join("dep.ts"), "export function dep() {}").unwrap(); + + let symbols = symbols::extract_project_symbols(dir.path().to_str().unwrap()); + + // Should find symbols from all 3 source files + let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"hello"), "Should find Rust function 'hello'"); + assert!(names.contains(&"Config"), "Should find Rust struct 'Config'"); + assert!(names.contains(&"main"), "Should find TS function 'main'"); + assert!(names.contains(&"add"), "Should find Python function 'add'"); + assert!(names.contains(&"Calculator"), "Should find Python class 'Calculator'"); + + // Should NOT find hidden/vendor files + assert!(!names.contains(&"secret"), "Should skip hidden directory"); + assert!(!names.contains(&"dep"), "Should skip node_modules"); + + // Verify ProjectSymbol fields + let hello = symbols.iter().find(|s| s.name == "hello").unwrap(); + assert_eq!(hello.language, "rust"); + assert_eq!(hello.kind, "function"); + assert!(hello.signature.contains("pub fn hello")); + assert!(hello.docstring.contains("greeting")); + assert!(hello.file_path.contains("lib.rs")); + assert!(!hello.content.is_empty()); + } + #[tokio::test] async fn test_research_execute_single_task() { let env_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(".env");