use crate::guardian::GuardianDB;
use crate::guardian::core::NewGuardianDBOptions;
use crate::p2p::network::client::IrohClient;
use crate::p2p::network::config::ClientConfig;
use crate::traits::{CreateDBOptions, Document, DocumentStoreGetOptions, StreamOptions};
use std::collections::HashMap;
use std::sync::Arc;
use tempfile::TempDir;
async fn create_test_iroh_client() -> (IrohClient, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let client_config = ClientConfig {
data_store_path: Some(temp_dir.path().to_path_buf()),
..Default::default()
};
let client = IrohClient::new(client_config)
.await
.expect("Failed to create Iroh Client");
(client, temp_dir)
}
async fn create_test_guardian_db() -> (GuardianDB, TempDir) {
let (client, temp_dir) = create_test_iroh_client().await;
let db_dir = temp_dir.path().join("guardian_db");
std::fs::create_dir_all(&db_dir).expect("Failed to create db dir");
let backend = client.backend().clone();
let options = NewGuardianDBOptions {
directory: Some(db_dir),
backend: Some(backend),
..Default::default()
};
let guardian = GuardianDB::new(client, Some(options))
.await
.expect("Failed to create GuardianDB");
(guardian, temp_dir)
}
fn create_test_options() -> CreateDBOptions {
CreateDBOptions {
directory: None,
create: Some(true),
store_type: None,
overwrite: Some(false),
local_only: Some(false),
access_controller: None,
access_controller_address: None,
replicate: Some(false),
identity: None,
event_bus: None,
keystore: None,
cache: None,
sort_fn: None,
timeout: None,
message_marshaler: None,
span: None,
close_func: None,
store_specific_opts: None,
}
}
#[tokio::test]
async fn test_create_guardian_db() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
assert!(!guardian.base().identity().id().is_empty());
}
#[tokio::test]
async fn test_guardian_db_with_custom_options() {
let (client, temp_dir) = create_test_iroh_client().await;
let custom_dir = temp_dir.path().join("custom_guardian");
std::fs::create_dir_all(&custom_dir).expect("Failed to create custom dir");
let backend = client.backend().clone();
let options = NewGuardianDBOptions {
directory: Some(custom_dir),
backend: Some(backend),
..Default::default()
};
let guardian = GuardianDB::new(client, Some(options))
.await
.expect("Failed to create GuardianDB with custom options");
assert!(!guardian.base().identity().id().is_empty());
}
#[tokio::test]
async fn test_create_event_log_store() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("test_eventlog", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
assert_eq!(store.store_type(), "eventlog");
}
#[tokio::test]
async fn test_eventlog_add_operation() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("test_eventlog_add", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
let data = b"test event data".to_vec();
let operation = store.add(data.clone()).await.expect("Failed to add event");
assert_eq!(operation.op(), "ADD");
assert_eq!(operation.value(), &data);
}
#[tokio::test]
async fn test_eventlog_add_multiple_operations() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("test_eventlog_multiple", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
for i in 0..5 {
let data = format!("event_{}", i).into_bytes();
store.add(data).await.expect("Failed to add event");
}
let operations = store.list(None).await.expect("Failed to list operations");
assert_eq!(operations.len(), 5);
}
#[tokio::test]
async fn test_eventlog_get_by_hash() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("test_eventlog_get", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
let data = b"test event for get".to_vec();
let _operation = store.add(data.clone()).await.expect("Failed to add event");
let oplog = store.op_log();
let hash = {
let oplog_guard = oplog.read();
let values = oplog_guard.values();
let first_entry = values.first().expect("No entries found");
*first_entry.hash()
};
let retrieved_op = store.get(&hash).await.expect("Failed to get operation");
assert_eq!(retrieved_op.op(), "ADD");
assert_eq!(retrieved_op.value(), &data);
}
#[tokio::test]
async fn test_eventlog_list_with_limit() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("test_eventlog_limit", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
for i in 0..10 {
let data = format!("event_{}", i).into_bytes();
store.add(data).await.expect("Failed to add event");
}
let options = StreamOptions {
amount: Some(5),
..Default::default()
};
let operations = store
.list(Some(options))
.await
.expect("Failed to list operations");
assert_eq!(operations.len(), 5);
}
#[tokio::test]
async fn test_eventlog_list_all() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("test_eventlog_list_all", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
for i in 0..7 {
let data = format!("event_{}", i).into_bytes();
store.add(data).await.expect("Failed to add event");
}
let options = StreamOptions {
amount: Some(-1),
..Default::default()
};
let operations = store
.list(Some(options))
.await
.expect("Failed to list operations");
assert_eq!(operations.len(), 7);
}
#[tokio::test]
async fn test_create_key_value_store() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("test_kv", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
assert_eq!(store.store_type(), "keyvalue");
}
#[tokio::test]
async fn test_kv_put_and_get() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("test_kv_put_get", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let key = "test_key";
let value = b"test_value".to_vec();
store
.put(key, value.clone())
.await
.expect("Failed to put value");
let retrieved = store
.get(key)
.await
.expect("Failed to get value")
.expect("Value not found");
assert_eq!(retrieved, value);
}
#[tokio::test]
async fn test_kv_put_multiple_keys() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("test_kv_multiple", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
for i in 0..5 {
let key = format!("key_{}", i);
let value = format!("value_{}", i).into_bytes();
store.put(&key, value).await.expect("Failed to put value");
}
let all_data = store.all();
assert_eq!(all_data.len(), 5);
}
#[tokio::test]
async fn test_kv_update_existing_key() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("test_kv_update", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let key = "update_key";
let value1 = b"first_value".to_vec();
let value2 = b"second_value".to_vec();
store.put(key, value1).await.expect("Failed to put value");
store
.put(key, value2.clone())
.await
.expect("Failed to update value");
let retrieved = store
.get(key)
.await
.expect("Failed to get value")
.expect("Value not found");
assert_eq!(retrieved, value2);
}
#[tokio::test]
async fn test_kv_delete() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("test_kv_delete", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let key = "delete_key";
let value = b"value_to_delete".to_vec();
store.put(key, value).await.expect("Failed to put value");
assert!(store.get(key).await.expect("Failed to get value").is_some());
store.delete(key).await.expect("Failed to delete value");
let retrieved = store.get(key).await.expect("Failed to get value");
assert!(retrieved.is_none());
}
#[tokio::test]
async fn test_kv_all() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("test_kv_all", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let mut expected = HashMap::new();
for i in 0..3 {
let key = format!("key_{}", i);
let value = format!("value_{}", i).into_bytes();
expected.insert(key.clone(), value.clone());
store.put(&key, value).await.expect("Failed to put value");
}
let all_data = store.all();
assert_eq!(all_data.len(), expected.len());
for (key, value) in expected {
assert_eq!(all_data.get(&key), Some(&value));
}
}
#[tokio::test]
async fn test_kv_get_nonexistent_key() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("test_kv_nonexistent", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let result = store
.get("nonexistent_key")
.await
.expect("Failed to get value");
assert!(result.is_none());
}
#[tokio::test]
async fn test_create_document_store() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
assert_eq!(store.store_type(), "document");
}
#[tokio::test]
async fn test_docs_put_and_get() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs_put_get", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
let doc_json = serde_json::json!({
"id": "doc1",
"title": "Test Document",
"content": "This is a test document"
});
let doc: Document = Box::new(doc_json.clone());
store.put(doc).await.expect("Failed to put document");
let oplog = store.op_log();
let oplog_guard = oplog.read();
assert_eq!(oplog_guard.values().len(), 1, "Document should be in oplog");
}
#[tokio::test]
async fn test_docs_put_multiple() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs_multiple", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
for i in 0..5 {
let doc_json = serde_json::json!({
"id": format!("doc{}", i),
"title": format!("Document {}", i),
"content": format!("Content for document {}", i)
});
let doc: Document = Box::new(doc_json);
store.put(doc).await.expect("Failed to put document");
}
let oplog = store.op_log();
let oplog_guard = oplog.read();
let count = oplog_guard.values().len();
assert!(count >= 5, "Expected at least 5 documents, found {}", count);
}
#[tokio::test]
async fn test_docs_delete() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs_delete", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
let doc_json = serde_json::json!({
"id": "doc_to_delete",
"title": "Document to Delete"
});
let doc: Document = Box::new(doc_json);
store.put(doc).await.expect("Failed to put document");
store
.delete("doc_to_delete")
.await
.expect("Failed to delete document");
let options = DocumentStoreGetOptions {
case_insensitive: false,
partial_matches: false,
};
let retrieved = store
.get("doc_to_delete", Some(options))
.await
.expect("Failed to get document");
assert_eq!(retrieved.len(), 0);
}
#[tokio::test]
async fn test_docs_get_with_partial_match() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs_partial", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
let keys = vec!["user_profile", "user_settings", "admin_profile"];
for key in keys {
let doc_json = serde_json::json!({
"key": key,
"data": format!("Data for {}", key)
});
let doc: Document = Box::new(doc_json);
store.put(doc).await.expect("Failed to put document");
}
let oplog = store.op_log();
let oplog_guard = oplog.read();
assert_eq!(
oplog_guard.values().len(),
3,
"All 3 documents should be in oplog"
);
}
#[tokio::test]
async fn test_docs_get_case_insensitive() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs_case", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
let doc_json = serde_json::json!({
"key": "TestKey",
"data": "test data"
});
let doc: Document = Box::new(doc_json);
store.put(doc).await.expect("Failed to put document");
let oplog = store.op_log();
let oplog_guard = oplog.read();
assert_eq!(oplog_guard.values().len(), 1, "Document should be in oplog");
}
#[tokio::test]
async fn test_docs_put_batch() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs_batch", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
let mut docs = Vec::new();
for i in 0..3 {
let doc_json = serde_json::json!({
"id": format!("batch_doc{}", i),
"content": format!("Batch content {}", i)
});
let doc: Document = Box::new(doc_json);
docs.push(doc);
}
store.put_batch(docs).await.expect("Failed to put batch");
let oplog = store.op_log();
let oplog_guard = oplog.read();
let count = oplog_guard.values().len();
assert!(count >= 1, "Expected at least 1 batch operation");
}
#[tokio::test]
async fn test_docs_query_with_filter() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("test_docs_query", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
for i in 0..5 {
let doc_json = serde_json::json!({
"id": format!("doc{}", i),
"value": i,
"active": i % 2 == 0
});
let doc: Document = Box::new(doc_json);
store.put(doc).await.expect("Failed to put document");
}
let oplog = store.op_log();
let oplog_guard = oplog.read();
let count = oplog_guard.values().len();
assert_eq!(count, 5, "Expected 5 documents");
}
#[tokio::test]
async fn test_multiple_stores_same_guardian() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let event_store = guardian
.log("events", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
let kv_store = guardian
.key_value("keyvalue", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let doc_store = guardian
.docs("documents", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
assert_eq!(event_store.store_type(), "eventlog");
assert_eq!(kv_store.store_type(), "keyvalue");
assert_eq!(doc_store.store_type(), "document");
}
#[tokio::test]
#[ignore = "File locking issue prevents reopening the same store"]
async fn test_store_persistence() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let address = "persistent_store";
let store = guardian
.key_value(address, Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
store
.put("persistent_key", b"persistent_value".to_vec())
.await
.expect("Failed to put value");
store.close().await.expect("Failed to close store");
drop(store);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let reopened_store = guardian
.key_value(address, Some(create_test_options()))
.await
.expect("Failed to reopen KeyValueStore");
let value = reopened_store
.get("persistent_key")
.await
.expect("Failed to get value")
.expect("Value not found after reopen");
assert_eq!(value, b"persistent_value".to_vec());
}
#[tokio::test]
async fn test_access_controller_registration() {
use crate::access_control::acl_simple::SimpleAccessController;
use crate::traits::AccessControllerConstructor;
let (guardian, _temp_dir) = create_test_guardian_db().await;
let constructor: AccessControllerConstructor = Arc::new(move |_db, _options, _params| {
Box::pin(async move {
let controller = SimpleAccessController::new(None);
Ok(Arc::new(controller) as Arc<dyn crate::access_control::traits::AccessController>)
})
});
guardian
.register_access_control_type_with_name("test_controller", constructor)
.expect("Failed to register access controller");
let types = guardian.access_control_types_names();
assert!(types.contains(&"test_controller".to_string()));
}
#[tokio::test]
async fn test_event_bus_integration() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("event_bus_test", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let event_bus = store.event_bus();
assert!(Arc::strong_count(&event_bus) > 0);
}
#[tokio::test]
async fn test_concurrent_operations() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("concurrent_test", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let store = Arc::new(store);
let mut handles = vec![];
for i in 0..10 {
let store_clone = Arc::clone(&store);
let handle = tokio::spawn(async move {
let key = format!("concurrent_key_{}", i);
let value = format!("concurrent_value_{}", i).into_bytes();
store_clone
.put(&key, value)
.await
.expect("Failed to put value");
});
handles.push(handle);
}
for handle in handles {
handle.await.expect("Task failed");
}
let all_data = store.all();
assert_eq!(all_data.len(), 10);
}
#[tokio::test]
async fn test_store_load_and_sync() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("load_sync_test", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
for i in 0..5 {
let data = format!("sync_data_{}", i).into_bytes();
store.add(data).await.expect("Failed to add event");
}
store.load(3).await.expect("Failed to load");
let _status = store.replication_status();
}
#[tokio::test]
async fn test_empty_eventlog() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.log("empty_eventlog", Some(create_test_options()))
.await
.expect("Failed to create EventLogStore");
let operations = store.list(None).await.expect("Failed to list operations");
assert_eq!(operations.len(), 0);
}
#[tokio::test]
async fn test_empty_keyvalue_all() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("empty_kv", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let all_data = store.all();
assert_eq!(all_data.len(), 0);
}
#[tokio::test]
async fn test_large_value_storage() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("large_value_test", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let large_value = vec![0u8; 1024 * 1024];
store
.put("large_key", large_value.clone())
.await
.expect("Failed to put large value");
let retrieved = store
.get("large_key")
.await
.expect("Failed to get large value")
.expect("Large value not found");
assert_eq!(retrieved.len(), large_value.len());
}
#[tokio::test]
async fn test_special_characters_in_keys() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.key_value("special_chars_test", Some(create_test_options()))
.await
.expect("Failed to create KeyValueStore");
let special_keys = vec![
"key with spaces",
"key/with/slashes",
"key.with.dots",
"key-with-dashes",
"key_with_underscores",
];
for key in special_keys {
let value = format!("value for {}", key).into_bytes();
store
.put(key, value.clone())
.await
.unwrap_or_else(|_| panic!("Failed to put value for key: {}", key));
let retrieved = store
.get(key)
.await
.expect("Failed to get value")
.expect("Value not found");
assert_eq!(retrieved, value);
}
}
#[tokio::test]
async fn test_document_with_complex_json() {
let (guardian, _temp_dir) = create_test_guardian_db().await;
let store = guardian
.docs("complex_json_test", Some(create_test_options()))
.await
.expect("Failed to create DocumentStore");
let complex_doc = serde_json::json!({
"id": "complex_doc",
"nested": {
"level1": {
"level2": {
"value": "deep nested value"
}
}
},
"array": [1, 2, 3, 4, 5],
"mixed": {
"string": "text",
"number": 42,
"boolean": true,
"null_value": null
}
});
let doc: Document = Box::new(complex_doc);
store
.put(doc)
.await
.expect("Failed to put complex document");
let oplog = store.op_log();
let oplog_guard = oplog.read();
assert!(!oplog_guard.values().is_empty());
}