#![forbid(unsafe_code)]
fn unique_path(label: &str) -> std::path::PathBuf {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.subsec_nanos())
.unwrap_or(0);
std::env::temp_dir().join(format!(
"oxistore_integ_{}_{}_{}.db",
label,
std::process::id(),
nanos
))
}
#[test]
#[cfg(all(feature = "kv-redb", feature = "kv-sled"))]
fn opening_redb_file_with_sled_backend_errors_gracefully() {
use oxistore::{open_with, StoreKind};
let path = unique_path("mismatch_redb_sled");
{
let store = open_with(StoreKind::Redb, &path).expect("open redb for mismatch test");
store.put(b"mismatch_key", b"mismatch_val").expect("put");
}
let result = open_with(StoreKind::Sled, &path);
assert!(
result.is_err(),
"opening a redb file with the sled backend must return an error, not succeed silently"
);
let err_msg = result
.err()
.expect("expected an error for backend mismatch")
.to_string();
assert!(
!err_msg.is_empty(),
"error message for backend mismatch must be non-empty, got empty string"
);
let _ = std::fs::remove_file(&path);
}
#[test]
#[cfg(all(feature = "kv-redb", feature = "kv-sled"))]
fn opening_sled_dir_with_redb_backend_errors_gracefully() {
use oxistore::{open_with, StoreKind};
let path = unique_path("mismatch_sled_redb_dir");
{
let store = open_with(StoreKind::Sled, &path).expect("open sled for mismatch test");
store.put(b"sled_key", b"sled_val").expect("put");
}
let result = open_with(StoreKind::Redb, &path);
assert!(
result.is_err(),
"opening a sled directory with the redb backend must return an error, not succeed silently"
);
let err_msg = result
.err()
.expect("expected an error for backend mismatch")
.to_string();
assert!(
!err_msg.is_empty(),
"error message for backend mismatch must be non-empty"
);
let _ = std::fs::remove_dir_all(&path);
}
#[test]
#[cfg(all(feature = "kv-redb", feature = "columnar", feature = "cache"))]
fn cross_crate_kv_columnar_cache_workflow() {
use oxistore::columnar::{ColumnarTable, DataType, Field, Int64Array, RecordBatch, Schema};
use oxistore::KvStore as _;
use std::sync::Arc;
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.subsec_nanos())
.unwrap_or(0);
let base = format!("{}_{}", std::process::id(), nanos);
let kv_path = std::env::temp_dir().join(format!("xc_kv_{base}.db"));
let parquet_path = std::env::temp_dir().join(format!("xc_col_{base}.parquet"));
let cached_path = std::env::temp_dir().join(format!("xc_cached_{base}.db"));
{
let store = oxistore::open(&kv_path).expect("open kv store");
for i in 0i64..10 {
let key = format!("row_{i:03}").into_bytes();
store.put(&key, &i.to_le_bytes()).expect("kv put");
}
}
{
let schema = Arc::new(Schema::new(vec![Field::new(
"row_id",
DataType::Int64,
false,
)]));
let ids = Int64Array::from((0i64..10).collect::<Vec<_>>());
let batch = RecordBatch::try_new(Arc::clone(&schema), vec![Arc::new(ids)])
.expect("build record batch");
let mut table = ColumnarTable::new(Arc::clone(&schema));
table.push(batch).expect("push batch");
table.write_to(&parquet_path).expect("write parquet");
}
let col = oxistore::open_columnar(&parquet_path).expect("open_columnar");
assert_eq!(col.row_count(), 10, "columnar table must have 10 rows");
{
let cached = oxistore::open_cached(oxistore::StoreKind::Redb, &cached_path, 128)
.expect("open_cached");
for i in 0i64..5 {
let key = format!("cache_{i:03}").into_bytes();
cached.put(&key, &i.to_le_bytes()).expect("cached put");
}
for i in 0i64..5 {
let key = format!("cache_{i:03}").into_bytes();
let val = cached.get(&key).expect("cached get (miss)");
assert_eq!(
val.as_deref(),
Some(i.to_le_bytes().as_ref()),
"cached get must return correct value on miss"
);
}
for i in 0i64..5 {
let key = format!("cache_{i:03}").into_bytes();
let val = cached.get(&key).expect("cached get (hit)");
assert_eq!(
val.as_deref(),
Some(i.to_le_bytes().as_ref()),
"cached get must return correct value on hit"
);
}
}
let _ = std::fs::remove_file(&kv_path);
let _ = std::fs::remove_file(&parquet_path);
let _ = std::fs::remove_file(&cached_path);
}
#[test]
#[cfg(feature = "kv-redb")]
fn oxisql_and_oxistore_coexist() {
let kv_path = unique_path("oxisql_kv");
let kv = oxistore::open(&kv_path).expect("open kv");
kv.put(b"user:1", b"alice").expect("kv put");
kv.put(b"user:2", b"bob").expect("kv put");
let alice = kv.get(b"user:1").expect("kv get").expect("user:1 missing");
assert_eq!(alice, b"alice", "kv round-trip failed");
assert_eq!(kv.count().expect("count"), 2, "expected 2 entries in kv");
drop(kv);
let _ = std::fs::remove_file(&kv_path);
}