use crate::vector::hnsw::HnswGraph;
use crate::vector::Metric;
fn base_config() -> crate::vector::hnsw::HnswConfig {
crate::vector::hnsw::HnswConfig::default()
.with_dimension(2)
.with_metric(Metric::L2)
.with_m(8)
.with_ef_construction(32)
}
fn make_graph() -> HnswGraph {
HnswGraph::new(base_config()).expect("設定が正しいので初期化に失敗しない")
}
#[test]
fn insert_and_search_basic_flow() {
let mut graph = make_graph();
graph.insert(b"a", &[0.0, 0.0], b"ma").unwrap();
graph.insert(b"b", &[1.0, 0.0], b"mb").unwrap();
graph.insert(b"c", &[0.0, 2.0], b"mc").unwrap();
let (results, stats) = graph.search(&[1.0, 0.1], 2, 4).unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].key, b"b");
assert_eq!(results[1].key, b"a");
assert!(stats.nodes_visited > 0);
}
#[test]
fn ef_search_is_auto_corrected() {
let mut graph = make_graph();
for i in 0..5u8 {
let key = [b'k', i];
graph
.insert(&key, &[i as f32, 0.0], &[i])
.expect("挿入に失敗しない");
}
let (results, _stats) = graph.search(&[0.0, 0.0], 3, 1).unwrap();
assert_eq!(results.len(), 3);
}
#[test]
fn deleted_nodes_are_skipped_in_results() {
let mut graph = make_graph();
graph.insert(b"a", &[0.0, 0.0], b"ma").unwrap();
graph.insert(b"b", &[0.1, 0.0], b"mb").unwrap();
graph.delete(b"a").unwrap();
let (results, _) = graph.search(&[0.0, 0.0], 2, 8).unwrap();
assert!(results.iter().all(|r| r.key != b"a"));
assert_eq!(graph.deleted_count, 1);
}
#[test]
fn tie_breaks_by_key_order() {
let mut graph = make_graph();
graph.insert(b"alpha", &[1.0, 1.0], b"ma").unwrap();
graph.insert(b"bravo", &[1.0, 1.0], b"mb").unwrap();
let (results, _) = graph.search(&[1.0, 1.0], 2, 10).unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].key, b"alpha");
assert_eq!(results[1].key, b"bravo");
}
#[test]
fn returns_less_than_k_when_insufficient() {
let mut graph = make_graph();
graph.insert(b"solo", &[0.0, 0.0], b"m").unwrap();
let (results, _) = graph.search(&[0.0, 0.0], 3, 5).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].key, b"solo");
}
#[test]
fn delete_marks_node_and_compact_removes_it() {
let mut graph = make_graph();
graph.insert(b"a", &[0.0, 0.0], b"ma").unwrap();
let removed_id = graph.insert(b"b", &[1.0, 0.0], b"mb").unwrap();
graph.insert(b"c", &[2.0, 0.0], b"mc").unwrap();
assert!(graph.delete(b"b").unwrap());
assert_eq!(graph.deleted_count, 1);
let compaction = graph.compact().unwrap();
assert_eq!(compaction.vectors_removed, 1);
assert_eq!(graph.deleted_count, 0);
assert!(!graph.key_to_node.contains_key(b"b".as_slice()));
assert!(graph.nodes.get(removed_id as usize).is_some());
assert!(graph.nodes[removed_id as usize].is_none());
let stats = graph.stats();
assert_eq!(stats.node_count, 2);
assert_eq!(stats.deleted_count, 0);
}