assemblage_db 0.1.0

Distributed Document/Graph DB for Connected and Overlapping Pages
Documentation
#![allow(clippy::float_cmp)]

use assemblage_db::{
    data::{Id, Layout, Node},
    tx, Db, Result,
};
use assemblage_kv::test;

#[cfg(target_arch = "wasm32")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

test! {
    async fn overlap_after_swap(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let foo_id = tx!(|db| db.add(Node::text("This is a sentence containing foo")).await?);
        let bar_id = tx!(|db| db.add(Node::text("This is a sentence containing bar")).await?);

        tx!(|db| db.add(Node::list(Layout::Page, vec![foo_id])).await?);
        tx!(|db| db.add(Node::list(Layout::Page, vec![bar_id])).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);

        tx!(|db| db.swap(foo_id, Node::text("something completely different")).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 0);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 0);

        tx!(|db| db.swap(foo_id, Node::text("sentence containing foo")).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);
    }
}

test! {
    async fn overlap_after_swap_of_nested_child(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let text_foo_id = tx!(|db| db.add(Node::text("This is a sentence containing foo")).await?);
        let text_bar_id = tx!(|db| db.add(Node::text("This is a sentence containing bar")).await?);

        let foo_id = tx!(|db| db.add(Node::list(Layout::Chain, vec![text_foo_id])).await?);
        let bar_id = tx!(|db| db.add(Node::list(Layout::Chain, vec![text_bar_id])).await?);

        tx!(|db| db.add(Node::list(Layout::Page, vec![foo_id])).await?);
        tx!(|db| db.add(Node::list(Layout::Page, vec![bar_id])).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);

        tx!(|db| db.swap(foo_id, Node::text("something completely different")).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 0);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 0);

        tx!(|db| db.swap(foo_id, Node::text("sentence containing foo")).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);
    }
}

test! {
    async fn overlap_after_push(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let foo_id = tx!(|db| db.add(Node::list(Layout::Chain, vec![
            Node::text("Thi"),
        ])).await?);

        let bar_id = tx!(|db| db.add(Node::text("This is a sentence about bar")).await?);

        tx!(|db| db.add(Node::list(Layout::Page, vec![foo_id])).await?);
        tx!(|db| db.add(Node::list(Layout::Page, vec![bar_id])).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 0);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 0);

        tx!(|db| db.push(foo_id, Node::text("s a sentence about foo")).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);
    }
}

test! {
    async fn overlap_after_remove(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let foo_id = tx!(|db| db.add(Node::list(Layout::Chain, vec![
            Node::text("foo"),
            Node::text("bar"),
            Node::text("baz"),
            Node::text("qux"),
        ])).await?);

        let bar_id = tx!(|db| db.add(Node::text("just fooqux")).await?);

        tx!(|db| db.add(Node::list(Layout::Page, vec![foo_id])).await?);
        tx!(|db| db.add(Node::list(Layout::Page, vec![bar_id])).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 0);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 0);

        tx!(|db| db.remove(foo_id, 2).await?);
        tx!(|db| db.remove(foo_id, 1).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);
    }
}

test! {
    async fn overlap_after_replacing_empty_text(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let shared_text = "This is a sentence about something";

        let foo_id = tx!(|db| db.add(Node::text("")).await?);
        let bar_id = tx!(|db| db.add(Node::text("")).await?);

        let page_foo_id = tx!(|db| db.add(Node::list(Layout::Page, vec![foo_id])).await?);
        let page_bar_id = tx!(|db| db.add(Node::list(Layout::Page, vec![bar_id])).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 0);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 0);

        tx!(|db| db.replace(page_foo_id, 0, Node::text(shared_text)).await?);
        tx!(|db| db.replace(page_bar_id, 0, Node::text(shared_text)).await?);

        let foo_id = tx!(|db| db.get(page_foo_id).await?.unwrap().children()[0].id()?);
        let bar_id = tx!(|db| db.get(page_bar_id).await?.unwrap().children()[0].id()?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);
    }
}

