jazz-rs 0.7.0

A framework for CRDT based, end-to-end enrypted distributed apps
Documentation
use futures::{Future, FutureExt, StreamExt};
use jazz_rs::{DocID, InvitationKind, Jazz};
use jmbl::Input;
use litl::Val;
use mofo::Mofo;
use caro::Remote;
use tracing::{debug, trace};
use std::time::Duration;

macro_rules! timeout {
    ($f:expr) => {
        tokio::time::timeout(Duration::from_millis(500), $f).map(|r| r.expect("Timed out"))
    };
}

#[tokio::test]
async fn can_create_team_and_share_document_for_reading() {
    // traceful::init_test_trace();

    let (_, _, background, setup_and_basic_assertions) =
        create_team_and_share_document(InvitationKind::Reader);

    background.run_until(setup_and_basic_assertions).await;
}

fn create_team_and_share_document(
    invitation_kind: InvitationKind,
) -> (Jazz, Jazz, Mofo, impl Future<Output = DocID>) {
    let background = Mofo::new();
    let jazz1 = Jazz::new("jazz1".to_string(), background.clone());
    let jazz2 = Jazz::new("jazz2".to_string(), background.clone());

    let setup = {
        let jazz1 = jazz1.clone();
        let jazz2 = jazz2.clone();

        async move {
            let team = timeout!(jazz1.create_team()).await;
            let doc = timeout!(jazz1.create_document(
                &team,
                Input::CollabMap(vec![("test".to_string(), Val::str("Hello World").into())]),
            ))
            .await;
            assert_eq!(
                doc.managed_jmbl.current_root().if_map().unwrap().to_litl(),
                Val::object([("test", Val::str("Hello World"))])
            );
            let invitation_token = team.create_invitation(
                invitation_kind,
                None,
                None,
                None,
                ti64::now() + 24 * 60 * 60 * 1000,
            )
            .await
            .unwrap();

            let (jazz1_as_remote, jazz2_as_remote) =
                Remote::new_connected_test_pair("jazz1", "jazz2");

            jazz1.add_remote(jazz2_as_remote).await;
            jazz2.add_remote(jazz1_as_remote).await;

            let jazz2 = jazz2.clone();

            debug!("invitation token: {:?}", invitation_token);
            jazz2.join_team(invitation_token).await.unwrap();
            let managed_doc2 = timeout!(jazz2.load_document(doc.id())).await.managed_jmbl;

            let doc2_updates = managed_doc2.updates("test2".to_owned());

            let (first_root, _) = timeout!(doc2_updates
                .filter(|(root, _)| {
                    let is_null = root.if_plain() == Ok(&Val::null());
                    trace!(root = ?root, is_null = is_null, "Got root");
                    futures::future::ready(!is_null)
                })
                .next())
            .await
            .unwrap();

            assert_eq!(
                first_root.if_map().unwrap().to_litl(),
                Val::object([("test", Val::str("Hello World"))])
            );

            doc.id()
        }
    };

    (jazz1, jazz2, background, setup)
}

#[tokio::test]
async fn can_create_team_and_share_document_for_writing() {
    // traceful::init_test_trace();

    let (jazz1, jazz2, background, setup_and_basic_assertions) =
        create_team_and_share_document(InvitationKind::Writer);

    background
        .run_until(async {
            // TODO: #70 wait until we definitely have write access, and make waiting for that easy

            let doc_id = setup_and_basic_assertions.await;

            let managed_doc2 = timeout!(jazz2.load_document(doc_id.clone()))
                .await
                .managed_jmbl;

            let write_view = managed_doc2.start_writing();
            write_view
                .get_root()
                .if_map_mut()
                .unwrap()
                .insert("test2", "Litl World");
            managed_doc2.finish_writing(write_view);

            assert_eq!(
                managed_doc2.current_root().if_map().unwrap().to_litl(),
                Val::object([
                    ("test", Val::str("Hello World")),
                    ("test2", Val::str("Litl World"))
                ])
            );

            let managed_doc1 = timeout!(jazz1.load_document(doc_id)).await.managed_jmbl;

            let doc1_updates = managed_doc1.updates("test1_after_sharing".to_owned());

            let (first_root_with_jazz2_update, _) = timeout!(doc1_updates
                .filter(|(root, _)| {
                    let has_update = root.if_map().unwrap().to_litl().get("test2").is_some();
                    trace!(root = ?root, has_update = has_update, "Got root");
                    futures::future::ready(has_update)
                })
                .next())
            .await
            .unwrap();

            assert_eq!(
                first_root_with_jazz2_update.if_map().unwrap().to_litl(),
                Val::object([
                    ("test", Val::str("Hello World")),
                    ("test2", Val::str("Litl World"))
                ])
            );
        })
        .await
}