use car_ast::{Language, ProjectIndex, parse, parse_file};
use std::path::Path;
#[test]
fn parse_real_rust_file() {
let source = std::fs::read_to_string(
Path::new(env!("CARGO_MANIFEST_DIR")).join("src/lib.rs")
).unwrap();
let parsed = parse(&source, Language::Rust).unwrap();
let parse_fns = parsed.find_symbol("parse");
assert!(!parse_fns.is_empty(), "should find the `parse` function in car-ast/src/lib.rs");
let parse_file_fns = parsed.find_symbol("parse_file");
assert!(!parse_file_fns.is_empty(), "should find `parse_file` function");
let all = parsed.all_symbols();
assert!(all.len() >= 4, "should have at least 4 symbols, got {}", all.len());
assert!(!parsed.imports.is_empty(), "should have imports");
}
#[test]
fn parse_types_file() {
let source = std::fs::read_to_string(
Path::new(env!("CARGO_MANIFEST_DIR")).join("src/types.rs")
).unwrap();
let parsed = parse(&source, Language::Rust).unwrap();
let language = parsed.find_symbol("Language");
assert!(!language.is_empty(), "should find Language enum");
let symbol = parsed.find_symbol("Symbol");
assert!(!symbol.is_empty(), "should find Symbol struct");
let span = parsed.find_symbol("Span");
assert!(!span.is_empty(), "should find Span struct");
let parsed_file = parsed.find_symbol("ParsedFile");
assert!(!parsed_file.is_empty(), "should find ParsedFile struct");
let impls: Vec<_> = parsed.symbols.iter()
.filter(|s| s.kind == car_ast::SymbolKind::Impl && s.name == "ParsedFile")
.collect();
assert!(!impls.is_empty(), "should find ParsedFile impl block");
let methods: Vec<_> = impls[0].children.iter()
.map(|m| m.name.as_str())
.collect();
assert!(methods.contains(&"find_symbol"), "ParsedFile should have find_symbol method, got {:?}", methods);
assert!(methods.contains(&"all_symbols"), "ParsedFile should have all_symbols method");
}
#[test]
fn index_car_ast_crate() {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let index = ProjectIndex::build(&root);
let stats = index.stats();
assert!(stats.files >= 5, "should parse >= 5 files, got {}", stats.files);
assert!(stats.symbols >= 20, "should have >= 20 symbols, got {}", stats.symbols);
assert!(stats.references > 0, "should have cross-file references, got {}", stats.references);
let symbols = index.find("Symbol");
assert!(!symbols.is_empty(), "should find Symbol in index");
let span_callers = index.callers_of("Span");
assert!(!span_callers.is_empty(), "Span should be referenced from other files");
println!("Index stats: {}", stats);
println!("Span referenced from {} locations", span_callers.len());
for r in &span_callers {
println!(" {} ({}) -> Span", r.from_file, r.from_symbol);
}
}
#[test]
fn parse_rust_extractor() {
let source = std::fs::read_to_string(
Path::new(env!("CARGO_MANIFEST_DIR")).join("src/languages/rust.rs")
).unwrap();
let parsed = parse_file(&source, "rust.rs").unwrap();
let extract = parsed.find_symbol("extract");
assert!(!extract.is_empty(), "should find `extract` function");
let helpers = parsed.find_symbol_fuzzy("extract_");
assert!(helpers.len() >= 3, "should find multiple extract_* helpers, got {}", helpers.len());
for sym in parsed.all_symbols() {
assert!(!sym.signature.is_empty(),
"symbol {} should have a non-empty signature", sym.name);
}
}
#[test]
fn parse_real_python() {
let source = r#"
import asyncio
from dataclasses import dataclass, field
from typing import Optional, List
@dataclass
class AgentConfig:
"""Configuration for an AI agent."""
name: str
model: str = "gpt-4"
temperature: float = 0.7
max_tokens: int = 4096
tools: List[str] = field(default_factory=list)
class Agent:
"""An AI agent that can execute tasks."""
def __init__(self, config: AgentConfig):
self.config = config
self._history: List[dict] = []
async def run(self, prompt: str) -> str:
"""Run the agent with a prompt."""
self._history.append({"role": "user", "content": prompt})
return await self._generate()
async def _generate(self) -> str:
"""Generate a response using the configured model."""
return "response"
@property
def history(self) -> List[dict]:
return self._history
async def create_agent(name: str, model: Optional[str] = None) -> Agent:
"""Factory function for creating agents."""
config = AgentConfig(name=name, model=model or "gpt-4")
return Agent(config)
MAX_RETRIES = 3
"#;
let parsed = parse(source, Language::Python).unwrap();
let agent_config = parsed.find_symbol("AgentConfig");
assert!(!agent_config.is_empty(), "should find AgentConfig class");
assert_eq!(agent_config[0].kind, car_ast::SymbolKind::Class);
let agent = parsed.find_symbol("Agent");
assert!(!agent.is_empty(), "should find Agent class");
let agent_class = &parsed.symbols.iter()
.find(|s| s.name == "Agent" && s.kind == car_ast::SymbolKind::Class)
.unwrap();
assert!(agent_class.children.len() >= 3,
"Agent should have >= 3 methods, got {}", agent_class.children.len());
let create = parsed.find_symbol("create_agent");
assert!(!create.is_empty(), "should find create_agent function");
assert_eq!(create[0].kind, car_ast::SymbolKind::Function);
let max_retries = parsed.find_symbol("MAX_RETRIES");
assert!(!max_retries.is_empty(), "should find MAX_RETRIES constant");
assert_eq!(max_retries[0].kind, car_ast::SymbolKind::Const);
assert!(parsed.imports.len() >= 2, "should have imports");
}
#[test]
fn parse_real_typescript() {
let source = r#"
import { EventEmitter } from 'events';
interface Plugin {
name: string;
init(): Promise<void>;
destroy?(): void;
}
type PluginFactory = (config: Record<string, unknown>) => Plugin;
export class Runtime extends EventEmitter {
private plugins: Map<string, Plugin> = new Map();
constructor(private config: RuntimeConfig) {
super();
}
async loadPlugin(factory: PluginFactory): Promise<void> {
const plugin = factory(this.config);
this.plugins.set(plugin.name, plugin);
await plugin.init();
this.emit('plugin:loaded', plugin.name);
}
getPlugin(name: string): Plugin | undefined {
return this.plugins.get(name);
}
}
export async function createRuntime(config: RuntimeConfig): Promise<Runtime> {
return new Runtime(config);
}
"#;
let parsed = parse(source, Language::TypeScript).unwrap();
let plugin = parsed.find_symbol("Plugin");
assert!(!plugin.is_empty(), "should find Plugin interface");
let factory = parsed.find_symbol("PluginFactory");
assert!(!factory.is_empty(), "should find PluginFactory type alias");
let runtime = parsed.find_symbol("Runtime");
assert!(!runtime.is_empty(), "should find Runtime class");
let runtime_class = &parsed.symbols.iter()
.find(|s| s.name == "Runtime" && s.kind == car_ast::SymbolKind::Class)
.unwrap();
let method_names: Vec<_> = runtime_class.children.iter()
.map(|m| m.name.as_str())
.collect();
assert!(method_names.contains(&"loadPlugin"), "should find loadPlugin method, got {:?}", method_names);
assert!(method_names.contains(&"getPlugin"), "should find getPlugin method");
let create = parsed.find_symbol("createRuntime");
assert!(!create.is_empty(), "should find createRuntime function");
}
#[test]
fn parse_real_go() {
let source = r#"
package server
import (
"context"
"net/http"
"sync"
)
// Server handles HTTP requests.
type Server struct {
mu sync.Mutex
handler http.Handler
addr string
}
// Handler defines the request handling interface.
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
Health() error
}
// NewServer creates a new server instance.
func NewServer(addr string, handler http.Handler) *Server {
return &Server{addr: addr, handler: handler}
}
// Start begins listening for connections.
func (s *Server) Start(ctx context.Context) error {
s.mu.Lock()
defer s.mu.Unlock()
return http.ListenAndServe(s.addr, s.handler)
}
const MaxConnections = 1000
"#;
let parsed = parse(source, Language::Go).unwrap();
let server = parsed.find_symbol("Server");
assert!(!server.is_empty(), "should find Server struct");
let handler = parsed.find_symbol("Handler");
assert!(!handler.is_empty(), "should find Handler interface");
let new_server = parsed.find_symbol("NewServer");
assert!(!new_server.is_empty(), "should find NewServer function");
let start = parsed.find_symbol("Start");
assert!(!start.is_empty(), "should find Start method");
assert_eq!(start[0].kind, car_ast::SymbolKind::Method);
assert!(start[0].parent.is_some(), "Start should have a receiver");
assert!(!parsed.imports.is_empty(), "should have imports");
}