loro 1.12.0

Loro is a high-performance CRDTs framework. Make your app collaborative efforlessly.
Documentation
use loro::{ExportMode, LoroDoc, LoroResult, ToJson, TreeParentId};
use pretty_assertions::assert_eq;
use serde_json::{json, Value};

fn deep_json(doc: &LoroDoc) -> Value {
    doc.get_deep_value().to_json_value()
}

#[test]
fn concurrent_delete_ancestor_and_move_descendant_out_keeps_moved_node_alive() -> LoroResult<()> {
    let base = LoroDoc::new();
    base.set_peer_id(701)?;
    let tree = base.get_tree("outline");
    tree.enable_fractional_index(0);

    let root = tree.create(TreeParentId::Root)?;
    let keep = tree.create_at(root, 0)?;
    let deleted_parent = tree.create_at(root, 1)?;
    let moved_child = tree.create_at(deleted_parent, 0)?;
    tree.get_meta(root)?.insert("name", "root")?;
    tree.get_meta(keep)?.insert("name", "keep")?;
    tree.get_meta(deleted_parent)?.insert("name", "delete-me")?;
    tree.get_meta(moved_child)?.insert("name", "move-me")?;
    base.commit();

    let base_updates = base.export(ExportMode::all_updates())?;
    let base_vv = base.oplog_vv();

    let alice = LoroDoc::new();
    alice.set_peer_id(702)?;
    alice.import(&base_updates)?;
    let alice_tree = alice.get_tree("outline");
    alice_tree.delete(deleted_parent)?;
    alice_tree.get_meta(keep)?.insert("actor", "alice")?;
    alice.commit();
    let alice_updates = alice.export(ExportMode::updates(&base_vv))?;

    let bob = LoroDoc::new();
    bob.set_peer_id(703)?;
    bob.import(&base_updates)?;
    let bob_tree = bob.get_tree("outline");
    bob_tree.mov_to(moved_child, keep, 0)?;
    bob_tree.get_meta(moved_child)?.insert("actor", "bob")?;
    bob.commit();
    let bob_updates = bob.export(ExportMode::updates(&base_vv))?;

    alice.import(&bob_updates)?;
    bob.import(&alice_updates)?;
    assert_eq!(deep_json(&alice), deep_json(&bob));

    let merged_tree = alice.get_tree("outline");
    assert_eq!(merged_tree.parent(root), Some(TreeParentId::Root));
    assert_eq!(merged_tree.parent(keep), Some(TreeParentId::Node(root)));
    assert_eq!(
        merged_tree.parent(deleted_parent),
        Some(TreeParentId::Deleted)
    );
    assert!(merged_tree.is_node_deleted(&deleted_parent)?);
    assert!(!merged_tree.is_node_deleted(&moved_child)?);
    assert_eq!(
        merged_tree.parent(moved_child),
        Some(TreeParentId::Node(keep))
    );
    assert_eq!(merged_tree.children(root), Some(vec![keep]));
    assert_eq!(merged_tree.children(keep), Some(vec![moved_child]));
    assert!(merged_tree
        .get_nodes(false)
        .iter()
        .all(|node| node.id != deleted_parent));
    assert!(merged_tree
        .get_nodes(true)
        .iter()
        .any(|node| node.id == deleted_parent));
    assert_eq!(
        merged_tree
            .get_meta(moved_child)?
            .get("actor")
            .unwrap()
            .get_deep_value()
            .to_json_value(),
        json!("bob")
    );

    let restored = LoroDoc::from_snapshot(&alice.export(ExportMode::Snapshot)?)?;
    assert_eq!(deep_json(&restored), deep_json(&alice));
    let restored_tree = restored.get_tree("outline");
    assert_eq!(
        restored_tree.parent(deleted_parent),
        Some(TreeParentId::Deleted)
    );
    assert_eq!(
        restored_tree.parent(moved_child),
        Some(TreeParentId::Node(keep))
    );

    Ok(())
}