use std::path::Path;
use opendal::{Operator, services::Memory};
use sqlx::PgPool;
use uuid::Uuid;
use crate::{context::Context, metadata::MetadataStore};
async fn pool() -> PgPool {
PgPool::connect("postgresql://postgres:postgres@localhost:15432/postgres")
.await
.expect("failed to connect to database")
}
fn operator() -> Operator {
let service = Memory::default();
Operator::new(service)
.expect("failed to create operator")
.finish()
}
async fn raw_content(pool: &PgPool, index: Uuid, path: &str) -> Option<Option<Vec<u8>>> {
sqlx::query_scalar(
r#"
SELECT content
FROM tantivy.metadata
WHERE index = $1
AND path = $2
"#,
)
.bind(index)
.bind(path)
.fetch_optional(pool)
.await
.expect("failed to read raw metadata content")
}
#[tokio::test(flavor = "multi_thread")]
async fn small_metadata_stored_in_postgresql() {
let index = Uuid::new_v4();
let pool = pool().await;
let operator = operator();
let mut context = Context::new(index);
context.threshold = 1024;
let store = MetadataStore::open(&context, pool.clone(), operator.clone())
.await
.expect("failed to open metadata store");
let filepath = Path::new("meta.json");
let content = vec![42u8; 512];
store
.write_metadata(filepath, &content)
.await
.expect("failed to write metadata");
let read = store
.read_metadata(filepath)
.await
.expect("failed to read metadata");
assert_eq!(read.as_deref(), Some(content.as_slice()));
let raw = raw_content(&pool, index, "meta.json").await;
assert_eq!(raw, Some(Some(content.clone())));
let remote = operator
.exists(context.path(filepath).to_str().unwrap())
.await
.expect("failed to check object store");
assert!(!remote, "small metadata must not be stored remotely");
}
#[tokio::test(flavor = "multi_thread")]
async fn large_metadata_stored_in_object_store() {
let index = Uuid::new_v4();
let pool = pool().await;
let operator = operator();
let mut context = Context::new(index);
context.threshold = 1024;
let store = MetadataStore::open(&context, pool.clone(), operator.clone())
.await
.expect("failed to open metadata store");
let filepath = Path::new("meta.json");
let content = vec![7u8; 4096];
store
.write_metadata(filepath, &content)
.await
.expect("failed to write metadata");
let read = store
.read_metadata(filepath)
.await
.expect("failed to read metadata");
assert_eq!(read.as_deref(), Some(content.as_slice()));
let remote = operator
.exists(context.path(filepath).to_str().unwrap())
.await
.expect("failed to check object store");
assert!(remote, "large metadata must be stored remotely");
}
#[tokio::test(flavor = "multi_thread")]
async fn missing_metadata_returns_none() {
let index = Uuid::new_v4();
let pool = pool().await;
let operator = operator();
let mut context = Context::new(index);
context.threshold = 1024;
let store = MetadataStore::open(&context, pool, operator)
.await
.expect("failed to open metadata store");
let read = store
.read_metadata(Path::new("missing.json"))
.await
.expect("failed to read metadata");
assert_eq!(read, None);
}
#[tokio::test(flavor = "multi_thread")]
async fn rewrite_across_threshold() {
let index = Uuid::new_v4();
let pool = pool().await;
let operator = operator();
let mut context = Context::new(index);
context.threshold = 1024;
let store = MetadataStore::open(&context, pool.clone(), operator.clone())
.await
.expect("failed to open metadata store");
let filepath = Path::new("meta.json");
let small = vec![1u8; 256];
store
.write_metadata(filepath, &small)
.await
.expect("failed to write small metadata");
assert_eq!(
store.read_metadata(filepath).await.unwrap().as_deref(),
Some(small.as_slice())
);
let large = vec![2u8; 4096];
store
.write_metadata(filepath, &large)
.await
.expect("failed to write large metadata");
assert_eq!(
store.read_metadata(filepath).await.unwrap().as_deref(),
Some(large.as_slice())
);
}