use car_ast::{parse, parse_file, Language, ProjectIndex};
use std::path::Path;
fn fixture(path: &str) -> std::path::PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/representative/src")
.join(path)
}
#[test]
fn parse_fixture_lib_rs() {
let source = std::fs::read_to_string(fixture("lib.rs")).unwrap();
let parsed = parse(&source, Language::Rust).unwrap();
assert_eq!(parsed.find_symbol("parse_fixture").len(), 1);
assert_eq!(parsed.find_symbol("parse_file_fixture").len(), 1);
assert!(
!parsed.imports.is_empty(),
"fixture imports must be captured"
);
let complex = std::fs::read_to_string(fixture("complex_pipeline.rs")).unwrap();
assert!(
complex.lines().count() >= 150,
"real-scale fixture shrank below 150 lines: {}",
complex.lines().count()
);
let parsed = parse(&complex, Language::Rust).unwrap();
for (name, kind) in [
("EventSink", car_ast::SymbolKind::Trait),
("EventKind", car_ast::SymbolKind::Enum),
("StreamProcessor", car_ast::SymbolKind::Struct),
("process_batch", car_ast::SymbolKind::Function),
("validation", car_ast::SymbolKind::Module),
] {
assert_eq!(
parsed
.all_symbols()
.into_iter()
.filter(|symbol| symbol.name == name && symbol.kind == kind)
.count(),
1,
"expected exactly one {kind:?} named {name}"
);
}
let processor_impl = parsed
.symbols
.iter()
.find(|symbol| {
symbol.kind == car_ast::SymbolKind::Impl && symbol.signature.contains("StreamProcessor")
})
.expect("generic StreamProcessor impl");
assert!(
processor_impl
.children
.iter()
.any(|child| child.name == "process"),
"async method inside generic impl was not extracted"
);
}
#[test]
fn parse_types_file() {
let source = std::fs::read_to_string(fixture("types.rs")).unwrap();
let parsed = parse(&source, Language::Rust).unwrap();
assert_eq!(parsed.find_symbol("FixtureLanguage").len(), 1);
assert_eq!(parsed.find_symbol("Span").len(), 1);
assert!(parsed
.find_symbol("Record")
.iter()
.any(|symbol| symbol.kind == car_ast::SymbolKind::Struct));
let record_impl = parsed
.symbols
.iter()
.find(|symbol| symbol.kind == car_ast::SymbolKind::Impl && symbol.name == "Record")
.expect("Record impl");
let methods: Vec<_> = record_impl
.children
.iter()
.map(|m| m.name.as_str())
.collect();
assert_eq!(methods, ["new", "name", "span"]);
}
#[test]
fn index_fixture_tree() {
let index = ProjectIndex::build(&fixture(""));
assert_eq!(index.find("Span").len(), 1);
let span_callers = index.callers_of("Span");
assert!(
span_callers
.iter()
.any(|reference| reference.from_file.ends_with("lib.rs")),
"fixture cross-file Span reference missing: {span_callers:?}"
);
}
#[test]
fn parse_fixture_rust_extractor() {
let source = std::fs::read_to_string(fixture("rust_extractor.rs")).unwrap();
let parsed = parse_file(&source, "rust_extractor.rs").unwrap();
assert_eq!(parsed.find_symbol("extract").len(), 1);
let helpers = parsed.find_symbol_fuzzy("extract_");
assert_eq!(helpers.len(), 3);
for symbol in parsed.all_symbols() {
assert!(
!symbol.signature.is_empty(),
"empty signature: {}",
symbol.name
);
}
}
#[test]
fn parse_fixture_edge_syntax() {
let source = r##"
pub fn r#type<T: Clone>(value: T) -> Vec<Vec<T>> {
let _raw = r#"fixture raw string"#;
vec![vec![value]]
}
"##;
let parsed = parse(source, Language::Rust).unwrap();
let function = parsed
.all_symbols()
.into_iter()
.find(|symbol| symbol.kind == car_ast::SymbolKind::Function)
.expect("raw-identifier function with nested generics");
assert!(function.signature.contains("Vec<Vec<T>>"));
}
#[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");
}