test! {
    async fn overlap_after_incrementally_replacing_text(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let foo_id = tx!(|db| db.add(Node::text("")).await?);
        let bar_id = tx!(|db| db.add(Node::text("")).await?);

        let page_foo_id = tx!(|db| db.add(Node::list(Layout::Page, vec![foo_id])).await?);
        let page_bar_id = tx!(|db| db.add(Node::list(Layout::Page, vec![bar_id])).await?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 0);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 0);

        tx!(|db| db.replace(page_foo_id, 0, Node::text("This")).await?);
        tx!(|db| db.replace(page_foo_id, 0, Node::text("This is")).await?);
        tx!(|db| db.replace(page_foo_id, 0, Node::text("This is another")).await?);
        tx!(|db| db.replace(page_foo_id, 0, Node::text("This is another sentence")).await?);
        tx!(|db| db.replace(page_foo_id, 0, Node::text("This is another sentence about")).await?);
        tx!(|db| db.replace(page_foo_id, 0, Node::text("This is another sentence about foo")).await?);

        tx!(|db| db.replace(page_bar_id, 0, Node::text("Just")).await?);
        tx!(|db| db.replace(page_bar_id, 0, Node::text("Just another")).await?);
        tx!(|db| db.replace(page_bar_id, 0, Node::text("Just another sentence")).await?);
        tx!(|db| db.replace(page_bar_id, 0, Node::text("Just another sentence about")).await?);
        tx!(|db| db.replace(page_bar_id, 0, Node::text("Just another sentence about bar")).await?);

        let foo_id = tx!(|db| db.get(page_foo_id).await?.unwrap().children()[0].id()?);
        let bar_id = tx!(|db| db.get(page_bar_id).await?.unwrap().children()[0].id()?);

        assert_eq!(db.current().await.overlaps(foo_id).await?.len(), 1);
        assert_eq!(db.current().await.overlaps(bar_id).await?.len(), 1);
    }
}

test! {
    async fn no_overlap_between_ancestor_and_child_after_replace(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let ancestor_page_id = tx!(|db| db.add(Node::list(Layout::Page, vec![
            Node::list(Layout::Page, vec![Node::text("")]),
        ])).await?);

        let text1_id = tx!(|db| db.add(Node::text("some text")).await?);

        let page_id = tx!(|db| db.add(Node::list(Layout::Page, vec![text1_id])).await?);

        tx!(|db| db.insert(ancestor_page_id, 0, page_id).await?);

        assert_eq!(db.current().await.overlaps(text1_id).await?.len(), 0);

        let text2_id = tx!(|db| db.add(Node::text("some text")).await?);

        tx!(|db| db.remove(page_id, 0).await?);
        tx!(|db| db.insert(page_id, 0, text2_id).await?);
    }
}

test! {
    async fn overlaps_after_restoring_node(storage) -> Result<()> {
        let db = Db::open(storage).await?;

        let text1_id = tx!(|db| {
            db.add(Node::text("this should be found as an overlap")).await?
        });
        let text2_id = tx!(|db| {
            db.add(Node::text("this should be found as an overlap...")).await?
        });

        let page1_id = tx!(|db| {
            db.add(Node::list(Layout::Page, vec![text1_id])).await?
        });

        tx!(|db| db.push(Id::root(), page1_id).await?);

        tx!(|db| db.add(Node::list(Layout::Page, vec![text2_id])).await?);

        tx!(|db| {
            assert_eq!(db.overlaps(text1_id).await?.len(), 1);
            assert_eq!(db.overlaps(text1_id).await?[0].id, text2_id);
            assert_eq!(db.overlaps(text2_id).await?.len(), 1);
            assert_eq!(db.overlaps(text2_id).await?[0].id, text1_id);
        });

        tx!(|db| db.remove(Id::root(), 0).await?);

        tx!(|db| {
            assert_eq!(db.get(text1_id).await?, None);
            assert_eq!(db.overlaps(text2_id).await?.len(), 0);
            assert!(db.overlaps(text1_id).await.is_err());
        });

        tx!(|db| db.restore(page1_id).await?);

        tx!(|db| {
            assert!(db.get(text1_id).await?.is_some());
            assert_eq!(db.overlaps(text1_id).await?.len(), 1);
            assert_eq!(db.overlaps(text1_id).await?[0].id, text2_id);
            assert_eq!(db.overlaps(text2_id).await?.len(), 1);
            assert_eq!(db.overlaps(text2_id).await?[0].id, text1_id);
        });
    }
}