use std::sync::Arc;
use calimero_blobstore::config::BlobStoreConfig;
use calimero_blobstore::{BlobManager as BlobStore, FileSystem};
use calimero_context_client::client::ContextClient;
use calimero_dag::{CausalDelta, DeltaKind};
use calimero_network_primitives::client::NetworkClient;
use calimero_node_primitives::client::{BlobManager, NodeClient, SyncClient};
use calimero_primitives::context::ContextId;
use calimero_primitives::identity::PublicKey;
use calimero_storage::action::Action;
use calimero_storage::logical_clock::HybridTimestamp;
use calimero_store::db::InMemoryDB;
use calimero_store::Store;
use calimero_utils_actix::LazyRecipient;
use tokio::sync::{broadcast, mpsc};
use crate::delta_store::DeltaStore;
fn make_delta(
id: [u8; 32],
parents: Vec<[u8; 32]>,
expected_root_hash: [u8; 32],
) -> CausalDelta<Vec<Action>> {
CausalDelta {
id,
parents,
payload: Vec::new(),
hlc: HybridTimestamp::default(),
expected_root_hash,
kind: DeltaKind::Regular,
}
}
#[tokio::test]
async fn add_local_applied_delta_prunes_non_head_ancestors() {
let tmp = tempfile::tempdir().expect("tempdir");
let store = Store::new(Arc::new(InMemoryDB::owned()));
let blob_config =
BlobStoreConfig::new(tmp.path().to_path_buf().try_into().expect("utf8 blob path"));
let file_system = FileSystem::new(&blob_config).await.expect("blob fs");
let blob_store = BlobStore::new(store.clone(), file_system);
let blob_manager = BlobManager::new(blob_store);
let node_recipient = LazyRecipient::new();
let context_recipient = LazyRecipient::new();
let network_recipient = LazyRecipient::new();
let network_client = NetworkClient::new(network_recipient);
let (event_sender, _) = broadcast::channel(16);
let (ctx_sync_tx, _ctx_sync_rx) = mpsc::channel(1);
let (ns_sync_tx, _ns_sync_rx) = mpsc::channel(1);
let (ns_join_tx, _ns_join_rx) = mpsc::channel(1);
let sync_client = SyncClient::new(ctx_sync_tx, ns_sync_tx, ns_join_tx);
let node_client = NodeClient::new(
store.clone(),
blob_manager,
network_client,
node_recipient,
event_sender,
sync_client,
String::new(),
None,
);
let context_client = ContextClient::new(store, node_client, context_recipient);
let context_id = ContextId::from([0xAA; 32]);
let our_identity = PublicKey::from([0xBB; 32]);
let root = [0u8; 32];
let delta_store = DeltaStore::new(root, context_client, context_id, our_identity);
let parent_id = [0x11u8; 32];
let parent_hash = [0xA1; 32];
let cascaded = delta_store
.add_local_applied_delta(make_delta(parent_id, vec![[0u8; 32]], parent_hash))
.await
.expect("add parent succeeds");
assert!(
cascaded.is_empty(),
"no pending children → no cascaded events"
);
let ids = delta_store.head_root_hash_ids().await;
assert_eq!(
ids,
vec![parent_id],
"parent is the sole head after first add"
);
let child_id = [0x22u8; 32];
let child_hash = [0xC1; 32];
let cascaded = delta_store
.add_local_applied_delta(make_delta(child_id, vec![parent_id], child_hash))
.await
.expect("add child");
assert!(cascaded.is_empty(), "still no pending children to cascade");
let ids = delta_store.head_root_hash_ids().await;
assert_eq!(
ids,
vec![child_id],
"parent entry must be pruned once it is no longer a head"
);
}