loro-internal 1.12.0

Loro internal library. Do not use it directly as it's not stable.
Documentation
use crate::{
    loro::ExportMode, state::fail_next_import_state_apply_for_test, version::VersionVector, LoroDoc,
};
use std::sync::atomic::Ordering::{Acquire, Release};

fn assert_visible_op_count_cache_matches_exact(doc: &LoroDoc) {
    let exact = doc.oplog().lock().visible_op_count_exact();
    assert_eq!(doc.visible_op_count.load(Acquire), exact);

    let _state = doc.app_state().lock();
    assert_eq!(doc.len_ops(), exact);
}

#[test]
fn len_ops_is_available_while_state_is_locked_with_local_ops() {
    let doc = LoroDoc::new_auto_commit();
    doc.set_peer_id(1).unwrap();
    doc.get_map("map").insert("k", 1).unwrap();

    let _state = doc.app_state().lock();
    assert_eq!(doc.len_ops(), 1);
}

#[test]
fn len_ops_tracks_snapshot_import_while_state_is_locked() {
    let src = LoroDoc::new_auto_commit();
    src.set_peer_id(2).unwrap();
    src.get_map("map").insert("k", 1).unwrap();
    src.commit_then_renew();
    let expected = src.len_ops();
    let snapshot = src.export(ExportMode::Snapshot).unwrap();

    let dst = LoroDoc::new();
    dst.import(&snapshot).unwrap();

    let _state = dst.app_state().lock();
    assert_eq!(dst.len_ops(), expected);
}

#[test]
fn len_ops_prefers_exact_oplog_value_when_lock_order_allows() {
    let doc = LoroDoc::new_auto_commit();
    doc.set_peer_id(3).unwrap();
    doc.get_map("map").insert("k", 1).unwrap();

    doc.visible_op_count.store(0, Release);
    assert_eq!(doc.len_ops(), 1);
}

#[test]
fn visible_op_count_cache_tracks_pending_activation() {
    let src = LoroDoc::new_auto_commit();
    src.set_peer_id(4).unwrap();
    let map = src.get_map("map");
    map.insert("base", "base").unwrap();
    let update_base = src
        .export(ExportMode::updates(&VersionVector::default()))
        .unwrap();
    let version_base = src.oplog_vv();

    map.insert("later", "later").unwrap();
    let update_later = src.export(ExportMode::updates(&version_base)).unwrap();

    let dst = LoroDoc::new();
    let status = dst.import(&update_later).unwrap();
    assert!(status.pending.is_some());
    assert_visible_op_count_cache_matches_exact(&dst);
    assert_eq!(dst.len_ops(), 0);

    let status = dst.import(&update_base).unwrap();
    assert!(status.pending.is_none());
    assert_eq!(dst.len_ops(), src.len_ops());
    assert_visible_op_count_cache_matches_exact(&dst);
}

#[test]
fn visible_op_count_cache_rolls_back_after_failed_pending_activation() {
    let src = LoroDoc::new_auto_commit();
    src.set_peer_id(5).unwrap();
    let map = src.get_map("map");
    map.insert("base", "base").unwrap();
    let update_base = src
        .export(ExportMode::updates(&VersionVector::default()))
        .unwrap();
    let version_base = src.oplog_vv();

    map.insert("later", "later").unwrap();
    let update_later = src.export(ExportMode::updates(&version_base)).unwrap();

    let dst = LoroDoc::new();
    dst.import(&update_later).unwrap();
    assert_eq!(dst.len_ops(), 0);
    assert_visible_op_count_cache_matches_exact(&dst);

    fail_next_import_state_apply_for_test();
    let err = dst.import(&update_base).unwrap_err();
    assert!(
        err.to_string().contains("state apply failpoint"),
        "unexpected error: {err:?}"
    );
    assert_eq!(dst.len_ops(), 0);
    assert_visible_op_count_cache_matches_exact(&dst);

    dst.import(&update_base).unwrap();
    assert_eq!(dst.len_ops(), src.len_ops());
    assert_visible_op_count_cache_matches_exact(&dst);
}