use engram::vector::{VectorFilter, VectorStore};
use engram::vector_embedded::EmbeddedVectorStore;
use uuid::Uuid;
fn random_embedding(dims: usize) -> Vec<f32> {
(0..dims)
.map(|i| ((i * 7 + 3) % 100) as f32 / 100.0)
.collect()
}
fn similar_embedding(base: &[f32], noise: f32) -> Vec<f32> {
base.iter().map(|v| v + noise).collect()
}
#[tokio::test]
async fn upsert_and_search_returns_matches() {
let store = EmbeddedVectorStore::new(128);
let filter = VectorFilter::default();
let base = random_embedding(128);
let similar = similar_embedding(&base, 0.01);
let different: Vec<f32> = (0..128)
.map(|i| ((i * 3 + 97) % 100) as f32 / 100.0)
.collect();
let id_base = Uuid::new_v4();
let id_similar = Uuid::new_v4();
let id_different = Uuid::new_v4();
store
.upsert(id_base, base.clone(), serde_json::json!({"label": "base"}))
.await
.expect("upsert base");
store
.upsert(id_similar, similar, serde_json::json!({"label": "similar"}))
.await
.expect("upsert similar");
store
.upsert(
id_different,
different,
serde_json::json!({"label": "different"}),
)
.await
.expect("upsert different");
let results = store.search(&base, &filter, 3).await.expect("search");
assert_eq!(results.len(), 3, "should return 3 results");
assert_eq!(results[0].id, id_base, "exact match should be ranked first");
assert_eq!(
results[1].id, id_similar,
"similar vector should be ranked second"
);
assert!(
results[0].score >= results[1].score,
"scores must be non-increasing"
);
assert!(
results[1].score >= results[2].score,
"scores must be non-increasing"
);
}
#[tokio::test]
async fn delete_removes_from_index() {
let store = EmbeddedVectorStore::new(128);
let filter = VectorFilter::default();
let embedding = random_embedding(128);
let id = Uuid::new_v4();
store
.upsert(id, embedding.clone(), serde_json::json!({}))
.await
.expect("upsert");
let before = store
.search(&embedding, &filter, 10)
.await
.expect("search before delete");
assert!(
before.iter().any(|m| m.id == id),
"entry should be present before deletion"
);
store.delete(id).await.expect("delete");
let after = store
.search(&embedding, &filter, 10)
.await
.expect("search after delete");
assert!(
!after.iter().any(|m| m.id == id),
"entry must not appear after deletion"
);
}
#[tokio::test]
async fn search_respects_top_k() {
let store = EmbeddedVectorStore::new(128);
let filter = VectorFilter::default();
let query = random_embedding(128);
for i in 0..10_usize {
let id = Uuid::new_v4();
let embedding: Vec<f32> = (0..128)
.map(|j| ((j * 7 + i * 13 + 3) % 100) as f32 / 100.0)
.collect();
store
.upsert(id, embedding, serde_json::json!({"index": i}))
.await
.expect("upsert");
}
let results = store.search(&query, &filter, 3).await.expect("search");
assert!(
results.len() <= 3,
"top_k=3 must return at most 3 results, got {}",
results.len()
);
}