#![cfg(test)]
use std::path::PathBuf;
use leindex::parse::languages::RustParser;
use leindex::parse::traits::CodeIntelligence;
fn repo_root() -> PathBuf {
let dir = env!("CARGO_MANIFEST_DIR");
PathBuf::from(dir)
}
fn read_fixture(rel: &str) -> Vec<u8> {
let path = repo_root().join(rel);
std::fs::read(&path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()))
}
#[test]
fn test_askpass_parses_struct() {
let source = read_fixture("tests/fixtures/rust/askpass.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let askpass = signatures
.iter()
.find(|s| s.name == "Askpass" && s.return_type.as_deref() == Some("struct"))
.unwrap_or_else(|| panic!("expected Askpass struct, got: {signatures:?}"));
assert!(
askpass.byte_range.0 < askpass.byte_range.1 && askpass.byte_range.1 <= source.len(),
"invalid byte_range for Askpass: {:?}",
askpass.byte_range
);
}
#[test]
fn test_askpass_parses_methods_with_qualified_names() {
let source = read_fixture("tests/fixtures/rust/askpass.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let new_method = signatures
.iter()
.find(|s| s.qualified_name == "Askpass::new")
.unwrap_or_else(|| panic!("expected Askpass::new, got: {signatures:?}"));
assert!(
new_method.is_method,
"Askpass::new should be flagged as a method (impl function)"
);
assert!(
new_method.byte_range.0 < new_method.byte_range.1,
"invalid byte_range for Askpass::new: {:?}",
new_method.byte_range
);
let path_method = signatures
.iter()
.find(|s| s.qualified_name == "Askpass::path")
.unwrap_or_else(|| panic!("expected Askpass::path, got: {signatures:?}"));
assert!(
path_method.is_method,
"Askpass::path should be flagged as a method (impl function)"
);
assert!(
path_method.byte_range.0 < path_method.byte_range.1,
"invalid byte_range for Askpass::path: {:?}",
path_method.byte_range
);
}
#[test]
fn test_askpass_parses_standalone_main() {
let source = read_fixture("tests/fixtures/rust/askpass.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let main_fn = signatures
.iter()
.find(|s| s.name == "main" && s.return_type.as_deref() != Some("struct"))
.unwrap_or_else(|| panic!("expected standalone main function, got: {signatures:?}"));
assert!(
!main_fn.is_method,
"main should NOT be flagged as a method (standalone function)"
);
assert!(
main_fn.byte_range.0 < main_fn.byte_range.1,
"invalid byte_range for main: {:?}",
main_fn.byte_range
);
}
#[test]
fn test_nested_cli_parses_cli_struct() {
let source = read_fixture("tests/fixtures/rust/nested_cli.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let cli = signatures
.iter()
.find(|s| s.name == "Cli" && s.return_type.as_deref() == Some("struct"))
.unwrap_or_else(|| panic!("expected Cli struct, got: {signatures:?}"));
assert!(
cli.byte_range.0 < cli.byte_range.1,
"invalid byte_range for Cli: {:?}",
cli.byte_range
);
}
#[test]
fn test_nested_cli_parses_commands_enum_and_variants() {
let source = read_fixture("tests/fixtures/rust/nested_cli.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let commands = signatures
.iter()
.find(|s| s.name == "Commands" && s.return_type.as_deref() == Some("enum"))
.unwrap_or_else(|| panic!("expected Commands enum, got: {signatures:?}"));
assert!(
commands.byte_range.0 < commands.byte_range.1,
"invalid byte_range for Commands: {:?}",
commands.byte_range
);
let install = signatures
.iter()
.find(|s| {
s.qualified_name == "Commands::Install"
&& s.return_type.as_deref() == Some("enum_variant")
})
.unwrap_or_else(|| panic!("expected Commands::Install enum_variant, got: {signatures:?}"));
assert!(
install.byte_range.0 < install.byte_range.1,
"invalid byte_range for Commands::Install: {:?}",
install.byte_range
);
assert!(
signatures
.iter()
.any(|s| s.qualified_name == "Commands::Uninstall"),
"expected Commands::Uninstall variant"
);
assert!(
signatures
.iter()
.any(|s| s.qualified_name == "Commands::Status"),
"expected Commands::Status variant"
);
}
#[test]
fn test_nested_cli_parses_config_module_and_config_struct() {
let source = read_fixture("tests/fixtures/rust/nested_cli.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let config_mod = signatures
.iter()
.find(|s| s.name == "config" && s.return_type.as_deref() == Some("module"))
.unwrap_or_else(|| panic!("expected config module, got: {signatures:?}"));
assert!(
config_mod.byte_range.0 < config_mod.byte_range.1,
"invalid byte_range for config module: {:?}",
config_mod.byte_range
);
let config = signatures
.iter()
.find(|s| s.qualified_name == "config::Config")
.unwrap_or_else(|| {
panic!("expected config::Config struct with qualified_name, got: {signatures:?}")
});
assert_eq!(
config.return_type.as_deref(),
Some("struct"),
"config::Config should have return_type=struct"
);
assert!(
config.byte_range.0 < config.byte_range.1,
"invalid byte_range for config::Config: {:?}",
config.byte_range
);
}
#[test]
fn test_nested_cli_associated_functions_without_self_are_methods() {
let source = read_fixture("tests/fixtures/rust/nested_cli.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let from_env = signatures
.iter()
.find(|s| s.qualified_name == "Cli::from_env")
.unwrap_or_else(|| panic!("expected Cli::from_env, got: {signatures:?}"));
assert!(
from_env.is_method,
"Cli::from_env (associated function without self) should be flagged is_method=true"
);
let config_new = signatures
.iter()
.find(|s| s.qualified_name == "config::Config::new")
.unwrap_or_else(|| panic!("expected config::Config::new, got: {signatures:?}"));
assert!(
config_new.is_method,
"config::Config::new (associated function without self) should be flagged is_method=true"
);
}
#[test]
fn test_flows_parses_registry_record_struct() {
let source = read_fixture("tests/fixtures/rust/flows.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let record = signatures
.iter()
.find(|s| s.name == "RegistryRecord" && s.return_type.as_deref() == Some("struct"))
.unwrap_or_else(|| panic!("expected RegistryRecord struct, got: {signatures:?}"));
assert!(
record.byte_range.0 < record.byte_range.1,
"invalid byte_range for RegistryRecord: {:?}",
record.byte_range
);
}
#[test]
fn test_flows_parses_standalone_functions() {
let source = read_fixture("tests/fixtures/rust/flows.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let run_installation = signatures
.iter()
.find(|s| s.name == "run_installation" && !s.is_method)
.unwrap_or_else(|| panic!("expected run_installation function, got: {signatures:?}"));
assert!(
run_installation.byte_range.0 < run_installation.byte_range.1,
"invalid byte_range for run_installation: {:?}",
run_installation.byte_range
);
let verify_registry = signatures
.iter()
.find(|s| s.name == "verify_registry" && !s.is_method)
.unwrap_or_else(|| panic!("expected verify_registry function, got: {signatures:?}"));
assert!(
verify_registry.byte_range.0 < verify_registry.byte_range.1,
"invalid byte_range for verify_registry: {:?}",
verify_registry.byte_range
);
let detect_arch = signatures
.iter()
.find(|s| s.name == "detect_arch" && !s.is_method)
.unwrap_or_else(|| panic!("expected detect_arch function, got: {signatures:?}"));
assert!(
detect_arch.byte_range.0 < detect_arch.byte_range.1,
"invalid byte_range for detect_arch: {:?}",
detect_arch.byte_range
);
}
#[test]
fn test_flows_run_installation_calls_verify_registry_and_detect_arch() {
let source = read_fixture("tests/fixtures/rust/flows.rs");
let parser = RustParser::new();
let signatures = parser.get_signatures(&source).unwrap();
let run_installation = signatures
.iter()
.find(|s| s.name == "run_installation")
.unwrap_or_else(|| panic!("expected run_installation function, got: {signatures:?}"));
assert!(
run_installation
.calls
.iter()
.any(|c| c.contains("verify_registry")),
"run_installation should call verify_registry, calls: {:?}",
run_installation.calls
);
assert!(
run_installation
.calls
.iter()
.any(|c| c.contains("detect_arch")),
"run_installation should call detect_arch, calls: {:?}",
run_installation.calls
);
}