leptos_sync_core/crdt/
tree.rs

1use super::{CRDT, Mergeable, ReplicaId};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::error::Error;
5use uuid::Uuid;
6
7/// Custom error type for tree operations
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct TreeError {
10    message: String,
11}
12
13impl TreeError {
14    pub fn new(message: String) -> Self {
15        Self { message }
16    }
17}
18
19impl std::fmt::Display for TreeError {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "TreeError: {}", self.message)
22    }
23}
24
25impl Error for TreeError {}
26
27/// Unique identifier for a tree node
28#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
29pub struct NodeId {
30    /// Unique identifier for the node
31    pub id: Uuid,
32    /// Replica that created the node
33    pub replica: ReplicaId,
34}
35
36impl NodeId {
37    /// Create a new node ID
38    pub fn new(replica: ReplicaId) -> Self {
39        Self {
40            id: Uuid::new_v4(),
41            replica,
42        }
43    }
44
45    /// Create a node ID from existing UUID and replica
46    pub fn from_parts(id: Uuid, replica: ReplicaId) -> Self {
47        Self { id, replica }
48    }
49}
50
51/// Metadata for a tree node
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct NodeMetadata {
54    /// When the node was created
55    pub created_at: u64,
56    /// When the node was last modified
57    pub modified_at: u64,
58    /// Whether the node is marked as deleted
59    pub deleted: bool,
60    /// Replica that last modified the node
61    pub last_modified_by: ReplicaId,
62}
63
64impl NodeMetadata {
65    /// Create new metadata
66    pub fn new(replica: ReplicaId, timestamp: u64) -> Self {
67        Self {
68            created_at: timestamp,
69            modified_at: timestamp,
70            deleted: false,
71            last_modified_by: replica,
72        }
73    }
74
75    /// Mark as modified
76    pub fn mark_modified(&mut self, replica: ReplicaId, timestamp: u64) {
77        self.modified_at = timestamp;
78        self.last_modified_by = replica;
79    }
80
81    /// Mark as deleted
82    pub fn mark_deleted(&mut self, replica: ReplicaId, timestamp: u64) {
83        self.deleted = true;
84        self.mark_modified(replica, timestamp);
85    }
86}
87
88/// A tree node with its metadata and relationships
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90pub struct TreeNode<T> {
91    /// Unique identifier
92    pub id: NodeId,
93    /// The actual value
94    pub value: T,
95    /// Metadata
96    pub metadata: NodeMetadata,
97    /// Parent node ID (None for root)
98    pub parent: Option<NodeId>,
99    /// Child node IDs
100    pub children: Vec<NodeId>,
101}
102
103impl<T> TreeNode<T> {
104    /// Create a new tree node
105    pub fn new(value: T, replica: ReplicaId, timestamp: u64) -> Self {
106        Self {
107            id: NodeId::new(replica),
108            value,
109            metadata: NodeMetadata::new(replica, timestamp),
110            parent: None,
111            children: Vec::new(),
112        }
113    }
114
115    /// Create a child node
116    pub fn new_child(value: T, replica: ReplicaId, timestamp: u64, parent: NodeId) -> Self {
117        Self {
118            id: NodeId::new(replica),
119            value,
120            metadata: NodeMetadata::new(replica, timestamp),
121            parent: Some(parent),
122            children: Vec::new(),
123        }
124    }
125
126    /// Mark as modified
127    pub fn mark_modified(&mut self, replica: ReplicaId, timestamp: u64) {
128        self.metadata.mark_modified(replica, timestamp);
129    }
130
131    /// Mark as deleted
132    pub fn mark_deleted(&mut self, replica: ReplicaId, timestamp: u64) {
133        self.metadata.mark_deleted(replica, timestamp);
134    }
135
136    /// Add a child
137    pub fn add_child(&mut self, child_id: NodeId) {
138        self.children.push(child_id);
139    }
140
141    /// Remove a child
142    pub fn remove_child(&mut self, child_id: &NodeId) -> bool {
143        if let Some(pos) = self.children.iter().position(|id| id == child_id) {
144            self.children.remove(pos);
145            true
146        } else {
147            false
148        }
149    }
150}
151
152/// Strategy for handling tree conflicts
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub enum TreeStrategy {
155    /// Add-Wins: Nodes are never removed, only marked as deleted
156    AddWins,
157    /// Remove-Wins: Deleted nodes are completely removed
158    RemoveWins,
159}
160
161/// Configuration for tree CRDTs
162#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
163pub struct TreeConfig {
164    /// Conflict resolution strategy
165    pub strategy: TreeStrategy,
166    /// Whether to preserve deleted nodes in metadata
167    pub preserve_deleted: bool,
168    /// Maximum depth of the tree
169    pub max_depth: Option<usize>,
170    /// Maximum number of children per node
171    pub max_children: Option<usize>,
172}
173
174impl Default for TreeConfig {
175    fn default() -> Self {
176        Self {
177            strategy: TreeStrategy::AddWins,
178            preserve_deleted: true,
179            max_depth: None,
180            max_children: None,
181        }
182    }
183}
184
185/// Add-Wins Tree CRDT implementation
186/// 
187/// This implementation ensures that nodes are never completely lost.
188/// Deleted nodes are marked as deleted but preserved for potential recovery.
189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
190pub struct AddWinsTree<T> {
191    /// Configuration
192    config: TreeConfig,
193    /// Nodes in the tree
194    nodes: HashMap<NodeId, TreeNode<T>>,
195    /// Replica ID for this instance
196    replica: ReplicaId,
197}
198
199impl<T: Clone + PartialEq + Eq + Send + Sync> AddWinsTree<T> {
200    /// Create a new Add-Wins tree
201    pub fn new(replica: ReplicaId) -> Self {
202        Self {
203            config: TreeConfig::default(),
204            nodes: HashMap::new(),
205            replica,
206        }
207    }
208
209    /// Create with custom configuration
210    pub fn with_config(replica: ReplicaId, config: TreeConfig) -> Self {
211        Self {
212            config,
213            nodes: HashMap::new(),
214            replica,
215        }
216    }
217
218    /// Add a root node to the tree
219    pub fn add_root(&mut self, value: T, timestamp: u64) -> NodeId {
220        let node = TreeNode::new(value, self.replica, timestamp);
221        let id = node.id.clone();
222        self.nodes.insert(id.clone(), node);
223        id
224    }
225
226    /// Add a child node
227    pub fn add_child(&mut self, parent_id: &NodeId, value: T, timestamp: u64) -> Result<NodeId, TreeError> {
228        if !self.nodes.contains_key(parent_id) {
229            return Err(TreeError::new("Parent node not found".to_string()));
230        }
231
232        let node = TreeNode::new_child(value, self.replica, timestamp, parent_id.clone());
233        let id = node.id.clone();
234        
235        // Add the child node
236        self.nodes.insert(id.clone(), node);
237        
238        // Update parent's children list
239        if let Some(parent) = self.nodes.get_mut(parent_id) {
240            parent.add_child(id.clone());
241        }
242        
243        Ok(id)
244    }
245
246    /// Update an existing node
247    pub fn update(&mut self, id: &NodeId, value: T, timestamp: u64) -> Result<(), TreeError> {
248        if let Some(node) = self.nodes.get_mut(id) {
249            node.value = value;
250            node.mark_modified(self.replica, timestamp);
251            Ok(())
252        } else {
253            Err(TreeError::new("Node not found".to_string()))
254        }
255    }
256
257    /// Mark a node as deleted
258    pub fn remove(&mut self, id: &NodeId, timestamp: u64) -> Result<(), TreeError> {
259        if let Some(node) = self.nodes.get_mut(id) {
260            node.mark_deleted(self.replica, timestamp);
261            Ok(())
262        } else {
263            Err(TreeError::new("Node not found".to_string()))
264        }
265    }
266
267    /// Move a node to a new parent
268    pub fn move_node(&mut self, id: &NodeId, new_parent_id: &NodeId) -> Result<(), TreeError> {
269        if !self.nodes.contains_key(id) || !self.nodes.contains_key(new_parent_id) {
270            return Err(TreeError::new("Node not found".to_string()));
271        }
272
273        // Get the old parent ID first
274        let old_parent_id = self.nodes.get(id).and_then(|node| node.parent.clone());
275        
276        // Remove from old parent
277        if let Some(old_parent_id) = old_parent_id {
278            if let Some(old_parent) = self.nodes.get_mut(&old_parent_id) {
279                old_parent.remove_child(id);
280            }
281        }
282        
283        // Update the node's parent reference
284        if let Some(node) = self.nodes.get_mut(id) {
285            node.parent = Some(new_parent_id.clone());
286        }
287        
288        // Add to new parent
289        if let Some(new_parent) = self.nodes.get_mut(new_parent_id) {
290            new_parent.add_child(id.clone());
291        }
292        
293        Ok(())
294    }
295
296    /// Get a node by ID
297    pub fn get(&self, id: &NodeId) -> Option<&TreeNode<T>> {
298        self.nodes.get(id)
299    }
300
301    /// Get all visible nodes (not deleted)
302    pub fn visible_nodes(&self) -> Vec<&TreeNode<T>> {
303        self.nodes
304            .values()
305            .filter(|n| !n.metadata.deleted)
306            .collect()
307    }
308
309    /// Get all nodes including deleted ones
310    pub fn all_nodes(&self) -> Vec<&TreeNode<T>> {
311        self.nodes.values().collect()
312    }
313
314    /// Get root nodes
315    pub fn roots(&self) -> Vec<&TreeNode<T>> {
316        self.nodes
317            .values()
318            .filter(|n| n.parent.is_none() && !n.metadata.deleted)
319            .collect()
320    }
321
322    /// Get children of a node
323    pub fn children(&self, id: &NodeId) -> Vec<&TreeNode<T>> {
324        if let Some(node) = self.nodes.get(id) {
325            node.children
326                .iter()
327                .filter_map(|child_id| self.nodes.get(child_id))
328                .filter(|n| !n.metadata.deleted)
329                .collect()
330        } else {
331            Vec::new()
332        }
333    }
334
335    /// Get descendants of a node (recursive)
336    pub fn descendants(&self, id: &NodeId) -> Vec<&TreeNode<T>> {
337        let mut descendants = Vec::new();
338        let mut to_visit = vec![id.clone()];
339        
340        while let Some(current_id) = to_visit.pop() {
341            if let Some(node) = self.nodes.get(&current_id) {
342                if !node.metadata.deleted {
343                    descendants.push(node);
344                    to_visit.extend(node.children.iter().cloned());
345                }
346            }
347        }
348        
349        descendants
350    }
351
352    /// Check if the tree contains a node
353    pub fn contains(&self, id: &NodeId) -> bool {
354        self.nodes.contains_key(id)
355    }
356
357    /// Get the number of visible nodes
358    pub fn len(&self) -> usize {
359        self.visible_nodes().len()
360    }
361
362    /// Check if the tree is empty
363    pub fn is_empty(&self) -> bool {
364        self.len() == 0
365    }
366
367    /// Clear all nodes
368    pub fn clear(&mut self) {
369        self.nodes.clear();
370    }
371}
372
373impl<T: Clone + PartialEq + Eq + Send + Sync> CRDT for AddWinsTree<T> {
374    fn replica_id(&self) -> &ReplicaId {
375        &self.replica
376    }
377}
378
379impl<T: Clone + PartialEq + Eq + Send + Sync> Mergeable for AddWinsTree<T> {
380    type Error = TreeError;
381
382    fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
383        for (id, node) in &other.nodes {
384            match self.nodes.get(id) {
385                Some(existing) => {
386                    // Conflict resolution: later timestamp wins
387                    if node.metadata.modified_at > existing.metadata.modified_at {
388                        self.nodes.insert(id.clone(), node.clone());
389                    }
390                }
391                None => {
392                    // New node, add it
393                    self.nodes.insert(id.clone(), node.clone());
394                }
395            }
396        }
397        Ok(())
398    }
399
400    fn has_conflict(&self, other: &Self) -> bool {
401        for (id, node) in &other.nodes {
402            if let Some(existing) = self.nodes.get(id) {
403                // Check for conflicts: same timestamp but different replica
404                if node.metadata.modified_at == existing.metadata.modified_at
405                    && node.metadata.last_modified_by != existing.metadata.last_modified_by
406                {
407                    return true;
408                }
409            }
410        }
411        false
412    }
413}
414
415/// Remove-Wins Tree CRDT implementation
416/// 
417/// This implementation completely removes deleted nodes.
418/// It's more memory-efficient but nodes cannot be recovered.
419#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
420pub struct RemoveWinsTree<T> {
421    /// Configuration
422    config: TreeConfig,
423    /// Nodes in the tree
424    nodes: HashMap<NodeId, TreeNode<T>>,
425    /// Replica ID for this instance
426    replica: ReplicaId,
427}
428
429impl<T: Clone + PartialEq + Eq + Send + Sync> RemoveWinsTree<T> {
430    /// Create a new Remove-Wins tree
431    pub fn new(replica: ReplicaId) -> Self {
432        Self {
433            config: TreeConfig {
434                strategy: TreeStrategy::RemoveWins,
435                preserve_deleted: false,
436                max_depth: None,
437                max_children: None,
438            },
439            nodes: HashMap::new(),
440            replica,
441        }
442    }
443
444    /// Create with custom configuration
445    pub fn with_config(replica: ReplicaId, config: TreeConfig) -> Self {
446        Self {
447            config,
448            nodes: HashMap::new(),
449            replica,
450        }
451    }
452
453    /// Add a root node to the tree
454    pub fn add_root(&mut self, value: T, timestamp: u64) -> NodeId {
455        let node = TreeNode::new(value, self.replica, timestamp);
456        let id = node.id.clone();
457        self.nodes.insert(id.clone(), node);
458        id
459    }
460
461    /// Add a child node
462    pub fn add_child(&mut self, parent_id: &NodeId, value: T, timestamp: u64) -> Result<NodeId, TreeError> {
463        if !self.nodes.contains_key(parent_id) {
464            return Err(TreeError::new("Parent node not found".to_string()));
465        }
466
467        let node = TreeNode::new_child(value, self.replica, timestamp, parent_id.clone());
468        let id = node.id.clone();
469        
470        // Add the child node
471        self.nodes.insert(id.clone(), node);
472        
473        // Update parent's children list
474        if let Some(parent) = self.nodes.get_mut(parent_id) {
475            parent.add_child(id.clone());
476        }
477        
478        Ok(id)
479    }
480
481    /// Update an existing node
482    pub fn update(&mut self, id: &NodeId, value: T, timestamp: u64) -> Result<(), TreeError> {
483        if let Some(node) = self.nodes.get_mut(id) {
484            node.value = value;
485            node.mark_modified(self.replica, timestamp);
486            Ok(())
487        } else {
488            Err(TreeError::new("Node not found".to_string()))
489        }
490    }
491
492    /// Remove a node completely
493    pub fn remove(&mut self, id: &NodeId) -> Result<(), TreeError> {
494        // Get the parent ID first
495        let parent_id = self.nodes.get(id).and_then(|node| node.parent.clone());
496        
497        // Remove from parent's children list
498        if let Some(parent_id) = parent_id {
499            if let Some(parent) = self.nodes.get_mut(&parent_id) {
500                parent.remove_child(id);
501            }
502        }
503        
504        // Remove the node
505        if self.nodes.remove(id).is_some() {
506            Ok(())
507        } else {
508            Err(TreeError::new("Node not found".to_string()))
509        }
510    }
511
512    /// Move a node to a new parent
513    pub fn move_node(&mut self, id: &NodeId, new_parent_id: &NodeId) -> Result<(), TreeError> {
514        if !self.nodes.contains_key(id) || !self.nodes.contains_key(new_parent_id) {
515            return Err(TreeError::new("Node not found".to_string()));
516        }
517
518        // Get the old parent ID first
519        let old_parent_id = self.nodes.get(id).and_then(|node| node.parent.clone());
520        
521        // Remove from old parent
522        if let Some(old_parent_id) = old_parent_id {
523            if let Some(old_parent) = self.nodes.get_mut(&old_parent_id) {
524                old_parent.remove_child(id);
525            }
526        }
527        
528        // Update the node's parent reference
529        if let Some(node) = self.nodes.get_mut(id) {
530            node.parent = Some(new_parent_id.clone());
531        }
532        
533        // Add to new parent
534        if let Some(new_parent) = self.nodes.get_mut(new_parent_id) {
535            new_parent.add_child(id.clone());
536        }
537        
538        Ok(())
539    }
540
541    /// Get a node by ID
542    pub fn get(&self, id: &NodeId) -> Option<&TreeNode<T>> {
543        self.nodes.get(id)
544    }
545
546    /// Get all nodes
547    pub fn nodes(&self) -> Vec<&TreeNode<T>> {
548        self.nodes.values().collect()
549    }
550
551    /// Get root nodes
552    pub fn roots(&self) -> Vec<&TreeNode<T>> {
553        self.nodes
554            .values()
555            .filter(|n| n.parent.is_none())
556            .collect()
557    }
558
559    /// Get children of a node
560    pub fn children(&self, id: &NodeId) -> Vec<&TreeNode<T>> {
561        if let Some(node) = self.nodes.get(id) {
562            node.children
563                .iter()
564                .filter_map(|child_id| self.nodes.get(child_id))
565                .collect()
566        } else {
567            Vec::new()
568        }
569    }
570
571    /// Get descendants of a node (recursive)
572    pub fn descendants(&self, id: &NodeId) -> Vec<&TreeNode<T>> {
573        let mut descendants = Vec::new();
574        let mut to_visit = vec![id.clone()];
575        
576        while let Some(current_id) = to_visit.pop() {
577            if let Some(node) = self.nodes.get(&current_id) {
578                descendants.push(node);
579                to_visit.extend(node.children.iter().cloned());
580            }
581        }
582        
583        descendants
584    }
585
586    /// Check if the tree contains a node
587    pub fn contains(&self, id: &NodeId) -> bool {
588        self.nodes.contains_key(id)
589    }
590
591    /// Get the number of nodes
592    pub fn len(&self) -> usize {
593        self.nodes.len()
594    }
595
596    /// Check if the tree is empty
597    pub fn is_empty(&self) -> bool {
598        self.nodes.is_empty()
599    }
600
601    /// Clear all nodes
602    pub fn clear(&mut self) {
603        self.nodes.clear();
604    }
605}
606
607impl<T: Clone + PartialEq + Eq + Send + Sync> CRDT for RemoveWinsTree<T> {
608    fn replica_id(&self) -> &ReplicaId {
609        &self.replica
610    }
611}
612
613impl<T: Clone + PartialEq + Eq + Send + Sync> Mergeable for RemoveWinsTree<T> {
614    type Error = TreeError;
615
616    fn merge(&mut self, other: &Self) -> Result<(), Self::Error> {
617        for (id, node) in &other.nodes {
618            match self.nodes.get(id) {
619                Some(existing) => {
620                    // Conflict resolution: later timestamp wins
621                    if node.metadata.modified_at > existing.metadata.modified_at {
622                        self.nodes.insert(id.clone(), node.clone());
623                    }
624                }
625                None => {
626                    // New node, add it
627                    self.nodes.insert(id.clone(), node.clone());
628                }
629            }
630        }
631        Ok(())
632    }
633
634    fn has_conflict(&self, other: &Self) -> bool {
635        for (id, node) in &other.nodes {
636            if let Some(existing) = self.nodes.get(id) {
637                // Check for conflicts: same timestamp but different replica
638                if node.metadata.modified_at == existing.metadata.modified_at
639                    && node.metadata.last_modified_by != existing.metadata.last_modified_by
640                {
641                    return true;
642                }
643            }
644        }
645        false
646    }
647}
648
649#[cfg(test)]
650mod tests {
651    use super::*;
652    use super::super::ReplicaId;
653    use uuid::Uuid;
654
655    fn create_replica(id: u64) -> ReplicaId {
656        ReplicaId::from(Uuid::from_u64_pair(0, id))
657    }
658
659    #[test]
660    fn test_node_id_creation() {
661        let replica = create_replica(1);
662        let node_id = NodeId::new(replica);
663        
664        assert_eq!(node_id.replica, replica);
665        assert_ne!(node_id.id, Uuid::nil());
666    }
667
668    #[test]
669    fn test_tree_node_creation() {
670        let replica = create_replica(1);
671        let timestamp = 1234567890;
672        let node = TreeNode::new("test_value", replica, timestamp);
673        
674        assert_eq!(node.value, "test_value");
675        assert_eq!(node.metadata.created_at, timestamp);
676        assert_eq!(node.metadata.modified_at, timestamp);
677        assert_eq!(node.metadata.deleted, false);
678        assert_eq!(node.metadata.last_modified_by, replica);
679        assert!(node.parent.is_none());
680        assert!(node.children.is_empty());
681    }
682
683    #[test]
684    fn test_add_wins_tree_basic_operations() {
685        let replica = create_replica(1);
686        let mut tree = AddWinsTree::new(replica);
687        
688        // Add root
689        let root_id = tree.add_root("root", 1000);
690        assert_eq!(tree.len(), 1);
691        assert!(tree.contains(&root_id));
692        
693        // Add child
694        let child_id = tree.add_child(&root_id, "child", 2000).unwrap();
695        assert_eq!(tree.len(), 2);
696        assert!(tree.contains(&child_id));
697        
698        // Check hierarchy
699        let root = tree.get(&root_id).unwrap();
700        assert!(root.children.contains(&child_id));
701        
702        let child = tree.get(&child_id).unwrap();
703        assert_eq!(child.parent, Some(root_id));
704    }
705
706    #[test]
707    fn test_remove_wins_tree_basic_operations() {
708        let replica = create_replica(1);
709        let mut tree = RemoveWinsTree::new(replica);
710        
711        // Add root and child
712        let root_id = tree.add_root("root", 1000);
713        let child_id = tree.add_child(&root_id, "child", 2000).unwrap();
714        
715        assert_eq!(tree.len(), 2);
716        
717        // Remove child completely
718        tree.remove(&child_id).unwrap();
719        assert_eq!(tree.len(), 1);
720        assert!(!tree.contains(&child_id));
721        
722        // Root should have no children
723        let root = tree.get(&root_id).unwrap();
724        assert!(root.children.is_empty());
725    }
726
727    #[test]
728    fn test_tree_move_operation() {
729        let replica = create_replica(1);
730        let mut tree = AddWinsTree::new(replica);
731        
732        // Create tree: root -> child1 -> child2
733        let root_id = tree.add_root("root", 1000);
734        let child1_id = tree.add_child(&root_id, "child1", 2000).unwrap();
735        let child2_id = tree.add_child(&child1_id, "child2", 3000).unwrap();
736        
737        // Move child2 to root
738        tree.move_node(&child2_id, &root_id).unwrap();
739        
740        let child2 = tree.get(&child2_id).unwrap();
741        assert_eq!(child2.parent, Some(root_id.clone()));
742        
743        let root = tree.get(&root_id).unwrap();
744        assert!(root.children.contains(&child2_id));
745        
746        let child1 = tree.get(&child1_id).unwrap();
747        assert!(!child1.children.contains(&child2_id));
748    }
749
750    #[test]
751    fn test_tree_traversal() {
752        let replica = create_replica(1);
753        let mut tree = AddWinsTree::new(replica);
754        
755        // Create tree: root -> child1 -> child2
756        let root_id = tree.add_root("root", 1000);
757        let child1_id = tree.add_child(&root_id, "child1", 2000).unwrap();
758        let child2_id = tree.add_child(&child1_id, "child2", 3000).unwrap();
759        
760        // Test descendants
761        let descendants = tree.descendants(&root_id);
762        assert_eq!(descendants.len(), 2);
763        
764        // Test children
765        let root_children = tree.children(&root_id);
766        assert_eq!(root_children.len(), 1);
767        assert_eq!(root_children[0].id, child1_id);
768    }
769
770    #[test]
771    fn test_tree_merge() {
772        let replica1 = create_replica(1);
773        let replica2 = create_replica(2);
774        
775        let mut tree1 = AddWinsTree::new(replica1);
776        let mut tree2 = AddWinsTree::new(replica2);
777        
778        // Add nodes to both trees
779        let root1_id = tree1.add_root("root1", 1000);
780        let root2_id = tree2.add_root("root2", 2000);
781        
782        // Merge tree2 into tree1
783        tree1.merge(&tree2).unwrap();
784        
785        // Both roots should be present
786        assert_eq!(tree1.len(), 2);
787        assert!(tree1.contains(&root1_id));
788        assert!(tree1.contains(&root2_id));
789    }
790
791    #[test]
792    fn test_tree_configuration() {
793        let replica = create_replica(1);
794        let config = TreeConfig {
795            strategy: TreeStrategy::RemoveWins,
796            preserve_deleted: false,
797            max_depth: Some(5),
798            max_children: Some(10),
799        };
800        
801        let tree: AddWinsTree<String> = AddWinsTree::with_config(replica, config);
802        assert_eq!(tree.config.strategy, TreeStrategy::RemoveWins);
803        assert_eq!(tree.config.max_depth, Some(5));
804        assert_eq!(tree.config.max_children, Some(10));
805    }
806}