//! Integration tests for zero-copy guard-based API (Phase 4)
//!
//! These tests verify that the zero-copy implementation works correctly
//! and that guards can be returned with proper lifetimes.
#![cfg(all(feature = "redb", feature = "redb-zerocopy"))]
use netabase_store::{netabase_definition_module, NetabaseModel};
use netabase_store::databases::redb_zerocopy::*;
use netabase_store::databases::redb_store::BincodeWrapper;
// Test definition and models
#[netabase_definition_module(TestDefinition, TestKeys)]
mod test_models {
use super::*;
#[derive(
NetabaseModel,
Debug,
Clone,
PartialEq,
bincode::Encode,
bincode::Decode,
serde::Serialize,
serde::Deserialize,
)]
#[netabase(TestDefinition)]
pub struct User {
#[primary_key]
pub id: u64,
pub name: String,
pub email: String,
}
#[derive(
NetabaseModel,
Debug,
Clone,
PartialEq,
bincode::Encode,
bincode::Decode,
serde::Serialize,
serde::Deserialize,
)]
#[netabase(TestDefinition)]
pub struct Product {
#[primary_key]
pub sku: String,
pub name: String,
pub price: f64,
}
}
use test_models::*;
#[test]
fn test_zerocopy_read_transaction() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test.redb");
// Create store and insert test data
{
let store = RedbStore::<TestDefinition>::new(&db_path).unwrap();
let mut txn = store.begin_write().unwrap();
let tables = TestDefinition::tables();
let mut users = txn.open_tree::<TestDefinition, User>(tables.user).unwrap();
users.put(User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
}).unwrap();
users.put(User {
id: 2,
name: "Bob".to_string(),
email: "bob@example.com".to_string(),
}).unwrap();
txn.commit().unwrap();
}
// Read with zero-copy API
{
let store = RedbStore::<TestDefinition>::new(&db_path).unwrap();
let txn = store.begin_read().unwrap();
let tables = TestDefinition::tables();
let users = txn.open_tree::<TestDefinition, User>(tables.user).unwrap();
// Test get_guard (zero-copy read at redb level)
if let Some(guard) = users.get_guard(UserPrimaryKey(1)).unwrap() {
let user: BincodeWrapper<User> = guard.value();
assert_eq!(user.0.id, 1);
assert_eq!(user.0.name, "Alice");
assert_eq!(user.0.email, "alice@example.com");
} else {
panic!("User not found");
}
// Test regular get (owned)
let user = users.get(UserPrimaryKey(2)).unwrap().expect("User 2 not found");
assert_eq!(user.id, 2);
assert_eq!(user.name, "Bob");
assert_eq!(user.email, "bob@example.com");
// Test len
assert_eq!(users.len().unwrap(), 2);
assert!(!users.is_empty().unwrap());
}
}
#[test]
fn test_zerocopy_write_transaction() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test_write.redb");
let store = RedbStore::<TestDefinition>::new(&db_path).unwrap();
let mut txn = store.begin_write().unwrap();
let tables = TestDefinition::tables();
let mut products = txn.open_tree::<TestDefinition, Product>(tables.product).unwrap();
// Insert product
products.put(Product {
sku: "ABC123".to_string(),
name: "Widget".to_string(),
price: 19.99,
}).unwrap();
// Read it back with guard
if let Some(guard) = products.get_guard(ProductPrimaryKey("ABC123".to_string())).unwrap() {
let product: BincodeWrapper<Product> = guard.value();
assert_eq!(product.0.sku, "ABC123");
assert_eq!(product.0.name, "Widget");
assert_eq!(product.0.price, 19.99);
} else {
panic!("Product not found");
}
// Update product
products.put(Product {
sku: "ABC123".to_string(),
name: "Super Widget".to_string(),
price: 29.99,
}).unwrap();
// Verify update
let product = products.get(ProductPrimaryKey("ABC123".to_string())).unwrap().unwrap();
assert_eq!(product.name, "Super Widget");
assert_eq!(product.price, 29.99);
txn.commit().unwrap();
}
#[test]
fn test_zerocopy_for_each() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test_iter.redb");
// Setup
{
let store = RedbStore::<TestDefinition>::new(&db_path).unwrap();
let mut txn = store.begin_write().unwrap();
let tables = TestDefinition::tables();
let mut users = txn.open_tree::<TestDefinition, User>(tables.user).unwrap();
for i in 1..=5 {
users.put(User {
id: i,
name: format!("User {}", i),
email: format!("user{}@example.com", i),
}).unwrap();
}
txn.commit().unwrap();
}
// Test for_each (zero-copy iteration)
{
let store = RedbStore::<TestDefinition>::new(&db_path).unwrap();
let txn = store.begin_read().unwrap();
let tables = TestDefinition::tables();
let users = txn.open_tree::<TestDefinition, User>(tables.user).unwrap();
let mut count = 0;
users.for_each(|key, user| {
assert_eq!(key.0, user.id);
assert!(user.name.starts_with("User "));
count += 1;
}).unwrap();
assert_eq!(count, 5);
}
}
#[test]
fn test_zerocopy_transaction_abort() {
let temp_dir = tempfile::tempdir().unwrap();
let db_path = temp_dir.path().join("test_abort.redb");
let store = RedbStore::<TestDefinition>::new(&db_path).unwrap();
// Insert and commit
{
let mut txn = store.begin_write().unwrap();
let tables = TestDefinition::tables();
let mut users = txn.open_tree::<TestDefinition, User>(tables.user).unwrap();
users.put(User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
}).unwrap();
txn.commit().unwrap();
}
// Try to insert more but abort
{
let mut txn = store.begin_write().unwrap();
let tables = TestDefinition::tables();
let mut users = txn.open_tree::<TestDefinition, User>(tables.user).unwrap();
users.put(User {
id: 2,
name: "Bob".to_string(),
email: "bob@example.com".to_string(),
}).unwrap();
txn.abort().unwrap();
}
// Verify only Alice exists
{
let txn = store.begin_read().unwrap();
let tables = TestDefinition::tables();
let users = txn.open_tree::<TestDefinition, User>(tables.user).unwrap();
assert_eq!(users.len().unwrap(), 1);
let alice = users.get(UserPrimaryKey(1)).unwrap().unwrap();
assert_eq!(alice.name, "Alice");
let bob = users.get(UserPrimaryKey(2)).unwrap();
assert!(bob.is_none());
}
}