use super::*;
use crate::api::transaction::{ReadOps, WriteOps};
use crate::core::GLOBAL_INTERNER;
use crate::core::error::{Error, Result};
use crate::core::id::NodeId;
use crate::core::property::{PropertyMapBuilder, PropertyValue};
#[test]
fn test_create_node() {
let db = AletheiaDB::new().unwrap();
let props = PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build();
let node_id = db.create_node("Person", props).unwrap();
assert_eq!(db.node_count(), 1);
let node = db.get_node(node_id).unwrap();
assert_eq!(node.id, node_id);
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Alice")
);
}
#[test]
fn test_create_edge() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = db
.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2020i64).build(),
)
.unwrap();
assert_eq!(db.edge_count(), 1);
let edge = db.get_edge(edge_id).unwrap();
assert_eq!(edge.source, alice);
assert_eq!(edge.target, bob);
}
#[test]
fn test_graph_traversal() {
let db = AletheiaDB::new().unwrap();
let n0 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(n0, n2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let outgoing = db.get_outgoing_edges(n0);
assert_eq!(outgoing.len(), 2);
let knows_edges = db.get_outgoing_edges_with_label(n0, "KNOWS");
assert_eq!(knows_edges.len(), 2);
}
#[test]
fn test_iterator_access() {
let db = AletheiaDB::new().unwrap();
let n0 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let e1 = db
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let e2 = db
.create_edge(n0, n2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let outgoing: Vec<_> = db.get_outgoing_edges_iter(n0).collect();
assert_eq!(outgoing.len(), 2);
assert!(outgoing.contains(&e1));
assert!(outgoing.contains(&e2));
let incoming1: Vec<_> = db.get_incoming_edges_iter(n1).collect();
assert_eq!(incoming1.len(), 1);
assert_eq!(incoming1[0], e1);
let incoming2: Vec<_> = db.get_incoming_edges_iter(n2).collect();
assert_eq!(incoming2.len(), 1);
assert_eq!(incoming2[0], e2);
}
#[test]
fn test_historical_stats() {
let db = AletheiaDB::new().unwrap();
db.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let stats = db.historical_stats().unwrap();
assert_eq!(stats.total_node_versions, 2);
assert_eq!(stats.node_anchor_count, 2); }
#[test]
fn test_closure_based_write_api() {
let db = AletheiaDB::new().unwrap();
let (node_id, edge_id) = db
.write(|tx| {
let n1 = tx.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)?;
let n2 = tx.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Bob").build(),
)?;
let e = tx.create_edge(
n1,
n2,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2024i64).build(),
)?;
Ok::<_, Error>((n1, e))
})
.unwrap();
assert_eq!(db.node_count(), 2);
assert_eq!(db.edge_count(), 1);
let node = db.get_node(node_id).unwrap();
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Alice")
);
let edge = db.get_edge(edge_id).unwrap();
assert_eq!(edge.source, node_id);
}
#[test]
fn test_closure_based_read_api() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Charlie").build(),
)
.unwrap();
let name = db
.read(|tx| {
let node = tx.get_node(node_id)?;
Ok::<_, Error>(
node.get_property("name")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
)
})
.unwrap();
assert_eq!(name, Some("Charlie".to_string()));
}
#[test]
fn test_explicit_write_transaction() {
let db = AletheiaDB::new().unwrap();
let mut tx = db.write_transaction().unwrap();
let n1 = tx
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "David").build(),
)
.unwrap();
let n2 = tx
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Eve").build(),
)
.unwrap();
tx.create_edge(n1, n2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(db.node_count(), 0);
tx.commit().unwrap();
assert_eq!(db.node_count(), 2);
assert_eq!(db.edge_count(), 1);
}
#[test]
fn test_explicit_read_transaction() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("age", 42i64).build(),
)
.unwrap();
let tx = db.read_transaction().unwrap();
let node = tx.get_node(node_id).unwrap();
assert_eq!(node.get_property("age").and_then(|v| v.as_int()), Some(42));
}
#[test]
fn test_transaction_atomicity() {
let db = AletheiaDB::new().unwrap();
let valid_node = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let result: std::result::Result<(), Error> = db.write(|tx| {
tx.create_node("Person", PropertyMapBuilder::new().build())?;
tx.create_node("Person", PropertyMapBuilder::new().build())?;
tx.create_edge(
valid_node,
NodeId::new(9999).unwrap(),
"KNOWS",
PropertyMapBuilder::new().build(),
)?;
Ok(())
});
assert!(result.is_err());
assert_eq!(db.node_count(), 1);
assert_eq!(db.edge_count(), 0);
}
#[test]
fn test_transaction_rollback_on_error() {
let db = AletheiaDB::new().unwrap();
let result: Result<()> = db.write(|tx| {
tx.create_node("Person", PropertyMapBuilder::new().build())?;
tx.create_node("Person", PropertyMapBuilder::new().build())?;
Err(crate::core::error::Error::Storage(
crate::core::error::StorageError::InconsistentState {
reason: "test error".to_string(),
},
))
});
assert!(result.is_err());
assert_eq!(db.node_count(), 0);
}
#[test]
fn test_multiple_transactions() {
let db = AletheiaDB::new().unwrap();
let n1 = db
.write(|tx| tx.create_node("Person", PropertyMapBuilder::new().build()))
.unwrap();
let n2 = db
.write(|tx| tx.create_node("Person", PropertyMapBuilder::new().build()))
.unwrap();
db.write(|tx| tx.create_edge(n1, n2, "KNOWS", PropertyMapBuilder::new().build()))
.unwrap();
assert_eq!(db.node_count(), 2);
assert_eq!(db.edge_count(), 1);
}
#[test]
fn test_snapshot_isolation() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("version", 1i64).build(),
)
.unwrap();
let tx1 = db.read_transaction().unwrap();
let node_v1 = tx1.get_node(node_id).unwrap();
assert_eq!(
node_v1.get_property("version").and_then(|v| v.as_int()),
Some(1)
);
let new_node_id = db
.write(|tx| {
tx.create_node(
"Person",
PropertyMapBuilder::new().insert("version", 2i64).build(),
)
})
.unwrap();
assert!(tx1.get_node(new_node_id).is_err());
let node_v1_again = tx1.get_node(node_id).unwrap();
assert_eq!(
node_v1_again
.get_property("version")
.and_then(|v| v.as_int()),
Some(1)
);
}
#[test]
fn test_new_returns_result() {
let result = AletheiaDB::new();
assert!(result.is_ok(), "new() should succeed with default config");
}
#[test]
fn test_with_config_returns_result() {
let result = AletheiaDB::with_config(crate::core::version::AnchorConfig::default());
assert!(
result.is_ok(),
"with_config() should succeed with default config"
);
}
#[test]
fn test_with_wal_config_returns_result() {
let wal_config = crate::config::WalConfig::default();
let result = AletheiaDB::with_wal_config(wal_config);
assert!(
result.is_ok(),
"with_wal_config() should succeed with default config"
);
}
#[test]
fn test_with_full_config_returns_result() {
let result = AletheiaDB::with_full_config(
crate::core::version::AnchorConfig::default(),
crate::config::WalConfig::default(),
);
assert!(
result.is_ok(),
"with_full_config() should succeed with default config"
);
}
#[test]
fn test_with_unified_config_returns_result() {
let temp_dir = tempfile::tempdir().unwrap();
let mut config = crate::config::AletheiaDBConfig::default();
config.wal.wal_dir = temp_dir.path().join("wal");
config.persistence.data_dir = temp_dir.path().join("data");
let result = AletheiaDB::with_unified_config(config);
assert!(
result.is_ok(),
"with_unified_config() should succeed with default config"
);
}
#[test]
fn test_cold_storage_configuration() {
use crate::config::{AletheiaDBConfig, HistoricalConfigBuilder, WalConfigBuilder};
use std::time::Duration;
let temp_dir = tempfile::tempdir().expect("Failed to create temp dir");
let cold_storage_path = temp_dir.path().join("cold.redb");
let config = AletheiaDBConfig::builder()
.wal(
WalConfigBuilder::new()
.wal_dir(temp_dir.path().join("wal"))
.build(),
)
.persistence(crate::storage::index_persistence::PersistenceConfig {
enabled: false, ..Default::default()
})
.historical(
HistoricalConfigBuilder::new()
.enable_cold_storage(true)
.cold_storage_path(&cold_storage_path)
.migration_age_threshold(Duration::from_secs(3600))
.max_hot_versions(1000)
.build(),
)
.build();
let _db = AletheiaDB::with_unified_config(config).expect("Failed to create database");
assert!(
cold_storage_path.exists(),
"Cold storage file should be created"
);
}
#[cfg(unix)]
#[test]
fn test_wal_creation_failure_propagates_error() {
use std::path::PathBuf;
let invalid_wal_dir = PathBuf::from("/dev/null/wal");
let wal_config = crate::config::WalConfigBuilder::new()
.wal_dir(invalid_wal_dir)
.build();
let result = AletheiaDB::with_wal_config(wal_config);
assert!(
result.is_err(),
"with_wal_config() should return Err when WAL directory cannot be created"
);
let err = result.expect_err("Expected an error");
let err_msg = err.to_string().to_lowercase();
assert!(
err_msg.contains("i/o")
|| err_msg.contains("directory")
|| err_msg.contains("not a directory"),
"Error message should indicate I/O issue, got: {}",
err
);
}
#[cfg(unix)]
#[test]
fn test_unified_config_wal_failure_propagates_error() {
use std::path::PathBuf;
let invalid_wal_dir = PathBuf::from("/dev/null/wal");
let config = crate::config::AletheiaDBConfigBuilder::new()
.wal(
crate::config::WalConfigBuilder::new()
.wal_dir(invalid_wal_dir)
.build(),
)
.build();
let result = AletheiaDB::with_unified_config(config);
assert!(
result.is_err(),
"with_unified_config() should return Err when WAL directory cannot be created"
);
}
#[test]
fn test_aletheiadb_is_vector_index_enabled_for() {
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
assert!(!db.is_vector_index_enabled());
assert!(!db.is_vector_index_enabled_for("embedding"));
assert!(!db.is_vector_index_enabled_for("vector"));
let config = HnswConfig::new(128, DistanceMetric::Cosine);
db.enable_vector_index("embedding", config).unwrap();
assert!(db.is_vector_index_enabled());
assert!(db.is_vector_index_enabled_for("embedding"));
assert!(!db.is_vector_index_enabled_for("vector"));
let config2 = HnswConfig::new(256, DistanceMetric::Euclidean);
db.enable_vector_index("vector", config2).unwrap();
assert!(db.is_vector_index_enabled_for("vector"));
}
#[test]
fn test_aletheiadb_default_durability() {
let db = AletheiaDB::new().unwrap();
let _durability = db.default_durability();
}
#[test]
fn test_get_edge_source_and_target() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let bob = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Bob").build(),
)
.unwrap();
let knows_edge = db
.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(db.get_edge_source(knows_edge).unwrap(), alice);
assert_eq!(db.get_edge_target(knows_edge).unwrap(), bob);
}
#[test]
fn test_get_node_at_valid_time() {
let db = AletheiaDB::new().unwrap();
let mut tx = db.write_transaction().unwrap();
let jan_1 = crate::core::hlc::HybridTimestamp::new(1_704_067_200_000_000, 0).unwrap();
let props = PropertyMapBuilder::new().insert("name", "Alice").build();
let node_id = tx
.create_node_with_valid_time("Person", props, Some(jan_1))
.unwrap();
tx.commit().unwrap();
let jan_15 = crate::core::hlc::HybridTimestamp::new(1_705_276_800_000_000, 0).unwrap();
let node = db.get_node_at_valid_time(node_id, jan_15).unwrap();
assert_eq!(node.id, node_id);
assert_eq!(
node.properties.get("name").unwrap(),
&PropertyValue::String("Alice".into())
);
}
#[test]
fn test_get_node_at_transaction_time() {
let db = AletheiaDB::new().unwrap();
let props = PropertyMapBuilder::new().insert("name", "Alice").build();
let node_id = db.create_node("Person", props).unwrap();
let tx_time = crate::core::temporal::time::now();
let node = db.get_node_at_transaction_time(node_id, tx_time).unwrap();
assert_eq!(node.id, node_id);
}
#[test]
fn test_get_node_history_returns_all_versions() {
let db = AletheiaDB::new().unwrap();
let props1 = PropertyMapBuilder::new().insert("name", "Alice").build();
let node_id = db.create_node("Person", props1).unwrap();
db.write(|tx| {
let props2 = PropertyMapBuilder::new()
.insert("name", "Alice Smith")
.build();
tx.update_node(node_id, props2)
})
.unwrap();
let history = db.get_node_history(node_id).unwrap();
assert_eq!(history.version_count(), 2);
assert_eq!(history.first_version().unwrap().version_number, 1);
assert_eq!(history.current_version().unwrap().version_number, 2);
}
#[test]
fn test_get_node_at_version() {
let db = AletheiaDB::new().unwrap();
let props1 = PropertyMapBuilder::new().insert("name", "Alice").build();
let node_id = db.create_node("Person", props1).unwrap();
db.write(|tx| {
let props2 = PropertyMapBuilder::new().insert("name", "Bob").build();
tx.update_node(node_id, props2)
})
.unwrap();
let v1 = db.get_node_at_version(node_id, 1).unwrap();
assert_eq!(
v1.properties.get("name").unwrap(),
&PropertyValue::String("Alice".into())
);
let v2 = db.get_node_at_version(node_id, 2).unwrap();
assert_eq!(
v2.properties.get("name").unwrap(),
&PropertyValue::String("Bob".into())
);
}
#[test]
fn test_diff_node_versions() {
let db = AletheiaDB::new().unwrap();
let props1 = PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build();
let node_id = db.create_node("Person", props1).unwrap();
db.write(|tx| {
let props2 = PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 31i64)
.insert("city", "NYC")
.build();
tx.update_node(node_id, props2)
})
.unwrap();
let history = db.get_node_history(node_id).unwrap();
let v1_id = history.first_version().unwrap().version_id;
let v2_id = history.current_version().unwrap().version_id;
let diff = db.diff_node_versions(node_id, v1_id, v2_id).unwrap();
assert!(diff.has_changes());
assert_eq!(diff.added.len(), 1); assert!(diff.added.contains_key("city"));
assert_eq!(diff.modified.len(), 1); assert!(diff.removed.is_empty());
}
#[test]
fn test_get_edge_history_returns_all_versions() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let props1 = PropertyMapBuilder::new().insert("since", 2020i64).build();
let edge_id = db.create_edge(alice, bob, "KNOWS", props1).unwrap();
let props2 = PropertyMapBuilder::new().insert("since", 2021i64).build();
db.write(|tx| tx.update_edge(edge_id, props2)).unwrap();
let history = db.get_edge_history(edge_id).unwrap();
assert_eq!(history.version_count(), 2);
}
#[test]
fn test_diff_edge_versions() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let props1 = PropertyMapBuilder::new().insert("weight", 1.0f64).build();
let edge_id = db.create_edge(alice, bob, "KNOWS", props1).unwrap();
let props2 = PropertyMapBuilder::new().insert("weight", 2.0f64).build();
db.write(|tx| tx.update_edge(edge_id, props2)).unwrap();
let history = db.get_edge_history(edge_id).unwrap();
let v1_id = history.first_version().unwrap().version_id;
let v2_id = history.current_version().unwrap().version_id;
let diff = db.diff_edge_versions(edge_id, v1_id, v2_id).unwrap();
assert!(diff.has_changes());
assert_eq!(diff.modified.len(), 1); }
#[test]
fn test_full_bitemporal_workflow() {
use crate::core::hlc::HybridTimestamp;
use crate::core::temporal::time;
let db = AletheiaDB::new().unwrap();
let jan_1 = HybridTimestamp::new(1_704_067_200_000_000, 0).unwrap(); let jan_15 = HybridTimestamp::new(1_705_276_800_000_000, 0).unwrap(); let feb_1 = HybridTimestamp::new(1_706_745_600_000_000, 0).unwrap();
let alice = db
.write(|tx| {
tx.create_node_with_valid_time(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
Some(jan_1),
)
})
.unwrap();
let result = db.get_node_at_valid_time(alice, jan_15);
assert!(result.is_ok(), "Should find Alice at Jan 15 valid time");
let result = db.get_node_at_transaction_time(alice, jan_15);
assert!(
result.is_err(),
"Should NOT find Alice at Jan 15 transaction time (recorded later)"
);
let result = db.get_node_at_transaction_time(alice, time::now());
assert!(
result.is_ok(),
"Should find Alice at current transaction time"
);
db.write(|tx| {
tx.update_node_with_valid_time(
alice,
PropertyMapBuilder::new()
.insert("name", "Alice Smith")
.build(),
Some(feb_1), )
})
.unwrap();
let history = db.get_node_history(alice).unwrap();
assert_eq!(
history.versions.len(),
2,
"Should have 2 versions after update"
);
let diff = db
.diff_node_versions(
alice,
history.versions[0].version_id,
history.versions[1].version_id,
)
.unwrap();
assert_eq!(diff.modified.len(), 1, "Should have 1 modified property");
let name_key = GLOBAL_INTERNER.intern("name").unwrap();
let (modified_key, _, _) = &diff.modified[0];
assert_eq!(
*modified_key, name_key,
"Modified property should be 'name'"
);
let v1 = db.get_node_at_version(alice, 1).unwrap();
assert_eq!(
v1.properties.get("name").unwrap(),
&PropertyValue::String("Alice".into()),
"Version 1 should have name='Alice'"
);
let v2 = db.get_node_at_version(alice, 2).unwrap();
assert_eq!(
v2.properties.get("name").unwrap(),
&PropertyValue::String("Alice Smith".into()),
"Version 2 should have name='Alice Smith'"
);
}
#[test]
fn test_find_similar_as_of_in() {
use crate::index::vector::temporal::{SnapshotStrategy, TemporalVectorConfig};
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
let hnsw_config = HnswConfig::new(4, DistanceMetric::Cosine);
let temporal_config = TemporalVectorConfig {
snapshot_strategy: SnapshotStrategy::TransactionInterval(1),
..TemporalVectorConfig::default_with_hnsw(hnsw_config)
};
db.enable_temporal_vector_index("embedding", temporal_config)
.unwrap();
let vector = vec![1.0, 0.0, 0.0, 0.0];
let props = PropertyMapBuilder::new()
.insert("name", "Test")
.insert_vector("embedding", &vector)
.build();
let (node_id, commit_ts) = db
.write_with_timestamp(|tx| tx.create_node("TestNode", props))
.unwrap();
let results = db
.find_similar_as_of_in("embedding", &vector, 10, commit_ts)
.unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, node_id);
}
#[test]
fn test_find_nodes_by_property_facade() {
let db = AletheiaDB::new().unwrap();
let alice_id = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build(),
)
.unwrap();
let bob_id = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Bob")
.insert("age", 30i64)
.build(),
)
.unwrap();
db.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Charlie")
.insert("age", 25i64)
.build(),
)
.unwrap();
let results =
db.find_nodes_by_property("Person", "name", &PropertyValue::String("Alice".into()));
assert_eq!(results, vec![alice_id]);
let mut results = db.find_nodes_by_property("Person", "age", &PropertyValue::Int(30));
results.sort();
let mut expected = vec![alice_id, bob_id];
expected.sort();
assert_eq!(results, expected);
let results =
db.find_nodes_by_property("Person", "name", &PropertyValue::String("Nobody".into()));
assert!(results.is_empty());
}
#[test]
fn test_find_nodes_by_property_facade_cross_label() {
let db = AletheiaDB::new().unwrap();
let person_id = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
db.create_node(
"Company",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let results =
db.find_nodes_by_property("Person", "name", &PropertyValue::String("Alice".into()));
assert_eq!(results, vec![person_id]);
}
#[test]
fn test_get_node_nonexistent() {
let db = AletheiaDB::new().unwrap();
let fake_id = NodeId::new(9999).unwrap();
let result = db.get_node(fake_id);
assert!(result.is_err());
}
#[test]
fn test_get_edge_nonexistent() {
let db = AletheiaDB::new().unwrap();
let fake_id = crate::core::id::EdgeId::new(9999).unwrap();
let result = db.get_edge(fake_id);
assert!(result.is_err());
}
#[test]
fn test_get_edge_source_nonexistent() {
let db = AletheiaDB::new().unwrap();
let fake_id = crate::core::id::EdgeId::new(9999).unwrap();
let result = db.get_edge_source(fake_id);
assert!(result.is_err());
}
#[test]
fn test_get_edge_target_nonexistent() {
let db = AletheiaDB::new().unwrap();
let fake_id = crate::core::id::EdgeId::new(9999).unwrap();
let result = db.get_edge_target(fake_id);
assert!(result.is_err());
}
#[test]
fn test_create_node_empty_properties() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let node = db.get_node(node_id).unwrap();
assert!(node.properties.is_empty());
}
#[test]
fn test_create_node_empty_label() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node("", PropertyMapBuilder::new().build())
.unwrap();
let node = db.get_node(node_id).unwrap();
assert_eq!(node.id, node_id);
}
#[test]
fn test_create_edge_invalid_source() {
let db = AletheiaDB::new().unwrap();
let target = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let fake_source = NodeId::new(9999).unwrap();
let result = db.create_edge(
fake_source,
target,
"KNOWS",
PropertyMapBuilder::new().build(),
);
assert!(result.is_err());
}
#[test]
fn test_create_edge_invalid_target() {
let db = AletheiaDB::new().unwrap();
let source = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let fake_target = NodeId::new(9999).unwrap();
let result = db.create_edge(
source,
fake_target,
"KNOWS",
PropertyMapBuilder::new().build(),
);
assert!(result.is_err());
}
#[test]
fn test_create_edge_both_invalid() {
let db = AletheiaDB::new().unwrap();
let result = db.create_edge(
NodeId::new(9998).unwrap(),
NodeId::new(9999).unwrap(),
"KNOWS",
PropertyMapBuilder::new().build(),
);
assert!(result.is_err());
}
#[test]
fn test_delete_node_via_transaction() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
assert_eq!(db.node_count(), 1);
db.write(|tx| tx.delete_node(node_id)).unwrap();
assert_eq!(db.node_count(), 0);
assert!(db.get_node(node_id).is_err());
}
#[test]
fn test_delete_node_with_edges_creates_orphans() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = db
.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
db.write(|tx| tx.delete_node(alice)).unwrap();
assert_eq!(db.node_count(), 1);
assert!(db.get_node(alice).is_err());
let edge = db.get_edge(edge_id).unwrap();
assert_eq!(edge.source, alice);
}
#[test]
fn test_delete_node_cascade() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let charlie = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(charlie, alice, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(db.edge_count(), 2);
db.write(|tx| tx.delete_node_cascade(alice)).unwrap();
assert_eq!(db.node_count(), 2); assert_eq!(db.edge_count(), 0); assert!(db.get_node(alice).is_err());
}
#[test]
fn test_delete_edge_via_transaction() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = db
.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(db.edge_count(), 1);
db.write(|tx| tx.delete_edge(edge_id)).unwrap();
assert_eq!(db.edge_count(), 0);
assert!(db.get_edge(edge_id).is_err());
}
#[test]
fn test_delete_nonexistent_node() {
let db = AletheiaDB::new().unwrap();
let result: Result<()> = db.write(|tx| tx.delete_node(NodeId::new(9999).unwrap()));
assert!(result.is_err());
}
#[test]
fn test_delete_nonexistent_edge() {
let db = AletheiaDB::new().unwrap();
let result: Result<()> =
db.write(|tx| tx.delete_edge(crate::core::id::EdgeId::new(9999).unwrap()));
assert!(result.is_err());
}
#[test]
fn test_scan_nodes_by_label() {
let db = AletheiaDB::new().unwrap();
let p1 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let p2 = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_node("Company", PropertyMapBuilder::new().build())
.unwrap();
let mut persons: Vec<NodeId> = db.scan_nodes_by_label("Person").collect();
persons.sort();
let mut expected = vec![p1, p2];
expected.sort();
assert_eq!(persons, expected);
let companies: Vec<NodeId> = db.scan_nodes_by_label("Company").collect();
assert_eq!(companies.len(), 1);
let empty: Vec<NodeId> = db.scan_nodes_by_label("NonExistent").collect();
assert!(empty.is_empty());
}
#[test]
fn test_outgoing_edges_for_node_with_no_edges() {
let db = AletheiaDB::new().unwrap();
let node = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let outgoing = db.get_outgoing_edges(node);
assert!(outgoing.is_empty());
let outgoing_iter: Vec<_> = db.get_outgoing_edges_iter(node).collect();
assert!(outgoing_iter.is_empty());
}
#[test]
fn test_incoming_edges_for_node_with_no_edges() {
let db = AletheiaDB::new().unwrap();
let node = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let incoming = db.get_incoming_edges(node);
assert!(incoming.is_empty());
let incoming_iter: Vec<_> = db.get_incoming_edges_iter(node).collect();
assert!(incoming_iter.is_empty());
}
#[test]
fn test_outgoing_edges_with_label_no_matches() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let edges = db.get_outgoing_edges_with_label(alice, "FOLLOWS");
assert!(edges.is_empty());
}
#[test]
fn test_node_and_edge_counts_empty_db() {
let db = AletheiaDB::new().unwrap();
assert_eq!(db.node_count(), 0);
assert_eq!(db.edge_count(), 0);
}
#[test]
fn test_out_degree_and_in_degree() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let charlie = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, charlie, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(db.out_degree(alice), 2);
assert_eq!(db.in_degree(alice), 0);
assert_eq!(db.out_degree(bob), 0);
assert_eq!(db.in_degree(bob), 1);
}
#[test]
fn test_iterator_vs_vec_consistency() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let charlie = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, charlie, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(charlie, alice, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
let mut vec_result = db.get_outgoing_edges(alice);
let mut iter_result: Vec<_> = db.get_outgoing_edges_iter(alice).collect();
vec_result.sort();
iter_result.sort();
assert_eq!(vec_result, iter_result);
let mut vec_result = db.get_incoming_edges(alice);
let mut iter_result: Vec<_> = db.get_incoming_edges_iter(alice).collect();
vec_result.sort();
iter_result.sort();
assert_eq!(vec_result, iter_result);
}
#[test]
fn test_write_with_timestamp() {
let db = AletheiaDB::new().unwrap();
let (node_id, commit_ts) = db
.write_with_timestamp(|tx| {
tx.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
})
.unwrap();
assert_eq!(db.node_count(), 1);
assert!(commit_ts.wallclock() > 0);
let node = db.get_node_at_time(node_id, commit_ts, commit_ts).unwrap();
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Alice")
);
}
#[test]
fn test_write_with_options_default() {
use crate::storage::wal::WriteOptions;
let db = AletheiaDB::new().unwrap();
let options = WriteOptions::new();
let node_id = db
.write_with_options(options, |tx| {
tx.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Bob").build(),
)
})
.unwrap();
assert_eq!(db.node_count(), 1);
let node = db.get_node(node_id).unwrap();
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Bob")
);
}
#[test]
fn test_concurrent_read_transactions() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let tx1 = db.read_transaction().unwrap();
let tx2 = db.read_transaction().unwrap();
let node1 = tx1.get_node(node_id).unwrap();
let node2 = tx2.get_node(node_id).unwrap();
assert_eq!(node1.id, node2.id);
assert_eq!(node1.get_property("name"), node2.get_property("name"));
}
#[test]
fn test_write_transaction_commit_then_rollback_path() {
let db = AletheiaDB::new().unwrap();
let mut tx1 = db.write_transaction().unwrap();
tx1.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
tx1.commit().unwrap();
assert_eq!(db.node_count(), 1);
let mut tx2 = db.write_transaction().unwrap();
tx2.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
drop(tx2); assert_eq!(db.node_count(), 1); }
#[test]
fn test_write_closure_error_propagation() {
let db = AletheiaDB::new().unwrap();
let result: Result<()> = db.write(|_tx| {
Err(Error::Storage(
crate::core::error::StorageError::InconsistentState {
reason: "custom test error".to_string(),
},
))
});
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("custom test error"),
"Error should contain our message: {}",
err_msg
);
}
#[test]
fn test_read_closure_error_propagation() {
let db = AletheiaDB::new().unwrap();
let result: Result<()> = db.read(|_tx| {
Err(Error::Storage(
crate::core::error::StorageError::InconsistentState {
reason: "read error".to_string(),
},
))
});
assert!(result.is_err());
}
#[test]
fn test_refresh_statistics() {
let db = AletheiaDB::new().unwrap();
db.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_node("Company", PropertyMapBuilder::new().build())
.unwrap();
db.refresh_statistics();
let stats = db.statistics();
assert!(stats.node_count() >= 3);
}
#[test]
fn test_invalidate_statistics() {
let db = AletheiaDB::new().unwrap();
db.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.refresh_statistics();
db.invalidate_statistics();
let _stats = db.statistics();
}
#[test]
fn test_persist_indexes_without_persistence_enabled() {
let db = AletheiaDB::new().unwrap();
let result = db.persist_indexes();
assert!(result.is_err());
}
#[test]
fn test_historical_stats_empty_db() {
let db = AletheiaDB::new().unwrap();
let stats = db.historical_stats().unwrap();
assert_eq!(stats.total_node_versions, 0);
assert_eq!(stats.node_anchor_count, 0);
}
#[test]
fn test_test_current_wal_lsn() {
let db = AletheiaDB::new().unwrap();
let lsn_before = db.__test_current_wal_lsn();
db.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let lsn_after = db.__test_current_wal_lsn();
assert!(
lsn_after > lsn_before,
"LSN should advance after operations"
);
}
#[test]
fn test_test_current_timestamp() {
let db = AletheiaDB::new().unwrap();
let ts = db.__test_current_timestamp();
assert!(ts.wallclock() > 0, "Timestamp should be non-zero");
}
#[test]
fn test_get_node_at_time_nonexistent_node() {
let db = AletheiaDB::new().unwrap();
let now = crate::core::temporal::time::now();
let result = db.get_node_at_time(NodeId::new(9999).unwrap(), now, now);
assert!(result.is_err());
}
#[test]
fn test_get_edge_at_time_nonexistent_edge() {
let db = AletheiaDB::new().unwrap();
let now = crate::core::temporal::time::now();
let result = db.get_edge_at_time(crate::core::id::EdgeId::new(9999).unwrap(), now, now);
assert!(result.is_err());
}
#[test]
fn test_get_nodes_at_time_batch() {
let db = AletheiaDB::new().unwrap();
let (n1, ts1) = db
.write_with_timestamp(|tx| {
tx.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
})
.unwrap();
let (n2, ts2) = db
.write_with_timestamp(|tx| {
tx.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Bob").build(),
)
})
.unwrap();
let query_ts = std::cmp::max(ts1, ts2);
let results = db.get_nodes_at_time(&[n1, n2], query_ts, query_ts).unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].0, n1);
assert!(results[0].1.is_some());
assert_eq!(results[1].0, n2);
assert!(results[1].1.is_some());
}
#[test]
fn test_get_nodes_at_time_batch_with_nonexistent() {
let db = AletheiaDB::new().unwrap();
let (n1, ts) = db
.write_with_timestamp(|tx| tx.create_node("Person", PropertyMapBuilder::new().build()))
.unwrap();
let fake_id = NodeId::new(9999).unwrap();
let results = db.get_nodes_at_time(&[n1, fake_id], ts, ts).unwrap();
assert_eq!(results.len(), 2);
assert!(results[0].1.is_some()); assert!(results[1].1.is_none()); }
#[test]
fn test_get_nodes_at_time_empty_batch() {
let db = AletheiaDB::new().unwrap();
let now = crate::core::temporal::time::now();
let results = db.get_nodes_at_time(&[], now, now).unwrap();
assert!(results.is_empty());
}
#[test]
fn test_get_edges_at_time_batch() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let (e1, ts) = db
.write_with_timestamp(|tx| {
tx.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
})
.unwrap();
let results = db.get_edges_at_time(&[e1], ts, ts).unwrap();
assert_eq!(results.len(), 1);
assert!(results[0].1.is_some());
}
#[test]
fn test_get_edges_at_time_empty_batch() {
let db = AletheiaDB::new().unwrap();
let now = crate::core::temporal::time::now();
let results = db.get_edges_at_time(&[], now, now).unwrap();
assert!(results.is_empty());
}
#[test]
fn test_get_outgoing_edges_at_time() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let (_, ts) = db
.write_with_timestamp(|tx| {
tx.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
})
.unwrap();
let edges = db.get_outgoing_edges_at_time(alice, ts, ts);
assert_eq!(edges.len(), 1);
}
#[test]
fn test_get_incoming_edges_at_time() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let (_, ts) = db
.write_with_timestamp(|tx| {
tx.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
})
.unwrap();
let edges = db.get_incoming_edges_at_time(bob, ts, ts);
assert_eq!(edges.len(), 1);
}
#[test]
fn test_get_node_history_nonexistent() {
let db = AletheiaDB::new().unwrap();
let result = db.get_node_history(NodeId::new(9999).unwrap());
assert!(result.is_err());
}
#[test]
fn test_get_node_at_version_nonexistent() {
let db = AletheiaDB::new().unwrap();
let result = db.get_node_at_version(NodeId::new(9999).unwrap(), 1);
assert!(result.is_err());
}
#[test]
fn test_get_node_at_version_invalid_version() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let result = db.get_node_at_version(node_id, 0);
assert!(result.is_err());
}
#[test]
fn test_get_node_at_version_beyond_latest() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let result = db.get_node_at_version(node_id, 999);
assert!(result.is_err());
}
#[test]
fn test_get_edge_at_valid_time() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let (edge_id, ts) = db
.write_with_timestamp(|tx| {
tx.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2020i64).build(),
)
})
.unwrap();
let edge = db.get_edge_at_valid_time(edge_id, ts).unwrap();
assert_eq!(edge.source, alice);
assert_eq!(edge.target, bob);
}
#[test]
fn test_get_edge_at_transaction_time() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = db
.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2020i64).build(),
)
.unwrap();
let tx_time = crate::core::temporal::time::now();
let edge = db.get_edge_at_transaction_time(edge_id, tx_time).unwrap();
assert_eq!(edge.source, alice);
}
#[test]
fn test_get_edge_history_nonexistent() {
let db = AletheiaDB::new().unwrap();
let result = db.get_edge_history(crate::core::id::EdgeId::new(9999).unwrap());
assert!(result.is_err());
}
#[test]
fn test_diff_node_versions_same_version() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let history = db.get_node_history(node_id).unwrap();
let v1_id = history.first_version().unwrap().version_id;
let diff = db.diff_node_versions(node_id, v1_id, v1_id).unwrap();
assert!(!diff.has_changes());
}
#[test]
fn test_find_similar_without_index() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let result = db.find_similar(node_id, 10);
assert!(result.is_err());
}
#[test]
fn test_find_similar_by_embedding_without_index() {
let db = AletheiaDB::new().unwrap();
let embedding = vec![0.1, 0.2, 0.3];
let result = db.find_similar_by_embedding(&embedding, 10);
assert!(result.is_err());
}
#[test]
fn test_search_vectors_in_without_index() {
let db = AletheiaDB::new().unwrap();
let embedding = vec![0.1, 0.2, 0.3];
let result = db.search_vectors_in("embedding", &embedding, 10);
assert!(result.is_err());
}
#[test]
fn test_find_similar_in_without_index() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let result = db.find_similar_in("embedding", node_id, 10);
assert!(result.is_err());
}
#[test]
fn test_vector_index_builder_basic() {
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
db.vector_index("embedding")
.hnsw(HnswConfig::new(128, DistanceMetric::Cosine))
.enable()
.unwrap();
assert!(db.has_vector_index("embedding"));
assert!(!db.has_vector_index("other"));
}
#[test]
fn test_list_vector_indexes() {
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
assert!(db.list_vector_indexes().is_empty());
db.enable_vector_index("embedding", HnswConfig::new(128, DistanceMetric::Cosine))
.unwrap();
let indexes = db.list_vector_indexes();
assert_eq!(indexes.len(), 1);
assert_eq!(indexes[0].property_name, "embedding");
}
#[test]
fn test_enable_vector_index_duplicate() {
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
db.enable_vector_index("embedding", HnswConfig::new(128, DistanceMetric::Cosine))
.unwrap();
let result = db.enable_vector_index("embedding", HnswConfig::new(128, DistanceMetric::Cosine));
assert!(result.is_err());
}
#[test]
fn test_find_similar_with_label_without_matches() {
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
db.enable_vector_index("embedding", HnswConfig::new(4, DistanceMetric::Cosine))
.unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert_vector("embedding", &[1.0, 0.0, 0.0, 0.0])
.build(),
)
.unwrap();
let results = db
.find_similar_with_label(node_id, "NonExistentLabel", 10)
.unwrap();
assert!(results.is_empty());
}
#[test]
fn test_find_similar_by_embedding_with_label() {
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
db.enable_vector_index("embedding", HnswConfig::new(4, DistanceMetric::Cosine))
.unwrap();
db.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Alice")
.insert_vector("embedding", &[1.0, 0.0, 0.0, 0.0])
.build(),
)
.unwrap();
db.create_node(
"Company",
PropertyMapBuilder::new()
.insert("name", "Acme")
.insert_vector("embedding", &[0.9, 0.1, 0.0, 0.0])
.build(),
)
.unwrap();
let query = vec![1.0, 0.0, 0.0, 0.0];
let results = db
.find_similar_by_embedding_with_label(&query, "Person", 10)
.unwrap();
assert_eq!(results.len(), 1);
let results = db
.find_similar_by_embedding_with_label(&query, "Company", 10)
.unwrap();
assert_eq!(results.len(), 1);
let results = db
.find_similar_by_embedding_with_label(&query, "NoLabel", 10)
.unwrap();
assert!(results.is_empty());
}
#[test]
fn test_is_temporal_vector_index_enabled() {
let db = AletheiaDB::new().unwrap();
assert!(!db.is_temporal_vector_index_enabled());
}
#[test]
fn test_list_temporal_vector_indexes_empty() {
let db = AletheiaDB::new().unwrap();
assert!(db.list_temporal_vector_indexes().is_empty());
}
#[test]
fn test_find_similar_as_of_without_temporal_index() {
let db = AletheiaDB::new().unwrap();
let now = crate::core::temporal::time::now();
let result = db.find_similar_as_of(&[0.1, 0.2, 0.3], 10, now);
assert!(result.is_err());
}
#[test]
fn test_update_node_via_transaction() {
let db = AletheiaDB::new().unwrap();
let node_id = db
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build(),
)
.unwrap();
db.write(|tx| {
tx.update_node(
node_id,
PropertyMapBuilder::new()
.insert("name", "Alice Updated")
.insert("age", 31i64)
.build(),
)
})
.unwrap();
let node = db.get_node(node_id).unwrap();
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Alice Updated")
);
assert_eq!(node.get_property("age").and_then(|v| v.as_int()), Some(31));
}
#[test]
fn test_update_nonexistent_node() {
let db = AletheiaDB::new().unwrap();
let result: Result<()> = db.write(|tx| {
tx.update_node(
NodeId::new(9999).unwrap(),
PropertyMapBuilder::new().build(),
)
});
assert!(result.is_err());
}
#[test]
fn test_update_edge_via_transaction() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = db
.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new().insert("weight", 1.0f64).build(),
)
.unwrap();
db.write(|tx| {
tx.update_edge(
edge_id,
PropertyMapBuilder::new().insert("weight", 2.0f64).build(),
)
})
.unwrap();
let edge = db.get_edge(edge_id).unwrap();
assert_eq!(
edge.properties.get("weight"),
Some(&PropertyValue::Float(2.0))
);
}
#[test]
fn test_update_nonexistent_edge() {
let db = AletheiaDB::new().unwrap();
let result: Result<()> = db.write(|tx| {
tx.update_edge(
crate::core::id::EdgeId::new(9999).unwrap(),
PropertyMapBuilder::new().build(),
)
});
assert!(result.is_err());
}
#[test]
fn test_multiple_creates_in_single_transaction() {
let db = AletheiaDB::new().unwrap();
let node_ids = db
.write(|tx| {
let mut ids = Vec::new();
for i in 0..10 {
let id = tx.create_node(
"Item",
PropertyMapBuilder::new().insert("index", i as i64).build(),
)?;
ids.push(id);
}
Ok::<_, Error>(ids)
})
.unwrap();
assert_eq!(node_ids.len(), 10);
assert_eq!(db.node_count(), 10);
}
#[test]
fn test_create_then_delete_edge_across_transactions() {
let db = AletheiaDB::new().unwrap();
let (n1, n2, edge_id) = db
.write(|tx| {
let n1 = tx.create_node("Person", PropertyMapBuilder::new().build())?;
let n2 = tx.create_node("Person", PropertyMapBuilder::new().build())?;
let e = tx.create_edge(n1, n2, "KNOWS", PropertyMapBuilder::new().build())?;
Ok::<_, Error>((n1, n2, e))
})
.unwrap();
assert_eq!(db.node_count(), 2);
assert_eq!(db.edge_count(), 1);
db.write(|tx| tx.delete_edge(edge_id)).unwrap();
assert_eq!(db.edge_count(), 0);
assert!(db.get_node(n1).is_ok());
assert!(db.get_node(n2).is_ok());
}
#[test]
fn test_self_edge() {
let db = AletheiaDB::new().unwrap();
let node = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = db
.create_edge(node, node, "SELF_REF", PropertyMapBuilder::new().build())
.unwrap();
let edge = db.get_edge(edge_id).unwrap();
assert_eq!(edge.source, node);
assert_eq!(edge.target, node);
assert_eq!(db.out_degree(node), 1);
assert_eq!(db.in_degree(node), 1);
}
#[test]
fn test_multiple_edges_same_nodes() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(alice, bob, "WORKS_WITH", PropertyMapBuilder::new().build())
.unwrap();
db.create_edge(bob, alice, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(db.edge_count(), 3);
assert_eq!(db.out_degree(alice), 2);
assert_eq!(db.in_degree(alice), 1);
assert_eq!(db.out_degree(bob), 1);
assert_eq!(db.in_degree(bob), 2);
}
#[test]
fn test_debug_implementation() {
let db = AletheiaDB::new().unwrap();
let debug_output = format!("{:?}", db);
assert!(debug_output.contains("AletheiaDB"));
assert!(debug_output.contains("current_timestamp"));
assert!(debug_output.contains("default_durability"));
assert!(debug_output.contains("persistence_enabled"));
assert!(debug_output.contains("stats"));
}
#[cfg(feature = "observability")]
fn poison_mutex<T>(mutex: &std::sync::Arc<std::sync::Mutex<T>>) {
let mutex = std::sync::Arc::clone(mutex);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
let _guard = mutex.lock().expect("failed to lock mutex for poisoning");
panic!("intentional mutex poison for metrics test");
}));
}
#[cfg(feature = "observability")]
#[test]
#[serial_test::serial]
fn test_create_node_transaction_error_counted_once_when_lock_poisoned() {
crate::observability::METRICS.reset();
let db = AletheiaDB::new().unwrap();
poison_mutex(&db.current_timestamp);
let result = db.create_node("Person", PropertyMapBuilder::new().build());
assert!(result.is_err());
let snapshot = crate::observability::METRICS.snapshot();
assert_eq!(snapshot.error_transaction_total, 1);
}
#[cfg(feature = "observability")]
#[test]
#[serial_test::serial]
fn test_vector_builder_duplicate_enable_counts_error_once() {
use crate::index::vector::{DistanceMetric, HnswConfig};
let db = AletheiaDB::new().unwrap();
db.enable_vector_index("embedding", HnswConfig::new(4, DistanceMetric::Cosine))
.unwrap();
crate::observability::METRICS.reset();
let result = db
.vector_index("embedding")
.hnsw(HnswConfig::new(4, DistanceMetric::Cosine))
.enable();
assert!(result.is_err());
let snapshot = crate::observability::METRICS.snapshot();
assert_eq!(snapshot.error_vector_total, 1);
}
#[cfg(feature = "observability")]
#[test]
#[serial_test::serial]
fn test_read_closure_db_error_counts_once() {
crate::observability::METRICS.reset();
let db = AletheiaDB::new().unwrap();
let missing_id = NodeId::new(999_999).unwrap();
let result: Result<()> = db.read(|tx| {
tx.get_node(missing_id)?;
Ok(())
});
assert!(result.is_err());
let snapshot = crate::observability::METRICS.snapshot();
assert_eq!(snapshot.error_storage_total, 1);
}
#[cfg(feature = "observability")]
#[test]
#[serial_test::serial]
fn test_write_commit_error_counts_once() {
crate::observability::METRICS.reset();
let db = AletheiaDB::new().unwrap();
poison_mutex(&db.commit_clock_observed_at);
let result: Result<()> = db.write(|tx| {
tx.create_node("Person", PropertyMapBuilder::new().build())?;
Ok(())
});
assert!(result.is_err());
let snapshot = crate::observability::METRICS.snapshot();
assert_eq!(snapshot.error_transaction_total, 1);
}