#![allow(warnings)]
#![allow(clippy::unwrap_used, clippy::expect_used)]
#![allow(clippy::absurd_extreme_comparisons, clippy::nonminimal_bool)]
use std::collections::HashMap;
use tracing::{debug, error, info, warn};
use vectorizer_sdk::*;
async fn gated_routes_available(client: &VectorizerClient) -> bool {
client.health_check().await.is_ok() && client.list_collections().await.is_ok()
}
#[tokio::test]
async fn test_client_creation() {
let client = VectorizerClient::new_default();
assert!(client.is_ok());
let client = VectorizerClient::new_with_url("http://localhost:15002");
assert!(client.is_ok());
let client = VectorizerClient::new_with_api_key("http://localhost:15002", "test-key");
assert!(client.is_ok());
}
#[tokio::test]
async fn test_health_check() {
let client = VectorizerClient::new_default().unwrap();
let Ok(health) = client.health_check().await else {
return;
};
if !health.version.starts_with("3.") {
return;
}
assert_eq!(health.status, "healthy");
assert!(!health.version.is_empty());
assert!(!health.timestamp.is_empty());
}
#[tokio::test]
async fn test_list_collections() {
let client = VectorizerClient::new_default().unwrap();
if !gated_routes_available(&client).await {
return;
}
match client.list_collections().await {
Ok(collections) => {
assert!(!collections.is_empty());
for collection in collections {
assert!(!collection.name.is_empty());
assert!(collection.dimension > 0);
if let Some(metric) = &collection.metric {
assert!(!metric.is_empty());
}
}
}
Err(e) => {
panic!("List collections failed: {}", e);
}
}
}
#[tokio::test]
async fn test_create_collection() {
let client = VectorizerClient::new_default().unwrap();
if !gated_routes_available(&client).await {
return;
}
let collection_name = format!("test_collection_{}", uuid::Uuid::new_v4());
let _ = client.delete_collection(&collection_name).await;
match client
.create_collection(&collection_name, 384, Some(SimilarityMetric::Cosine))
.await
{
Ok(info) => {
assert_eq!(info.name, collection_name);
assert_eq!(info.dimension, 384);
assert_eq!(info.metric.to_lowercase(), "cosine");
if let Some(ref status) = info.indexing_status {
assert!(!status.status.is_empty());
}
}
Err(e) => {
panic!("Create collection failed: {}", e);
}
}
let _ = client.delete_collection(&collection_name).await;
}
#[tokio::test]
async fn test_insert_texts() {
let client = VectorizerClient::new_default().unwrap();
let collection_name = format!("test_insert_{}", uuid::Uuid::new_v4());
let create_result = client.create_collection(&collection_name, 384, None).await;
if create_result.is_err() {
return;
}
let texts = vec![
BatchTextRequest {
id: "test_doc_1".to_string(),
text: "This is a test document for vectorization.".to_string(),
metadata: Some({
let mut meta = HashMap::new();
meta.insert("category".to_string(), "test".to_string());
meta.insert("language".to_string(), "english".to_string());
meta
}),
},
BatchTextRequest {
id: "test_doc_2".to_string(),
text: "Machine learning and artificial intelligence are fascinating topics."
.to_string(),
metadata: Some({
let mut meta = HashMap::new();
meta.insert("category".to_string(), "ai".to_string());
meta.insert("language".to_string(), "english".to_string());
meta
}),
},
];
match client.insert_texts(&collection_name, texts).await {
Ok(response) => {
assert!(response.success);
assert_eq!(response.collection, collection_name);
assert_eq!(response.operation, "insert");
assert_eq!(response.total_operations, 2);
assert_eq!(response.successful_operations, 2);
assert_eq!(response.failed_operations, 0);
}
Err(e) => {
tracing::info!("Insert texts failed (expected in test environment): {}", e);
}
}
let _ = client.delete_collection(&collection_name).await;
}
#[tokio::test]
async fn test_search_vectors() {
let client = VectorizerClient::new_default().unwrap();
let collection_name = format!("test_search_{}", uuid::Uuid::new_v4());
let create_result = client.create_collection(&collection_name, 384, None).await;
if create_result.is_err() {
return;
}
let texts = vec![
BatchTextRequest {
id: "search_doc_1".to_string(),
text: "Artificial intelligence and machine learning are transforming technology."
.to_string(),
metadata: None,
},
BatchTextRequest {
id: "search_doc_2".to_string(),
text: "Natural language processing enables computers to understand human language."
.to_string(),
metadata: None,
},
];
let _ = client.insert_texts(&collection_name, texts).await;
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
match client
.search_vectors(
&collection_name,
"artificial intelligence",
Some(5),
Some(0.1),
)
.await
{
Ok(response) => {
assert!(!response.results.is_empty());
assert!(response.results.len() > 0);
for result in response.results {
assert!(!result.id.is_empty());
assert!(result.score >= 0.0 && result.score <= 1.0);
}
}
Err(e) => {
tracing::info!(
"Search vectors failed (expected in test environment): {}",
e
);
}
}
let _ = client.delete_collection(&collection_name).await;
}
#[tokio::test]
async fn test_get_vector() {
let client = VectorizerClient::new_default().unwrap();
let collection_name = format!("test_get_vector_{}", uuid::Uuid::new_v4());
let create_result = client.create_collection(&collection_name, 384, None).await;
if create_result.is_err() {
return;
}
let texts = vec![BatchTextRequest {
id: "vector_doc_1".to_string(),
text: "This is a test document for vector retrieval.".to_string(),
metadata: None,
}];
let _ = client.insert_texts(&collection_name, texts).await;
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
match client.get_vector(&collection_name, "vector_doc_1").await {
Ok(vector) => {
assert_eq!(vector.id, "vector_doc_1");
assert_eq!(vector.data.len(), 384);
assert!(vector.data.iter().all(|&x| x.is_finite()));
}
Err(e) => {
tracing::info!("Get vector failed (expected in test environment): {}", e);
}
}
let _ = client.delete_collection(&collection_name).await;
}
#[tokio::test]
async fn test_get_collection_info() {
let client = VectorizerClient::new_default().unwrap();
let collection_name = format!("test_info_{}", uuid::Uuid::new_v4());
let create_result = client.create_collection(&collection_name, 384, None).await;
if create_result.is_err() {
return;
}
match client.get_collection_info(&collection_name).await {
Ok(info) => {
assert_eq!(info.name, collection_name);
assert_eq!(info.dimension, 384);
assert_eq!(info.metric.to_lowercase(), "cosine");
if let Some(ref status) = info.indexing_status {
assert!(!status.status.is_empty());
}
}
Err(e) => {
panic!("Get collection info failed: {}", e);
}
}
let _ = client.delete_collection(&collection_name).await;
}
#[tokio::test]
async fn test_embed_text() {
let client = VectorizerClient::new_default().unwrap();
match client
.embed_text("This is a test text for embedding generation", None)
.await
{
Ok(response) => {
assert_eq!(
response.text,
"This is a test text for embedding generation"
);
assert!(!response.model.is_empty());
assert!(!response.provider.is_empty());
assert!(response.dimension > 0);
assert_eq!(response.embedding.len(), response.dimension);
}
Err(e) => {
tracing::info!("Embed text failed (expected in test environment): {}", e);
}
}
}
#[tokio::test]
async fn test_delete_collection() {
let client = VectorizerClient::new_default().unwrap();
if !gated_routes_available(&client).await {
return;
}
let collection_name = format!("test_delete_{}", uuid::Uuid::new_v4());
let _ = client.create_collection(&collection_name, 384, None).await;
let collections = client.list_collections().await.unwrap();
if !collections.iter().any(|c| c.name == collection_name) {
return;
}
match client.delete_collection(&collection_name).await {
Ok(_) => {
let collections = client.list_collections().await.unwrap();
assert!(!collections.iter().any(|c| c.name == collection_name));
}
Err(e) => {
panic!("Delete collection failed: {}", e);
}
}
}
#[tokio::test]
async fn test_error_handling() {
let client = VectorizerClient::new_default().unwrap();
match client.get_collection_info("non_existent_collection").await {
Ok(_) => {
panic!("Should have failed for non-existent collection");
}
Err(e) => {
assert!(matches!(e, VectorizerError::Server { message: _ }));
}
}
match client.create_collection("", 384, None).await {
Ok(_) => {
panic!("Should have failed for empty collection name");
}
Err(e) => {
assert!(
matches!(e, VectorizerError::Validation { message: _ })
|| matches!(e, VectorizerError::Server { message: _ })
);
}
}
}
#[tokio::test]
async fn test_serialization() {
let client = VectorizerClient::new_default().unwrap();
if !gated_routes_available(&client).await {
return;
}
let health = client.health_check().await.unwrap();
let health_json = serde_json::to_string(&health).unwrap();
let health_deserialized: HealthStatus = serde_json::from_str(&health_json).unwrap();
assert_eq!(health.status, health_deserialized.status);
let collections = client.list_collections().await.unwrap();
let collections_json = serde_json::to_string(&collections).unwrap();
let collections_deserialized: Vec<CollectionInfo> =
serde_json::from_str(&collections_json).unwrap();
assert_eq!(collections.len(), collections_deserialized.len());
}
#[tokio::test]
async fn test_batch_operations() {
let client = VectorizerClient::new_default().unwrap();
let collection_name = format!("test_batch_{}", uuid::Uuid::new_v4());
let create_result = client.create_collection(&collection_name, 384, None).await;
if create_result.is_err() {
return;
}
let texts = (1..=10)
.map(|i| BatchTextRequest {
id: format!("batch_doc_{}", i),
text: format!(
"This is batch document number {} for testing batch operations.",
i
),
metadata: Some({
let mut meta = HashMap::new();
meta.insert("batch_id".to_string(), i.to_string());
meta.insert("test_type".to_string(), "batch".to_string());
meta
}),
})
.collect();
match client.insert_texts(&collection_name, texts).await {
Ok(response) => {
assert!(response.success);
assert_eq!(response.total_operations, 10);
assert_eq!(response.successful_operations, 10);
assert_eq!(response.failed_operations, 0);
}
Err(e) => {
tracing::info!("Batch insert failed (expected in test environment): {}", e);
}
}
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
match client
.search_vectors(&collection_name, "batch document", Some(10), None)
.await
{
Ok(response) => {
assert!(!response.results.is_empty());
assert!(response.results.len() <= 10);
}
Err(e) => {
tracing::info!("Batch search failed (expected in test environment): {}", e);
}
}
let _ = client.delete_collection(&collection_name).await;
}
#[tokio::test]
async fn test_performance() {
let client = VectorizerClient::new_default().unwrap();
let collection_name = format!("test_perf_{}", uuid::Uuid::new_v4());
let create_result = client.create_collection(&collection_name, 384, None).await;
if create_result.is_err() {
return;
}
let start_time = std::time::Instant::now();
let texts = (1..=50)
.map(|i| BatchTextRequest {
id: format!("perf_doc_{}", i),
text: format!(
"Performance test document number {} with some content for testing.",
i
),
metadata: None,
})
.collect();
let insert_result = match client.insert_texts(&collection_name, texts).await {
Ok(result) => result,
Err(e) => {
tracing::info!("Insert texts failed (expected in test environment): {}", e);
return;
}
};
let insert_time = start_time.elapsed();
assert!(insert_result.success);
assert!(insert_time.as_secs() < 30);
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
let search_start = std::time::Instant::now();
let search_result = match client
.search_vectors(&collection_name, "performance test", Some(20), None)
.await
{
Ok(result) => result,
Err(e) => {
tracing::info!(
"Search vectors failed (expected in test environment): {}",
e
);
return;
}
};
let search_time = search_start.elapsed();
assert!(!search_result.results.is_empty());
assert!(search_time.as_millis() < 5000);
let _ = client.delete_collection(&collection_name).await;
}
#[test]
fn test_response_models_tolerate_unknown_fields() {
let json = r#"{
"name": "c1",
"dimension": 384,
"metric": "Cosine",
"vector_count": 0,
"document_count": 0,
"created_at": "2026-04-21T00:00:00Z",
"updated_at": "2026-04-21T00:00:00Z",
"size": { "total": 0, "total_bytes": 0, "future_field": "ok" },
"quantization": { "enabled": false, "future_field": 42 },
"normalization": { "enabled": false, "future_field": [1, 2, 3] },
"status": "ready",
"future_top_level_field": { "arbitrary": "payload" }
}"#;
let info: CollectionInfo = serde_json::from_str(json)
.expect("CollectionInfo must tolerate unknown top-level + nested fields");
assert_eq!(info.name, "c1");
assert_eq!(info.dimension, 384);
assert_eq!(info.metric.to_lowercase(), "cosine");
assert_eq!(info.status.as_deref(), Some("ready"));
let collection_json = r#"{
"name": "c2",
"dimension": 512,
"metric": "Euclidean",
"future_top_level_field": true
}"#;
let collection: Collection = serde_json::from_str(collection_json)
.expect("Collection must tolerate unknown top-level fields");
assert_eq!(collection.name, "c2");
assert_eq!(collection.dimension, 512);
assert_eq!(
collection.metric.as_deref().map(str::to_lowercase),
Some("euclidean".to_string())
);
}