use crate::core::{Kind, RepoIdentity, Symbol};
use crate::store::Store;
fn sym(name: &str) -> Symbol {
Symbol {
name: name.into(),
kind: Kind::Function,
language: "rust".into(),
file: "a.rs".into(),
line: 1,
end_line: 1,
parent: None,
visibility: None,
}
}
#[test]
fn exact_match_survives_a_flooded_first_char_bucket() {
let mut store = Store::open_in_memory().unwrap();
let repo = store
.upsert_repository(&RepoIdentity::local("/tmp/x"), None)
.unwrap();
let mut syms: Vec<Symbol> = (1..=50).map(|i| sym(&format!("manaa{i:03}"))).collect();
syms.push(sym("mango"));
store
.replace_file_symbols(repo, "a.rs", "rust", None, "h", &syms)
.unwrap();
let cands = store
.search_candidates("mango", 5, false, None, None)
.unwrap();
assert!(
cands.iter().any(|c| c.name == "mango"),
"exact match dropped by the cap; got {:?}",
cands.iter().map(|c| &c.name).collect::<Vec<_>>()
);
}
#[test]
fn a_strong_match_short_circuits_the_broad_fuzzy_layers() {
let mut store = Store::open_in_memory().unwrap();
let repo = store
.upsert_repository(&RepoIdentity::local("/tmp/x"), None)
.unwrap();
store
.replace_file_symbols(
repo,
"a.rs",
"rust",
None,
"h",
&[sym("User"), sym("Peruser")],
)
.unwrap();
let strong_only = store
.search_candidates("user", 50, false, None, None)
.unwrap();
assert!(strong_only.iter().any(|c| c.name == "User"), "prefix kept");
assert!(
!strong_only.iter().any(|c| c.name == "Peruser"),
"fuzzy-only candidate skipped when a strong match exists"
);
let forced = store
.search_candidates("user", 50, true, None, None)
.unwrap();
assert!(
forced.iter().any(|c| c.name == "Peruser"),
"force_fuzzy still recalls the fuzzy candidate"
);
}
#[test]
fn a_repo_scoped_cap_is_filled_by_that_repo_alone() {
let mut store = Store::open_in_memory().unwrap();
let here = store
.upsert_repository(&RepoIdentity::local("/tmp/here"), None)
.unwrap();
let other = store
.upsert_repository(&RepoIdentity::local("/tmp/other"), None)
.unwrap();
let flood: Vec<Symbol> = (1..=50)
.map(|i| sym(&format!("aaa_widget{i:03}")))
.collect();
store
.replace_file_symbols(other, "a.rs", "rust", None, "h", &flood)
.unwrap();
store
.replace_file_symbols(here, "a.rs", "rust", None, "h", &[sym("MyWidget")])
.unwrap();
let cands = store
.search_candidates("widget", 5, false, Some(here), None)
.unwrap();
let names: Vec<&str> = cands.iter().map(|c| c.name.as_str()).collect();
assert_eq!(
names,
["MyWidget"],
"only this repo's rows, and its match fits"
);
}
#[test]
fn a_filtered_net_reaches_past_rows_that_cannot_match() {
let mut store = Store::open_in_memory().unwrap();
let repo = store
.upsert_repository(&RepoIdentity::local("/tmp/x"), None)
.unwrap();
let mut syms: Vec<Symbol> = (1..=10).map(|i| sym(&format!("maa{i:03}"))).collect();
syms.push(sym("mongo"));
store
.replace_file_symbols(repo, "a.rs", "rust", None, "h", &syms)
.unwrap();
let names = |filter: Option<crate::store::CandidateFilter>| -> Vec<String> {
store
.search_candidates("mgo", 5, false, None, filter)
.unwrap()
.into_iter()
.map(|c| c.name)
.collect()
};
assert!(
!names(None).contains(&"mongo".to_string()),
"the cap binds unfiltered"
);
let in_order = Box::new(|name: &str, _: &str, _: &str| name.contains('o'));
assert_eq!(names(Some(in_order)), vec!["mongo".to_string()]);
}