selene-db-graph 1.3.0

In-memory property-graph storage core (ArcSwap + imbl CoW, label/typed indexes, write funnel) for selene-db.
Documentation
use std::sync::Arc;

use super::*;

#[test]
fn read_within_tx_sees_own_writes() {
    let shared = SharedGraph::new(GraphId::new(1));
    let mut txn = shared.begin_write();
    let mut mutator = txn.mutator();
    let id = empty_node(&mut mutator);
    assert!(mutator.read().is_node_alive(id));
}

#[test]
fn read_within_tx_sees_label_index_updates() {
    let shared = SharedGraph::new(GraphId::new(1));
    let mut txn = shared.begin_write();
    let label = db_string("node.index.tx-read").unwrap();
    let mut mutator = txn.mutator();
    let id = mutator
        .create_node(LabelSet::single(label.clone()), PropertyMap::new())
        .expect("create_node ok");
    let row = mutator
        .read()
        .row_for_node_id(id)
        .expect("created node is mapped")
        .get();
    assert!(
        mutator
            .read()
            .nodes_with_label(&label)
            .unwrap()
            .contains(row)
    );
}

#[test]
fn multi_step_tx_emits_changes_in_order() {
    let shared = SharedGraph::new(GraphId::new(1));
    let mut txn = shared.begin_write();
    let id = {
        let mut mutator = txn.mutator();
        let id = empty_node(&mut mutator);
        mutator
            .update_node(
                id,
                LabelDiff::new([db_string("node.updated").unwrap()], []).unwrap(),
                PropertyDiff::new([], []).unwrap(),
            )
            .unwrap();
        mutator.delete_node(id).unwrap();
        id
    };
    let outcome = txn.commit().unwrap();
    assert!(matches!(outcome.changes[0], Change::NodeCreated { .. }));
    assert!(matches!(outcome.changes[1], Change::NodeUpdated { .. }));
    assert_eq!(outcome.changes[2], Change::NodeDeleted { id });
}

#[test]
fn schema_change_emits_change_passthrough() {
    let shared = SharedGraph::new(GraphId::new(1));
    let mut txn = shared.begin_write();
    {
        let mut mutator = txn.mutator();
        mutator.schema_change(
            GraphId::new(1),
            SchemaChange::GraphDropped {
                id: GraphId::new(2),
            },
        );
    }
    let outcome = txn.commit().unwrap();
    assert!(matches!(outcome.changes[0], Change::SchemaChanged { .. }));
}

#[test]
#[cfg(not(miri))]
fn four_writer_stress_no_double_allocation() {
    let shared = Arc::new(SharedGraph::new(GraphId::new(1)));
    let nodes_per_thread = 64;
    std::thread::scope(|scope| {
        for _ in 0..4 {
            let shared = Arc::clone(&shared);
            scope.spawn(move || {
                let mut txn = shared.begin_write();
                {
                    let mut mutator = txn.mutator();
                    for _ in 0..nodes_per_thread {
                        mutator
                            .create_node(LabelSet::new(), PropertyMap::new())
                            .expect("create_node ok");
                    }
                }
                txn.commit().unwrap();
            });
        }
    });
    let snapshot = shared.read();
    assert_eq!(snapshot.node_count(), 4 * nodes_per_thread);
    assert_eq!(
        snapshot.meta.next_node_id,
        (4 * nodes_per_thread + 1) as u64
    );
}

#[test]
fn value_type_import_smoke_keeps_schema_deferred() {
    let value_type = ValueType::predefined(PredefinedValueType::String);
    assert_eq!(value_type.predefined, Some(PredefinedValueType::String));
}