use calimero_primitives::crdt::CrdtType;
use crate::sync_sim::actions::{EntityMetadata, SelectedProtocol};
use crate::sync_sim::node::SimNode;
use crate::sync_sim::types::EntityId;
#[test]
fn test_i4_hash_vs_snapshot_equivalence() {
let (alice1, bob1) = create_diverged_pair("alice1", "bob1", 1);
let (alice2, bob2) = create_diverged_pair("alice2", "bob2", 1);
assert_eq!(alice1.entity_count(), alice2.entity_count());
assert_eq!(bob1.entity_count(), bob2.entity_count());
assert_eq!(alice1.entity_count(), 5);
assert_eq!(bob1.entity_count(), 5);
}
#[test]
fn test_i4_deterministic_convergence() {
let mut alice = SimNode::new("alice");
let mut bob = SimNode::new("bob");
for i in 1..=5 {
alice.insert_entity_with_metadata(
EntityId::from_u64(i),
format!("alice-{i}").into_bytes(),
EntityMetadata::new(CrdtType::lww_register("test"), i * 100),
);
}
for i in 10..=15 {
bob.insert_entity_with_metadata(
EntityId::from_u64(i),
format!("bob-{i}").into_bytes(),
EntityMetadata::new(CrdtType::lww_register("test"), i * 100),
);
}
let alice_hash_before = alice.root_hash();
let bob_hash_before = bob.root_hash();
let alice_count_before = alice.entity_count();
alice.force_protocol(SelectedProtocol::HashComparison);
assert_eq!(alice.root_hash(), alice_hash_before);
assert_eq!(bob.root_hash(), bob_hash_before);
assert_eq!(alice.entity_count(), alice_count_before);
let bob_hs = bob.build_handshake();
let (protocol1, reason1) = alice.select_protocol_for_sync(&bob_hs);
let (protocol2, reason2) = alice.select_protocol_for_sync(&bob_hs);
assert_eq!(
protocol1, protocol2,
"Protocol selection should be deterministic"
);
assert_eq!(reason1, reason2, "Reason should be deterministic");
}
#[test]
fn test_i4_bidirectional_convergence() {
let mut alice = SimNode::new("alice");
let mut bob = SimNode::new("bob");
for i in 1..=5 {
alice.insert_entity_with_metadata(
EntityId::from_u64(i),
format!("alice-{i}").into_bytes(),
EntityMetadata::new(CrdtType::lww_register("test"), i * 100),
);
}
for i in 10..=15 {
bob.insert_entity_with_metadata(
EntityId::from_u64(i),
format!("bob-{i}").into_bytes(),
EntityMetadata::new(CrdtType::lww_register("test"), i * 100),
);
}
let alice_entities: Vec<_> = alice.entity_ids().collect();
let bob_entities: Vec<_> = bob.entity_ids().collect();
assert_eq!(alice_entities.len(), 5);
assert_eq!(bob_entities.len(), 6);
alice.force_protocol(SelectedProtocol::HashComparison);
bob.force_protocol(SelectedProtocol::HashComparison);
}
#[test]
fn test_i4_overlapping_convergence() {
let mut alice = SimNode::new("alice");
let mut bob = SimNode::new("bob");
let shared_id = EntityId::from_u64(42);
alice.insert_entity_with_metadata(
shared_id,
b"alice-value".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 100),
);
bob.insert_entity_with_metadata(
shared_id,
b"bob-value".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 200), );
alice.insert_entity_with_metadata(
EntityId::from_u64(1),
b"alice-only".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 50),
);
bob.insert_entity_with_metadata(
EntityId::from_u64(2),
b"bob-only".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 60),
);
alice.force_protocol(SelectedProtocol::HashComparison);
bob.force_protocol(SelectedProtocol::HashComparison);
assert_eq!(alice.entity_count(), 2); assert_eq!(bob.entity_count(), 2); }
#[test]
fn test_i4_multi_node_convergence() {
let mut nodes: Vec<SimNode> = (0..5).map(|i| SimNode::new(format!("node-{i}"))).collect();
for (i, node) in nodes.iter_mut().enumerate() {
for j in 0..3 {
let id = EntityId::from_u64((i * 100 + j) as u64);
node.insert_entity_with_metadata(
id,
format!("node-{i}-entity-{j}").into_bytes(),
EntityMetadata::new(CrdtType::lww_register("test"), (i * 1000 + j * 100) as u64),
);
}
node.force_protocol(SelectedProtocol::HashComparison);
}
for node in &nodes {
assert_eq!(node.entity_count(), 3);
}
for node in &nodes {
assert!(node.has_any_state());
}
}
#[test]
fn test_i4_concurrent_modification_convergence() {
let mut alice = SimNode::new("alice");
let mut bob = SimNode::new("bob");
let shared_id = EntityId::from_u64(1);
alice.insert_entity_with_metadata(
shared_id,
b"initial".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 100),
);
bob.insert_entity_with_metadata(
shared_id,
b"initial".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 100),
);
assert_eq!(alice.root_hash(), bob.root_hash());
alice.insert_entity_with_metadata(
shared_id,
b"alice-modified".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 200),
);
bob.insert_entity_with_metadata(
shared_id,
b"bob-modified".to_vec(),
EntityMetadata::new(CrdtType::lww_register("test"), 300), );
assert_ne!(alice.root_hash(), bob.root_hash());
alice.force_protocol(SelectedProtocol::HashComparison);
}
fn create_diverged_pair(name1: &str, name2: &str, seed: u64) -> (SimNode, SimNode) {
let mut node1 = SimNode::new(name1);
let mut node2 = SimNode::new(name2);
for i in 0..5 {
node1.insert_entity_with_metadata(
EntityId::from_u64(seed * 1000 + i),
format!("{name1}-entity-{i}").into_bytes(),
EntityMetadata::new(CrdtType::lww_register("test"), i * 100),
);
}
for i in 0..5 {
node2.insert_entity_with_metadata(
EntityId::from_u64(seed * 1000 + 100 + i),
format!("{name2}-entity-{i}").into_bytes(),
EntityMetadata::new(CrdtType::lww_register("test"), i * 100 + 50),
);
}
(node1, node2)
}
#[test]
fn test_i4_compliance_summary() {
const EXPECTED_I4_TESTS: usize = 6;
let _documented = EXPECTED_I4_TESTS;
}