mod common;
use common::{mock_embedding, test_chunk, TestStore};
use cqs::parser::{ChunkType, Language};
use cqs::reference::{self, merge_results, ReferenceIndex};
use cqs::store::{SearchFilter, SearchResult, UnifiedResult};
use std::path::PathBuf;
fn make_code_result(name: &str, score: f32) -> SearchResult {
SearchResult {
chunk: cqs::store::ChunkSummary {
id: format!("id-{}", name),
file: PathBuf::from(format!("src/{}.rs", name)),
language: Language::Rust,
chunk_type: ChunkType::Function,
name: name.to_string(),
signature: String::new(),
content: format!("fn {}() {{}}", name),
doc: None,
line_start: 1,
line_end: 1,
parent_id: None,
parent_type_name: None,
content_hash: String::new(),
window_idx: None,
},
score,
}
}
fn insert_chunks(store: &TestStore, chunks: &[cqs::Chunk], seed: f32) {
let emb = mock_embedding(seed);
let pairs: Vec<_> = chunks.iter().map(|c| (c.clone(), emb.clone())).collect();
store.upsert_chunks_batch(&pairs, Some(12345)).unwrap();
}
#[test]
fn test_merge_results_interleaves_by_score() {
let primary = vec![
UnifiedResult::Code(make_code_result("p1", 0.95)),
UnifiedResult::Code(make_code_result("p2", 0.5)),
];
let refs = vec![
(
"ref_a".to_string(),
vec![make_code_result("r1", 0.8), make_code_result("r2", 0.6)],
),
("ref_b".to_string(), vec![make_code_result("r3", 0.7)]),
];
let merged = merge_results(primary, refs, 10);
assert_eq!(merged.len(), 5);
for w in merged.windows(2) {
assert!(
w[0].result.score() >= w[1].result.score(),
"Scores should be descending: {} >= {}",
w[0].result.score(),
w[1].result.score()
);
}
assert!(merged[0].source.is_none()); }
#[test]
fn test_merge_results_multiple_refs_tagged_correctly() {
let primary = vec![];
let refs = vec![
("alpha".to_string(), vec![make_code_result("a1", 0.9)]),
("beta".to_string(), vec![make_code_result("b1", 0.8)]),
("gamma".to_string(), vec![make_code_result("g1", 0.7)]),
];
let merged = merge_results(primary, refs, 10);
assert_eq!(merged.len(), 3);
assert_eq!(merged[0].source.as_deref(), Some("alpha"));
assert_eq!(merged[1].source.as_deref(), Some("beta"));
assert_eq!(merged[2].source.as_deref(), Some("gamma"));
}
#[test]
fn test_merge_results_truncates_strictly() {
let primary: Vec<UnifiedResult> = (0..5)
.map(|i| UnifiedResult::Code(make_code_result(&format!("p{}", i), 0.9 - i as f32 * 0.1)))
.collect();
let refs = vec![(
"ref".to_string(),
(0..5)
.map(|i| make_code_result(&format!("r{}", i), 0.85 - i as f32 * 0.1))
.collect(),
)];
let merged = merge_results(primary, refs, 3);
assert_eq!(merged.len(), 3, "Should truncate to limit=3");
}
#[test]
fn test_merge_results_empty_inputs() {
let primary: Vec<UnifiedResult> = vec![];
let refs: Vec<(String, Vec<SearchResult>)> = vec![];
let merged = merge_results(primary, refs, 10);
assert!(merged.is_empty());
}
#[test]
fn test_search_reference_applies_weight() {
let store = TestStore::new();
let c1 = test_chunk("weighted_fn", "fn weighted_fn() { test }");
insert_chunks(&store, &[c1], 1.0);
let ref_store = cqs::Store::open(&store.db_path()).unwrap();
let ref_idx = ReferenceIndex {
name: "test-ref".to_string(),
store: ref_store,
index: None,
weight: 0.7,
};
let query = mock_embedding(1.0);
let filter = SearchFilter::default();
let results = reference::search_reference(&ref_idx, &query, &filter, 10, 0.0, true).unwrap();
assert!(!results.is_empty());
for r in &results {
assert!(
r.score <= 0.71,
"Score {} should be <= weight 0.7 (with rounding)",
r.score
);
}
}
#[test]
fn test_search_reference_weight_filters_below_threshold() {
let store = TestStore::new();
let c1 = test_chunk("fn_a", "fn fn_a() { test }");
insert_chunks(&store, &[c1], 1.0);
let ref_store = cqs::Store::open(&store.db_path()).unwrap();
let ref_idx = ReferenceIndex {
name: "test-ref".to_string(),
store: ref_store,
index: None,
weight: 0.5, };
let query = mock_embedding(1.0);
let filter = SearchFilter::default();
let results = reference::search_reference(&ref_idx, &query, &filter, 10, 0.8, true).unwrap();
assert!(
results.is_empty(),
"Weighted scores below threshold should be filtered"
);
}
#[test]
fn test_search_reference_by_name_weight() {
let store = TestStore::new();
let c1 = test_chunk("lookup_fn", "fn lookup_fn() { lookup }");
insert_chunks(&store, &[c1], 1.0);
let ref_store = cqs::Store::open(&store.db_path()).unwrap();
let ref_idx = ReferenceIndex {
name: "test-ref".to_string(),
store: ref_store,
index: None,
weight: 0.6,
};
let results =
reference::search_reference_by_name(&ref_idx, "lookup_fn", 10, 0.0, true).unwrap();
assert!(!results.is_empty());
for r in &results {
assert!(
r.score <= 0.61,
"Name search score {} should be <= weight 0.6",
r.score
);
}
}
#[test]
fn test_validate_ref_name_edge_cases() {
assert!(reference::validate_ref_name("a").is_ok());
assert!(reference::validate_ref_name("123").is_ok());
assert!(reference::validate_ref_name("v1.0").is_ok());
assert!(reference::validate_ref_name("my-ref_v2").is_ok());
assert!(reference::validate_ref_name("foo..bar").is_err());
}
#[test]
fn test_search_reference_unweighted_returns_raw_scores() {
let store = TestStore::new();
let c1 = test_chunk("unweighted_fn", "fn unweighted_fn() { test }");
insert_chunks(&store, &[c1], 1.0);
let ref_store = cqs::Store::open(&store.db_path()).unwrap();
let ref_idx = ReferenceIndex {
name: "test-ref".to_string(),
store: ref_store,
index: None,
weight: 0.5, };
let query = mock_embedding(1.0);
let filter = SearchFilter::default();
let unweighted =
reference::search_reference(&ref_idx, &query, &filter, 10, 0.0, false).unwrap();
assert!(!unweighted.is_empty());
for r in &unweighted {
assert!(
r.score > 0.51,
"Unweighted score {} should be above weight 0.5 (raw score)",
r.score
);
}
}
#[test]
fn test_search_reference_unweighted_vs_weighted() {
let store = TestStore::new();
let c1 = test_chunk("compare_fn", "fn compare_fn() { compare }");
insert_chunks(&store, &[c1], 1.0);
let ref_store_w = cqs::Store::open(&store.db_path()).unwrap();
let ref_store_u = cqs::Store::open(&store.db_path()).unwrap();
let weight = 0.6;
let ref_idx_w = ReferenceIndex {
name: "weighted".to_string(),
store: ref_store_w,
index: None,
weight,
};
let ref_idx_u = ReferenceIndex {
name: "unweighted".to_string(),
store: ref_store_u,
index: None,
weight,
};
let query = mock_embedding(1.0);
let filter = SearchFilter::default();
let weighted = reference::search_reference(&ref_idx_w, &query, &filter, 10, 0.0, true).unwrap();
let unweighted =
reference::search_reference(&ref_idx_u, &query, &filter, 10, 0.0, false).unwrap();
assert!(!weighted.is_empty());
assert!(!unweighted.is_empty());
let w_score = weighted[0].score;
let u_score = unweighted[0].score;
assert!(
u_score > w_score,
"Unweighted {} should be > weighted {} (weight={})",
u_score,
w_score,
weight
);
let expected = u_score * weight;
assert!(
(w_score - expected).abs() < 0.01,
"Weighted {} should ≈ unweighted {} * weight {} = {}",
w_score,
u_score,
weight,
expected
);
}
#[test]
fn test_search_reference_by_name_unweighted() {
let store = TestStore::new();
let c1 = test_chunk("name_search_fn", "fn name_search_fn() { test }");
insert_chunks(&store, &[c1], 1.0);
let ref_store = cqs::Store::open(&store.db_path()).unwrap();
let ref_idx = ReferenceIndex {
name: "test-ref".to_string(),
store: ref_store,
index: None,
weight: 0.5,
};
let results =
reference::search_reference_by_name(&ref_idx, "name_search_fn", 10, 0.0, false).unwrap();
assert!(!results.is_empty());
for r in &results {
assert!(
r.score > 0.51,
"Unweighted name search score {} should be > weight 0.5",
r.score
);
}
}
#[test]
fn test_search_reference_by_name_unweighted_threshold() {
let store = TestStore::new();
let c1 = test_chunk("threshold_fn", "fn threshold_fn() { test }");
insert_chunks(&store, &[c1], 1.0);
let ref_store = cqs::Store::open(&store.db_path()).unwrap();
let ref_idx = ReferenceIndex {
name: "test-ref".to_string(),
store: ref_store,
index: None,
weight: 0.5,
};
let results =
reference::search_reference_by_name(&ref_idx, "threshold_fn", 10, 2.0, false).unwrap();
assert!(
results.is_empty(),
"Threshold above max score should return empty"
);
}
#[test]
fn test_merge_weighted_ref_results_rank_correctly() {
let primary = vec![UnifiedResult::Code(make_code_result("primary_fn", 0.8))];
let refs = vec![(
"ref".to_string(),
vec![make_code_result("ref_fn", 0.63)], )];
let merged = merge_results(primary, refs, 10);
assert_eq!(merged.len(), 2);
assert!(merged[0].source.is_none(), "Primary should rank first");
assert_eq!(merged[1].source.as_deref(), Some("ref"));
}