#[cfg(test)]
mod guardian_core_tests {
use crate::guardian::core::{
Emitters, EventExchangeHeads, EventGuardianDBReady, GuardianDB, NewGuardianDBOptions,
};
use crate::guardian::error::{GuardianError, Result};
use crate::log::entry::Entry;
use crate::log::identity::{Identity, Signatures};
use crate::p2p::EventBus;
use crate::p2p::network::client::IrohClient;
use crate::p2p::network::config::ClientConfig;
use crate::p2p::network::core::IrohBackend;
use crate::traits::{CreateDBOptions, MessageExchangeHeads};
use iroh::EndpointId as NodeId;
use iroh_blobs::Hash;
use std::ops::Deref;
use std::sync::Arc;
use tempfile::TempDir;
fn create_test_identity(id: &str) -> Identity {
let signatures = Signatures::new(&format!("sig_id_{}", id), &format!("sig_pubkey_{}", id));
Identity::new(id, &format!("pubkey_{}", id), signatures)
}
fn create_test_node_id(seed: u8) -> NodeId {
let secret = iroh::SecretKey::from_bytes(&[seed; 32]);
secret.public()
}
fn create_test_hash(seed: u8) -> Hash {
let mut bytes = [0u8; 32];
bytes[0] = seed;
Hash::from(bytes)
}
fn create_test_entry(identity: Identity, payload: &str, seed: u8) -> Entry {
Entry::new(
identity,
&format!("log_{}", seed),
payload.as_bytes(),
&[],
None,
)
}
async fn create_test_backend(name: &str) -> Result<(Arc<IrohBackend>, TempDir)> {
let temp_dir = TempDir::new()
.map_err(|e| GuardianError::Other(format!("Failed to create temp dir: {}", e)))?;
let client_config = ClientConfig {
data_store_path: Some(temp_dir.path().join(name)),
..Default::default()
};
let backend = Arc::new(IrohBackend::new(&client_config).await?);
Ok((backend, temp_dir))
}
async fn create_test_guardian_db(
name: &str,
) -> Result<(GuardianDB, Arc<IrohBackend>, TempDir)> {
let (backend, temp_dir) = create_test_backend(name).await?;
let client = IrohClient::new_with_backend(backend.clone()).await?;
let identity = create_test_identity(&format!("guardian_{}", name));
let options = NewGuardianDBOptions {
directory: Some(temp_dir.path().join("db").to_path_buf()),
backend: Some(backend.clone()),
..Default::default()
};
let guardian = GuardianDB::new_guardian_db(client, identity, Some(options)).await?;
Ok((guardian, backend, temp_dir))
}
#[tokio::test]
async fn test_guardian_db_initialization() {
let result = create_test_guardian_db("init_test").await;
assert!(result.is_ok(), "Failed to create GuardianDB");
let (guardian, _, _temp_dir) = result.unwrap();
assert!(!guardian.identity().id().is_empty());
assert_ne!(guardian.node_id(), NodeId::from_bytes(&[0; 32]).unwrap());
}
#[tokio::test]
async fn test_guardian_db_with_custom_options() {
let temp_dir = TempDir::new().unwrap();
let client_config = ClientConfig {
data_store_path: Some(temp_dir.path().to_path_buf()),
..Default::default()
};
let backend = Arc::new(IrohBackend::new(&client_config).await.unwrap());
let client = IrohClient::new_with_backend(backend.clone()).await.unwrap();
let identity = create_test_identity("custom_test");
let options = NewGuardianDBOptions {
directory: Some(temp_dir.path().join("custom_db").to_path_buf()),
backend: Some(backend),
..Default::default()
};
let result = GuardianDB::new_guardian_db(client, identity.clone(), Some(options)).await;
assert!(result.is_ok());
let guardian = result.unwrap();
assert_eq!(guardian.identity().id(), identity.id());
}
#[tokio::test]
async fn test_guardian_db_event_bus_initialization() {
let (guardian, _, _temp_dir) = create_test_guardian_db("eventbus_test").await.unwrap();
let event_bus = guardian.event_bus();
assert!(Arc::strong_count(&event_bus) >= 1);
}
#[tokio::test]
async fn test_emitters_generation() {
let event_bus = Arc::new(EventBus::new());
let result = Emitters::generate_emitters(&event_bus).await;
assert!(result.is_ok(), "Failed to generate emitters");
}
#[tokio::test]
async fn test_set_and_get_store() {
let (guardian, _, _temp_dir) = create_test_guardian_db("store_mgmt_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let store_result = guardian
.create("test_store", "eventlog", Some(options))
.await;
assert!(store_result.is_ok(), "Failed to create store");
let store = store_result.unwrap();
let address = store.address().to_string();
let retrieved = guardian.get_store(&address);
assert!(retrieved.is_some(), "Store not found");
let retrieved_store = retrieved.unwrap();
assert_eq!(retrieved_store.address().to_string(), address);
}
#[tokio::test]
async fn test_delete_store() {
let (guardian, _, _temp_dir) = create_test_guardian_db("delete_store_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("keyvalue".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let store = guardian
.create("delete_test", "keyvalue", Some(options))
.await
.unwrap();
let address = store.address().to_string();
assert!(guardian.get_store(&address).is_some());
guardian.delete_store(&address);
assert!(guardian.get_store(&address).is_none());
}
#[tokio::test]
async fn test_store_types_registration() {
let (guardian, _, _temp_dir) = create_test_guardian_db("store_types_test").await.unwrap();
let types = guardian.store_types_names();
assert!(types.contains(&"eventlog".to_string()));
assert!(types.contains(&"keyvalue".to_string()));
assert!(types.contains(&"document".to_string()));
assert_eq!(types.len(), 3, "Expected 3 default store types");
}
#[tokio::test]
async fn test_get_store_constructor() {
let (guardian, _, _temp_dir) = create_test_guardian_db("store_constructor_test")
.await
.unwrap();
assert!(guardian.get_store_constructor("eventlog").is_some());
assert!(guardian.get_store_constructor("keyvalue").is_some());
assert!(guardian.get_store_constructor("document").is_some());
assert!(guardian.get_store_constructor("nonexistent").is_none());
}
#[tokio::test]
async fn test_access_controller_registration() {
let (guardian, _, _temp_dir) = create_test_guardian_db("ac_registration_test")
.await
.unwrap();
let result = guardian.register_default_access_control_types().await;
assert!(result.is_ok(), "Failed to register default AC types");
let types = guardian.access_control_types_names();
assert!(types.contains(&"simple".to_string()));
}
#[tokio::test]
async fn test_get_access_control_type() {
let (guardian, _, _temp_dir) = create_test_guardian_db("ac_get_test").await.unwrap();
guardian
.register_default_access_control_types()
.await
.unwrap();
let simple_ac = guardian.get_access_control_type("simple");
assert!(simple_ac.is_some(), "Simple AC not found");
let nonexistent = guardian.get_access_control_type("nonexistent");
assert!(nonexistent.is_none(), "Should not find nonexistent AC");
}
#[tokio::test]
async fn test_unregister_access_control_type() {
let (guardian, _, _temp_dir) = create_test_guardian_db("ac_unregister_test").await.unwrap();
guardian
.register_default_access_control_types()
.await
.unwrap();
assert!(guardian.get_access_control_type("simple").is_some());
guardian.unregister_access_control_type("simple");
assert!(guardian.get_access_control_type("simple").is_none());
}
#[tokio::test]
async fn test_event_exchange_heads_creation() {
let node_id = create_test_node_id(42);
let identity = create_test_identity("event_test");
let entry = create_test_entry(identity, "test_data", 1);
let message = MessageExchangeHeads {
address: "/test/address".to_string(),
heads: vec![entry],
};
let event = EventExchangeHeads::new(node_id, message);
assert_eq!(event.peer, node_id);
assert_eq!(event.message.address, "/test/address");
assert_eq!(event.message.heads.len(), 1);
}
#[tokio::test]
async fn test_event_guardian_db_ready() {
let event = EventGuardianDBReady {
address: "/guardian/test".to_string(),
db_type: "eventlog".to_string(),
};
assert_eq!(event.address, "/guardian/test");
assert_eq!(event.db_type, "eventlog");
}
#[tokio::test]
async fn test_event_sync_completed_creation() {
use crate::guardian::core::EventSyncCompleted;
let event = EventSyncCompleted::new(
"/store/test".to_string(),
"peer123".to_string(),
10,
500,
true,
);
assert_eq!(event.store_address, "/store/test");
assert_eq!(event.node_id, "peer123");
assert_eq!(event.heads_synced, 10);
assert_eq!(event.duration_ms, 500);
assert!(event.success);
}
#[tokio::test]
async fn test_event_sync_error_types() {
use crate::guardian::core::{EventSyncError, SyncErrorType};
let error_types = vec![
SyncErrorType::PermissionDenied,
SyncErrorType::NetworkError,
SyncErrorType::ValidationError,
SyncErrorType::StoreError,
SyncErrorType::UnknownError,
];
for error_type in error_types {
let event = EventSyncError::new(
"/store/test".to_string(),
"peer123".to_string(),
"Test error".to_string(),
5,
error_type.clone(),
);
assert_eq!(event.error_message, "Test error");
}
}
#[tokio::test]
async fn test_identity_basic_validation() {
let identity = create_test_identity("validation_test");
assert!(!identity.id().is_empty());
assert!(!identity.pub_key().is_empty());
assert!(!identity.signatures().id().is_empty());
assert!(!identity.signatures().pub_key().is_empty());
}
#[tokio::test]
async fn test_multiple_identities_uniqueness() {
let id1 = create_test_identity("user1");
let id2 = create_test_identity("user2");
let id3 = create_test_identity("user3");
assert_ne!(id1.id(), id2.id());
assert_ne!(id1.id(), id3.id());
assert_ne!(id2.id(), id3.id());
}
#[tokio::test]
async fn test_message_exchange_heads_structure() {
let identity = create_test_identity("sync_test");
let entry1 = create_test_entry(identity.clone(), "data1", 1);
let entry2 = create_test_entry(identity.clone(), "data2", 2);
let message = MessageExchangeHeads {
address: "/sync/test".to_string(),
heads: vec![entry1, entry2],
};
assert_eq!(message.heads.len(), 2);
assert_eq!(message.address, "/sync/test");
}
#[tokio::test]
async fn test_entry_creation_with_identity() {
let identity = create_test_identity("entry_test");
let entry = create_test_entry(identity.clone(), "test_payload", 42);
assert_eq!(entry.payload, b"test_payload");
assert!(entry.identity.is_some());
let entry_identity = entry.identity.as_ref().unwrap();
assert_eq!(entry_identity.id(), identity.id());
}
#[tokio::test]
async fn test_multiple_heads_with_same_identity() {
let identity = create_test_identity("multi_heads_test");
let entries: Vec<Entry> = (0..5)
.map(|i| create_test_entry(identity.clone(), &format!("payload_{}", i), i))
.collect();
assert_eq!(entries.len(), 5);
for entry in &entries {
let entry_id = entry.identity.as_ref().unwrap();
assert_eq!(entry_id.id(), identity.id());
}
}
#[tokio::test]
async fn test_guardian_db_close() {
let (guardian, _, _temp_dir) = create_test_guardian_db("close_test").await.unwrap();
let result = guardian.close().await;
assert!(result.is_ok(), "Failed to close GuardianDB");
}
#[tokio::test]
async fn test_close_all_stores() {
let (guardian, _, _temp_dir) = create_test_guardian_db("close_stores_test").await.unwrap();
for i in 0..3 {
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let _ = guardian
.create(&format!("store_{}", i), "eventlog", Some(options))
.await;
}
guardian.close_all_stores().await;
for i in 0..3 {
assert!(
guardian.get_store(&format!("store_{}", i)).is_none(),
"Store {} should be closed",
i
);
}
}
#[tokio::test]
async fn test_close_cache() {
let (guardian, _, _temp_dir) = create_test_guardian_db("close_cache_test").await.unwrap();
guardian.close_cache();
}
#[tokio::test]
async fn test_determine_address_valid_name() {
let (guardian, _, _temp_dir) = create_test_guardian_db("determine_address_test")
.await
.unwrap();
guardian
.register_default_access_control_types()
.await
.unwrap();
let result = guardian
.determine_address("test_db", "eventlog", None)
.await;
assert!(result.is_ok(), "Failed to determine address");
let address = result.unwrap();
assert!(address.to_string().contains("/GuardianDB/"));
assert!(address.to_string().contains("test_db"));
}
#[tokio::test]
async fn test_determine_address_invalid_store_type() {
let (guardian, _, _temp_dir) = create_test_guardian_db("invalid_store_type_test")
.await
.unwrap();
let result = guardian
.determine_address("test_db", "nonexistent_type", None)
.await;
assert!(result.is_err(), "Should fail with invalid store type");
}
#[tokio::test]
async fn test_determine_address_with_already_valid_address() {
let (guardian, _, _temp_dir) = create_test_guardian_db("valid_address_test").await.unwrap();
let hash = create_test_hash(42);
let valid_address = format!("/GuardianDB/{}/test", hash);
let result = guardian
.determine_address(&valid_address, "eventlog", None)
.await;
assert!(
result.is_err(),
"Should reject already valid addresses as names"
);
}
#[tokio::test]
async fn test_create_eventlog_store() {
let (guardian, _, _temp_dir) = create_test_guardian_db("create_eventlog_test")
.await
.unwrap();
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result = guardian.create("test_log", "eventlog", Some(options)).await;
assert!(result.is_ok(), "Failed to create eventlog store");
let store = result.unwrap();
assert_eq!(store.store_type(), "eventlog");
}
#[tokio::test]
async fn test_create_keyvalue_store() {
let (guardian, _, _temp_dir) = create_test_guardian_db("create_kv_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("keyvalue".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result = guardian.create("test_kv", "keyvalue", Some(options)).await;
assert!(result.is_ok(), "Failed to create keyvalue store");
let store = result.unwrap();
assert_eq!(store.store_type(), "keyvalue");
}
#[tokio::test]
async fn test_create_document_store() {
let (guardian, _, _temp_dir) = create_test_guardian_db("create_doc_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("document".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result = guardian.create("test_doc", "document", Some(options)).await;
assert!(result.is_ok(), "Failed to create document store");
let store = result.unwrap();
assert_eq!(store.store_type(), "document");
}
#[tokio::test]
async fn test_create_without_overwrite_duplicate() {
let (guardian, _, _temp_dir) = create_test_guardian_db("no_overwrite_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result1 = guardian
.create("duplicate_test", "eventlog", Some(options.clone()))
.await;
assert!(result1.is_ok());
let mut options2 = options;
options2.overwrite = Some(false);
let result2 = guardian
.create("duplicate_test", "eventlog", Some(options2))
.await;
assert!(
result2.is_ok() || result2.is_err(),
"Should have consistent behavior"
);
}
#[tokio::test]
async fn test_concurrent_store_access() {
let (guardian, _, _temp_dir) = create_test_guardian_db("concurrent_access_test")
.await
.unwrap();
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let store = guardian
.create("concurrent_store", "eventlog", Some(options))
.await
.unwrap();
let address = store.address().to_string();
let guardian_arc = Arc::new(guardian);
let mut handles = vec![];
for i in 0..5 {
let guardian_clone = Arc::clone(&guardian_arc);
let addr = address.clone();
let handle = tokio::spawn(async move {
let retrieved = guardian_clone.get_store(&addr);
assert!(
retrieved.is_some(),
"Store should be accessible in thread {}",
i
);
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
}
#[tokio::test]
async fn test_empty_store_name() {
let (guardian, _, _temp_dir) = create_test_guardian_db("empty_name_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result = guardian.create("", "eventlog", Some(options)).await;
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn test_very_long_store_name() {
let (guardian, _, _temp_dir) = create_test_guardian_db("long_name_test").await.unwrap();
let long_name = "a".repeat(1000);
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result = guardian.create(&long_name, "eventlog", Some(options)).await;
assert!(result.is_ok() || result.is_err());
}
#[tokio::test]
async fn test_special_characters_in_name() {
let (guardian, _, _temp_dir) = create_test_guardian_db("special_chars_test").await.unwrap();
let special_names = vec![
"test/store",
"test\\store",
"test:store",
"test?store",
"test#store",
];
for name in special_names {
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result = guardian.create(name, "eventlog", Some(options)).await;
assert!(
result.is_ok() || result.is_err(),
"Should handle special chars consistently"
);
}
}
#[tokio::test]
async fn test_node_id_generation() {
let id1 = create_test_node_id(1);
let id2 = create_test_node_id(2);
let id3 = create_test_node_id(1);
assert_ne!(id1, id2, "Different seeds should produce different IDs");
assert_eq!(id1, id3, "Same seed should produce same ID");
}
#[tokio::test]
async fn test_hash_generation() {
let hash1 = create_test_hash(1);
let hash2 = create_test_hash(2);
let hash3 = create_test_hash(1);
assert_ne!(hash1, hash2);
assert_eq!(hash1, hash3);
}
#[tokio::test]
async fn test_full_workflow_create_and_close() {
let (guardian, _, _temp_dir) = create_test_guardian_db("full_workflow_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let store = guardian
.create("workflow_store", "eventlog", Some(options))
.await
.unwrap();
let address = store.address().to_string();
assert!(guardian.get_store(&address).is_some());
let close_result = guardian.close().await;
assert!(close_result.is_ok());
}
#[tokio::test]
async fn test_multiple_stores_lifecycle() {
let (guardian, _, _temp_dir) = create_test_guardian_db("multi_stores_test").await.unwrap();
let store_types = ["eventlog", "keyvalue", "document"];
let mut addresses = vec![];
for (i, store_type) in store_types.iter().enumerate() {
let options = CreateDBOptions {
store_type: Some(store_type.to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let store = guardian
.create(&format!("store_{}", i), store_type, Some(options))
.await
.unwrap();
addresses.push(store.address().to_string());
assert_eq!(store.store_type(), *store_type);
}
for address in &addresses {
assert!(
guardian.get_store(address).is_some(),
"Store {} should exist",
address
);
}
guardian.close().await.unwrap();
}
#[tokio::test]
async fn test_event_emission_during_operations() {
let (guardian, _, _temp_dir) = create_test_guardian_db("event_ops_test").await.unwrap();
let options = CreateDBOptions {
store_type: Some("eventlog".to_string()),
create: Some(true),
overwrite: Some(true),
event_bus: Some(guardian.event_bus().deref().clone()),
..Default::default()
};
let result = guardian
.create("event_store", "eventlog", Some(options))
.await;
assert!(result.is_ok(), "Store creation should succeed");
}
#[tokio::test]
async fn test_tracer_initialization() {
let (guardian, _, _temp_dir) = create_test_guardian_db("tracer_test").await.unwrap();
let tracer = guardian.tracer();
assert!(Arc::strong_count(&tracer) >= 1);
}
#[tokio::test]
async fn test_span_access() {
let (guardian, _, _temp_dir) = create_test_guardian_db("span_test").await.unwrap();
let span = guardian.span();
assert!(span.id().is_some() || span.id().is_none()); }
#[tokio::test]
async fn test_keystore_initialization() {
let (guardian, _, _temp_dir) = create_test_guardian_db("keystore_test").await.unwrap();
let keystore = guardian.keystore();
assert!(Arc::strong_count(&keystore) >= 1);
}
#[tokio::test]
async fn test_close_keystore() {
let (guardian, _, _temp_dir) = create_test_guardian_db("close_keystore_test")
.await
.unwrap();
guardian.close_key_store();
let keystore = guardian.keystore();
assert!(Arc::strong_count(&keystore) >= 1);
}
}