netabase_store 0.0.8

A type-safe, multi-backend key-value storage library for Rust with support for native (Sled, Redb) and WASM (IndexedDB) environments.
Documentation
//! 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());
    }
}