#![cfg(feature = "assemblage-broadcast-integration-tests")] use std::{collections::BTreeSet, iter::FromIterator}; use assemblage_db::{ broadcast::Broadcast, data::{Id, Layout, Node}, tx, Db, }; use assemblage_kv::{ storage::{self, Storage}, test, }; use assemblage_view::{ model::{Block, Span, Tile}, bindings::{open, DbContainer, RefreshError, SyncError, SyncedSection, SyncedSubsection}, }; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); test! { async fn sync_refresh_broadcast_tile(storage) -> Result<(), RefreshError> { let db = Db::open(storage).await?; let root_id = Id::root(); let (_id1, _id2, broadcast) = { let text1 = Node::text("foo"); let id1 = tx!(|db| db.add(text1).await?); tx!(|db| db.push(root_id, id1).await?); tx!(|db| assert_eq!(db.get(id1).await?.unwrap().str()?, "foo")); let text2 = Node::text("foobar"); let id2 = tx!(|db| db.add(text2).await?); tx!(|db| db.push(root_id, id2).await?); tx!(|db| assert_eq!(db.get(id2).await?.unwrap().str()?, "foobar")); let broadcast = tx!(|db| db.publish_broadcast(root_id).await?); (id1, id2, broadcast) }; let store_name = "store_in_container".to_string(); let db = open(store_name.clone()).await.expect("Could not open DB container"); let tile = refresh(&db, format!("broadcast:{}", broadcast.broadcast_id)) .await .expect("Could not refresh tile"); let expected_preview = Block::Text { styles: BTreeSet::new(), spans: vec![ Span::Text { styles: BTreeSet::new(), text: "foo".to_string(), } ] }; assert_eq!(tile.preview, expected_preview); storage::purge(store_name).await?; storage::purge(broadcast.broadcast_id.to_string()).await?; } } test! { async fn sync_tile_with_broadcast(storage) -> Result<(), SyncError> { let store_name = storage.name().to_string(); let db = Db::open(storage).await?; let root_id = Id::root(); let (_id1, id2, broadcast, last_updated) = { let text1 = Node::text("foo"); let id1 = tx!(|db| db.add(text1).await?); tx!(|db| db.push(root_id, id1).await?); tx!(|db| assert_eq!(db.get(id1).await?.unwrap().str()?, "foo")); let text2 = Node::text("foo"); let text3 = Node::text("bar"); let id2 = tx!(|db| db.add(Node::list(Layout::Page, vec![text2, text3])).await?); tx!(|db| db.push(root_id, id2).await?); let last_updated = tx!(|db| db.last_updated().await?.unwrap()); let broadcast = tx!(|db| db.publish_broadcast(root_id).await?); (id1, id2, broadcast, last_updated) }; let db = open(store_name).await.expect("Could not open DB container"); let tile = sync(&db, Some(Id::root().to_string()), vec![ SyncedSection::Edited { blocks: vec![ SyncedSubsection::Text { markup: "baz".to_string() } ] }, SyncedSection::Existing { id: id2 }, ]).await.expect("Could not sync edited sections"); assert_eq!(tile.id, root_id); let expected_preview = Block::Text { styles: BTreeSet::new(), spans: vec![ Span::Text { styles: BTreeSet::new(), text: "baz".to_string(), } ] }; assert_eq!(tile.preview, expected_preview); let expected_broadcast = Broadcast { broadcast_id: broadcast.broadcast_id.clone(), node_id: root_id, last_updated, expiration: broadcast.expiration, }; assert_eq!(tile.broadcasts, BTreeSet::from_iter(vec![expected_broadcast])); assert_eq!(tile.sections[0].id, None); assert_eq!(tile.sections[0].subsections.len(), 1); let section1_id = tile.sections[0].subsections[0].id; let tile = sync(&db, Some(root_id.to_string()), vec![ SyncedSection::Existing { id: id2 }, SyncedSection::Existing { id: section1_id }, ]).await.expect("Could not sync moved existing sections"); let expected_preview = Block::Text { styles: BTreeSet::new(), spans: vec![ Span::Text { styles: BTreeSet::new(), text: "foo".to_string(), } ] }; assert_eq!(tile.preview, expected_preview); let subsection2_id = tile.sections[1].subsections[0].id; let tile = sync(&db, Some(root_id.to_string()), vec![ SyncedSection::Linked { id: subsection2_id }, SyncedSection::Existing { id: id2 }, ]).await.expect("Could not sync linked sections"); let expected_preview = Block::Text { styles: BTreeSet::new(), spans: vec![ Span::Text { styles: BTreeSet::new(), text: "bar".to_string(), } ] }; assert_eq!(tile.preview, expected_preview); } } #[cfg(not(target_arch = "wasm32"))] async fn refresh(db: &DbContainer, id: String) -> Result { db.refresh(id).await } #[cfg(target_arch = "wasm32")] async fn refresh(db: &DbContainer, id: String) -> Result { let promise = db.refresh(id); let result = wasm_bindgen_futures::JsFuture::from(promise).await?; let tile: Tile = result.into_serde().expect("Invalid result tile"); Ok(tile) } #[cfg(not(target_arch = "wasm32"))] async fn sync( db: &DbContainer, id: Option, sections: Vec, ) -> Result { db.sync(id, sections).await } #[cfg(target_arch = "wasm32")] async fn sync( db: &DbContainer, id: Option, sections: Vec, ) -> Result { let sections_as_js = JsValue::from_serde(§ions).expect("Invalid synced sections"); let promise = db.sync(id, sections_as_js); let result = wasm_bindgen_futures::JsFuture::from(promise).await?; let tile: Tile = result.into_serde().expect("Invalid result tile"); Ok(tile) }