use memvdb::*;
use std::collections::HashMap;
#[test]
fn test_full_database_lifecycle() {
let mut db = CacheDB::new();
assert!(
db.create_collection("euclidean_collection".to_string(), 128, Distance::Euclidean)
.is_ok()
);
assert!(
db.create_collection("cosine_collection".to_string(), 256, Distance::Cosine)
.is_ok()
);
assert!(
db.create_collection(
"dot_product_collection".to_string(),
64,
Distance::DotProduct
)
.is_ok()
);
assert!(db.get_collection("euclidean_collection").is_some());
assert!(db.get_collection("cosine_collection").is_some());
assert!(db.get_collection("dot_product_collection").is_some());
assert!(db.delete_collection("dot_product_collection").is_ok());
assert!(db.get_collection("dot_product_collection").is_none());
}
#[test]
fn test_large_scale_embeddings() {
let mut db = CacheDB::new();
db.create_collection("large_collection".to_string(), 100, Distance::Euclidean)
.unwrap();
for i in 0..1000 {
let mut id = HashMap::new();
id.insert("id".to_string(), i.to_string());
let mut metadata = HashMap::new();
metadata.insert("category".to_string(), format!("category_{}", i % 10));
metadata.insert("index".to_string(), i.to_string());
let vector: Vec<f32> = (0..100).map(|j| (i as f32 + j as f32) / 100.0).collect();
let embedding = Embedding {
id,
vector,
metadata: Some(metadata),
};
assert!(
db.insert_into_collection("large_collection", embedding)
.is_ok()
);
}
let embeddings = db.get_embeddings("large_collection").unwrap();
assert_eq!(embeddings.len(), 1000);
let collection = db.get_collection("large_collection").unwrap();
let query_vector: Vec<f32> = (0..100).map(|i| i as f32 / 100.0).collect();
let results = collection.get_similarity(&query_vector, 10);
assert_eq!(results.len(), 10);
for i in 1..results.len() {
assert!(results[i - 1].score <= results[i].score);
}
}
#[test]
fn test_batch_operations() {
let mut db = CacheDB::new();
db.create_collection("batch_collection".to_string(), 50, Distance::Cosine)
.unwrap();
let mut embeddings = Vec::new();
for i in 0..100 {
let mut id = HashMap::new();
id.insert("batch_id".to_string(), format!("batch_{}", i));
let vector: Vec<f32> = (0..50).map(|j| ((i + j) as f32).sin()).collect();
embeddings.push(Embedding {
id,
vector,
metadata: None,
});
}
assert!(
db.update_collection("batch_collection", embeddings.clone())
.is_ok()
);
let stored_embeddings = db.get_embeddings("batch_collection").unwrap();
assert_eq!(stored_embeddings.len(), 100);
}
#[test]
fn test_error_conditions() {
let mut db = CacheDB::new();
assert!(db.delete_collection("nonexistent").is_err());
assert_eq!(
db.delete_collection("nonexistent").unwrap_err(),
Error::NotFound
);
db.create_collection("duplicate_test".to_string(), 10, Distance::Euclidean)
.unwrap();
let result = db.create_collection("duplicate_test".to_string(), 20, Distance::Cosine);
assert!(result.is_err());
db.create_collection("dimension_test".to_string(), 5, Distance::Euclidean)
.unwrap();
let mut id = HashMap::new();
id.insert("id".to_string(), "test".to_string());
let wrong_dimension_embedding = Embedding {
id,
vector: vec![1.0, 2.0, 3.0], metadata: None,
};
let result = db.insert_into_collection("dimension_test", wrong_dimension_embedding);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::DimensionMismatch);
}
#[test]
fn test_duplicate_embedding_detection() {
let mut db = CacheDB::new();
db.create_collection(
"duplicate_embedding_test".to_string(),
3,
Distance::Euclidean,
)
.unwrap();
let mut id = HashMap::new();
id.insert("unique_key".to_string(), "same_id".to_string());
let embedding1 = Embedding {
id: id.clone(),
vector: vec![1.0, 2.0, 3.0],
metadata: None,
};
let embedding2 = Embedding {
id: id.clone(),
vector: vec![4.0, 5.0, 6.0],
metadata: None,
};
assert!(
db.insert_into_collection("duplicate_embedding_test", embedding1)
.is_ok()
);
let result = db.insert_into_collection("duplicate_embedding_test", embedding2);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::EmbeddingUniqueViolation);
}
#[test]
fn test_similarity_search_accuracy() {
let mut db = CacheDB::new();
db.create_collection("accuracy_test".to_string(), 4, Distance::Euclidean)
.unwrap();
let test_vectors = vec![
(vec![1.0, 0.0, 0.0, 0.0], "vector1"),
(vec![0.0, 1.0, 0.0, 0.0], "vector2"),
(vec![0.0, 0.0, 1.0, 0.0], "vector3"),
(vec![0.0, 0.0, 0.0, 1.0], "vector4"),
(vec![0.5, 0.5, 0.0, 0.0], "vector5"), ];
for (vector, name) in test_vectors {
let mut id = HashMap::new();
id.insert("name".to_string(), name.to_string());
let embedding = Embedding {
id,
vector,
metadata: None,
};
db.insert_into_collection("accuracy_test", embedding)
.unwrap();
}
let collection = db.get_collection("accuracy_test").unwrap();
let query = vec![0.9, 0.1, 0.0, 0.0];
let results = collection.get_similarity(&query, 3);
assert_eq!(results.len(), 3);
println!("Query: {:?}", query);
for (i, result) in results.iter().enumerate() {
let name = result.embedding.id.get("name").unwrap();
println!(
"Result {}: {} with score {}, vector: {:?}",
i, name, result.score, result.embedding.vector
);
}
let closest_name = results[0].embedding.id.get("name").unwrap();
assert!(
["vector1", "vector2", "vector3", "vector4", "vector5"].contains(&closest_name.as_str())
);
}
#[test]
fn test_different_distance_metrics() {
let mut db = CacheDB::new();
db.create_collection("euclidean_test".to_string(), 3, Distance::Euclidean)
.unwrap();
db.create_collection("cosine_test".to_string(), 3, Distance::Cosine)
.unwrap();
db.create_collection("dot_product_test".to_string(), 3, Distance::DotProduct)
.unwrap();
let vectors = vec![
vec![1.0, 0.0, 0.0],
vec![0.0, 1.0, 0.0],
vec![0.0, 0.0, 1.0],
vec![1.0, 1.0, 0.0],
];
for collection_name in &["euclidean_test", "cosine_test", "dot_product_test"] {
for (i, vector) in vectors.iter().enumerate() {
let mut id = HashMap::new();
id.insert("id".to_string(), format!("vec_{}", i));
let embedding = Embedding {
id,
vector: vector.clone(),
metadata: None,
};
db.insert_into_collection(collection_name, embedding)
.unwrap();
}
}
let query = vec![0.5, 0.5, 0.0];
let euclidean_results = db
.get_collection("euclidean_test")
.unwrap()
.get_similarity(&query, 4);
let cosine_results = db
.get_collection("cosine_test")
.unwrap()
.get_similarity(&query, 4);
let dot_results = db
.get_collection("dot_product_test")
.unwrap()
.get_similarity(&query, 4);
assert_eq!(euclidean_results.len(), 4);
assert_eq!(cosine_results.len(), 4);
assert_eq!(dot_results.len(), 4);
}
#[test]
fn test_metadata_handling() {
let mut db = CacheDB::new();
db.create_collection("metadata_test".to_string(), 2, Distance::Euclidean)
.unwrap();
let mut id = HashMap::new();
id.insert("document_id".to_string(), "doc_123".to_string());
id.insert("user_id".to_string(), "user_456".to_string());
let mut metadata = HashMap::new();
metadata.insert("title".to_string(), "Test Document".to_string());
metadata.insert("author".to_string(), "Test Author".to_string());
metadata.insert("date".to_string(), "2024-01-01".to_string());
metadata.insert("category".to_string(), "Research".to_string());
metadata.insert("tags".to_string(), "test,document,research".to_string());
let embedding = Embedding {
id,
vector: vec![0.1, 0.2],
metadata: Some(metadata.clone()),
};
db.insert_into_collection("metadata_test", embedding)
.unwrap();
let embeddings = db.get_embeddings("metadata_test").unwrap();
assert_eq!(embeddings.len(), 1);
let retrieved_embedding = &embeddings[0];
assert!(retrieved_embedding.metadata.is_some());
let retrieved_metadata = retrieved_embedding.metadata.as_ref().unwrap();
assert_eq!(
retrieved_metadata.get("title"),
Some(&"Test Document".to_string())
);
assert_eq!(
retrieved_metadata.get("author"),
Some(&"Test Author".to_string())
);
assert_eq!(retrieved_metadata.len(), metadata.len());
}
#[test]
fn test_empty_collections() {
let mut db = CacheDB::new();
db.create_collection("empty_test".to_string(), 10, Distance::Euclidean)
.unwrap();
let embeddings = db.get_embeddings("empty_test");
assert!(embeddings.is_some());
assert_eq!(embeddings.unwrap().len(), 0);
let collection = db.get_collection("empty_test").unwrap();
let results = collection.get_similarity(&vec![1.0; 10], 5);
assert_eq!(results.len(), 0);
}
#[test]
fn test_vector_normalization() {
let test_cases = vec![
(vec![3.0, 4.0], vec![0.6, 0.8]), (vec![1.0, 1.0, 1.0], vec![1.0 / 3.0_f32.sqrt(); 3]), (vec![0.0, 0.0, 0.0], vec![0.0, 0.0, 0.0]), (vec![5.0], vec![1.0]), ];
for (input, expected) in test_cases {
let normalized = normalize(&input);
if input.iter().all(|&x| x == 0.0) {
assert_eq!(normalized, input);
} else {
let magnitude: f32 = normalized.iter().map(|x| x.powi(2)).sum::<f32>().sqrt();
assert!(
(magnitude - 1.0).abs() < 1e-6,
"Magnitude should be 1, got {}",
magnitude
);
for (norm, exp) in normalized.iter().zip(expected.iter()) {
assert!((norm - exp).abs() < 1e-6, "Expected {}, got {}", exp, norm);
}
}
}
}
#[test]
fn test_score_index_heap_behavior() {
use std::collections::BinaryHeap;
let mut heap = BinaryHeap::new();
heap.push(ScoreIndex {
score: 0.8,
index: 0,
});
heap.push(ScoreIndex {
score: 0.2,
index: 1,
});
heap.push(ScoreIndex {
score: 0.5,
index: 2,
});
heap.push(ScoreIndex {
score: 0.1,
index: 3,
});
heap.push(ScoreIndex {
score: 0.9,
index: 4,
});
let first = heap.pop().unwrap();
assert_eq!(first.score, 0.1);
assert_eq!(first.index, 3);
let second = heap.pop().unwrap();
assert_eq!(second.score, 0.2);
assert_eq!(second.index, 1);
let third = heap.pop().unwrap();
assert_eq!(third.score, 0.5);
assert_eq!(third.index, 2);
}
#[test]
fn test_concurrent_operations() {
use std::sync::{Arc, Mutex};
use std::thread;
let db = Arc::new(Mutex::new(CacheDB::new()));
{
let mut db_lock = db.lock().unwrap();
db_lock
.create_collection("concurrent_test".to_string(), 3, Distance::Euclidean)
.unwrap();
}
let mut handles = vec![];
for i in 0..10 {
let db_clone = Arc::clone(&db);
let handle = thread::spawn(move || {
let mut id = HashMap::new();
id.insert("thread_id".to_string(), format!("thread_{}", i));
let embedding = Embedding {
id,
vector: vec![i as f32, (i * 2) as f32, (i * 3) as f32],
metadata: None,
};
let mut db_lock = db_clone.lock().unwrap();
db_lock
.insert_into_collection("concurrent_test", embedding)
.unwrap();
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let db_lock = db.lock().unwrap();
let embeddings = db_lock.get_embeddings("concurrent_test").unwrap();
assert_eq!(embeddings.len(), 10);
}