#[macro_use]
#[path = "sync_sim/mod.rs"]
pub mod sync_sim;
#[path = "sync_scenarios/mod.rs"]
pub mod sync_scenarios;
#[path = "sync_compliance/mod.rs"]
pub mod sync_compliance;
pub use sync_sim::prelude::*;
#[cfg(test)]
mod tests {
use super::*;
use calimero_primitives::crdt::CrdtType;
#[test]
fn test_empty_runtime() {
let mut rt = SimRuntime::new(42);
assert!(rt.check_convergence().is_converged());
}
#[test]
fn test_single_node() {
let mut rt = SimRuntime::new(42);
let a = rt.add_node("alice");
rt.node_mut(&a).unwrap().insert_entity(
EntityId::from_u64(1),
vec![1, 2, 3],
CrdtType::lww_register("test"),
);
assert!(rt.check_convergence().is_converged());
}
#[test]
fn test_two_nodes_same_state() {
let mut rt = SimRuntime::new(42);
let (a, b) = Scenario::force_none();
let _a_id = rt.add_existing_node(a);
let _b_id = rt.add_existing_node(b);
assert!(rt.check_convergence().is_converged());
}
#[test]
fn test_two_nodes_different_state() {
let mut rt = SimRuntime::new(42);
let (a, b) = Scenario::force_hash_high_divergence();
rt.add_existing_node(a);
rt.add_existing_node(b);
assert!(rt.check_convergence().is_diverged());
}
#[test]
fn test_scenario_force_none() {
let (a, b) = Scenario::force_none();
assert_eq!(a.root_hash(), b.root_hash());
}
#[test]
fn test_scenario_force_snapshot() {
let (fresh, source) = Scenario::force_snapshot();
assert!(!fresh.has_any_state());
assert!(source.has_any_state());
}
#[test]
fn test_scenario_partial_overlap() {
let (a, b) = Scenario::partial_overlap();
assert_eq!(a.entity_count(), 75);
assert_eq!(b.entity_count(), 75);
}
#[test]
fn test_scenario_both_initialized() {
let (a, b) = Scenario::both_initialized();
assert!(a.has_any_state());
assert!(b.has_any_state());
}
#[test]
fn test_convergence_pending_messages() {
let mut rt = SimRuntime::new(42);
let a = rt.add_node("alice");
let b = rt.add_node("bob");
assert!(
rt.check_convergence().is_converged(),
"Empty system should be converged"
);
rt.inject_message(
a,
b,
SyncMessage::SyncComplete { success: true },
SimDuration::from_millis(100),
)
.expect("sender node should exist");
assert!(
!rt.check_convergence().is_converged(),
"System with in-flight message should not be converged"
);
assert_eq!(
rt.network().in_flight_count(),
1,
"Should have 1 message in flight"
);
rt.step();
assert!(
rt.check_convergence().is_converged(),
"System should be converged after message delivered"
);
}
#[test]
fn test_partition_blocks_messages() {
let mut rt =
SimRuntime::with_config(SimConfig::with_seed(42).with_faults(FaultConfig::none()));
let a = rt.add_node("alice");
let b = rt.add_node("bob");
rt.schedule_partition(vec![vec![a.clone()], vec![b.clone()]], SimDuration::ZERO);
rt.step();
rt.inject_message(
a,
b,
SyncMessage::SyncComplete { success: true },
SimDuration::from_millis(10),
)
.expect("sender node should exist");
rt.step();
assert_eq!(rt.network().metrics.messages_dropped_partition, 1);
}
#[test]
fn test_fault_injection_loss() {
let mut rt = SimRuntime::with_config(
SimConfig::with_seed(42).with_faults(FaultConfig::none().with_loss(1.0)),
);
let a = rt.add_node("alice");
let b = rt.add_node("bob");
rt.send_message(a, b, SyncMessage::SyncComplete { success: true })
.expect("sender node should exist");
assert_eq!(
rt.network().metrics.messages_dropped_loss,
1,
"Message should have been dropped due to 100% loss rate"
);
}
#[test]
fn test_crash_preserves_storage() {
let mut rt = SimRuntime::new(42);
let a = rt.add_node("alice");
rt.node_mut(&a).unwrap().insert_entity(
EntityId::from_u64(1),
vec![1],
CrdtType::lww_register("test"),
);
rt.schedule_crash(a.clone(), SimDuration::ZERO);
rt.step();
assert_eq!(rt.node(&a).unwrap().entity_count(), 1);
}
#[test]
fn test_restart_increments_session() {
let mut rt = SimRuntime::new(42);
let a = rt.add_node("alice");
assert_eq!(rt.node(&a).unwrap().session, 0);
rt.schedule_crash(a.clone(), SimDuration::ZERO);
rt.schedule_restart(a.clone(), SimDuration::from_millis(10));
rt.step(); rt.step();
assert_eq!(rt.node(&a).unwrap().session, 1);
}
#[test]
fn test_random_scenario_deterministic() {
let nodes1 = RandomScenario::two_nodes_random(42);
let nodes2 = RandomScenario::two_nodes_random(42);
assert_eq!(nodes1.len(), nodes2.len());
for (n1, n2) in nodes1.iter().zip(nodes2.iter()) {
assert_eq!(n1.entity_count(), n2.entity_count());
}
}
#[test]
fn test_random_scenario_mesh() {
let nodes = RandomScenario::mesh_random(42, 5);
assert_eq!(nodes.len(), 5);
}
#[test]
fn test_metrics_crash_counted() {
let mut rt = SimRuntime::new(42);
let a = rt.add_node("alice");
rt.schedule_crash(a, SimDuration::ZERO);
rt.step();
assert_eq!(rt.metrics().effects.node_crashes, 1);
}
#[test]
fn test_metrics_partition_counted() {
let mut rt = SimRuntime::new(42);
let a = rt.add_node("alice");
let b = rt.add_node("bob");
rt.schedule_partition(vec![vec![a], vec![b]], SimDuration::ZERO);
rt.step();
assert_eq!(rt.metrics().effects.partitions, 1);
}
#[test]
fn test_assert_macros() {
let mut a = SimNode::new("a");
let mut b = SimNode::new("b");
assert_converged!(a, b);
a.insert_entity(
EntityId::from_u64(1),
vec![1],
CrdtType::lww_register("test"),
);
b.insert_entity(
EntityId::from_u64(2),
vec![2],
CrdtType::lww_register("test"),
);
assert_not_converged!(a, b);
assert_entity_count!(a, 1);
assert_has_entity!(a, EntityId::from_u64(1));
assert_no_entity!(a, EntityId::from_u64(2));
assert_idle!(a);
assert_buffer_empty!(a);
}
}