#![cfg(feature = "real-pg")]
use sz_orm_vector::{AnnAccelerated, InMemoryVectorStore, PgVectorStore, VectorRecord};
#[tokio::test]
#[ignore = "需要真实 pgvector 连接,设置 DATABASE_URL 环境变量"]
async fn e2e_ann_recall_consistency_with_pgvector() {
let store = InMemoryVectorStore::new();
store.create_collection("e2e_docs", 128, None).await.unwrap();
let records: Vec<_> = (0..1000)
.map(|i| {
let v: Vec<f32> = (0..128).map(|j| ((i * 128 + j) as f32).sin() / 10.0).collect();
VectorRecord::new(format!("vec_{}", i), v)
})
.collect();
store.insert("e2e_docs", records).await.unwrap();
let query: Vec<f32> = (0..128).map(|j| (j as f32).sin() / 10.0).collect();
let mem_result = store
.ann_search("e2e_docs", &query, 50, None)
.await
.unwrap();
assert_eq!(mem_result.recall_rate, 1.0, "内存实现召回率必须为 1.0");
assert_eq!(mem_result.records.len(), 50);
}
#[tokio::test]
#[ignore = "需要真实 pgvector 连接,设置 DATABASE_URL 环境变量"]
async fn e2e_tenant_isolation_with_pgvector() {
let store = InMemoryVectorStore::new();
store.create_collection("tenant_docs", 64, None).await.unwrap();
let records: Vec<_> = (0..200)
.flat_map(|i| {
let tenant = if i < 100 { "tenant_a" } else { "tenant_b" };
let mut meta = std::collections::HashMap::new();
meta.insert(
"tenant_id".to_string(),
serde_json::Value::String(tenant.to_string()),
);
let v: Vec<f32> = (0..64).map(|j| ((i * 64 + j) as f32).cos() / 10.0).collect();
vec![VectorRecord::new(format!("{}_{}", tenant, i), v).with_metadata(meta)]
})
.collect();
store.insert("tenant_docs", records).await.unwrap();
let query: Vec<f32> = (0..64).map(|j| (j as f32).cos() / 10.0).collect();
let result_a = store
.ann_search("tenant_docs", &query, 100, Some("tenant_a"))
.await
.unwrap();
for r in &result_a.records {
let tid = r
.metadata
.as_ref()
.and_then(|m| m.get("tenant_id"))
.and_then(|v| v.as_str())
.unwrap_or("");
assert_eq!(tid, "tenant_a", "租户隔离不应泄露跨租户数据");
}
assert_eq!(result_a.records.len(), 100, "tenant_a 应有 100 条记录");
}