Nathan.fooo 122a392bf8
chore: update client api (#5208)
* chore: update client api

* chore: rename test target

* chore: fix test
2024-04-26 18:00:56 +08:00

561 lines
17 KiB
Rust

use event_integration_test::event_builder::EventBuilder;
use event_integration_test::EventIntegrationTest;
use flowy_folder::entities::icon::{UpdateViewIconPayloadPB, ViewIconPB, ViewIconTypePB};
use flowy_folder::entities::*;
use flowy_user::errors::ErrorCode;
#[tokio::test]
async fn create_workspace_event_test() {
let test = EventIntegrationTest::new_anon().await;
let request = CreateWorkspacePayloadPB {
name: "my second workspace".to_owned(),
desc: "".to_owned(),
};
let view_pb = EventBuilder::new(test)
.event(flowy_folder::event_map::FolderEvent::CreateFolderWorkspace)
.payload(request)
.async_send()
.await
.parse::<flowy_folder::entities::ViewPB>();
assert_eq!(view_pb.parent_view_id, "my second workspace".to_owned());
}
// #[tokio::test]
// async fn open_workspace_event_test() {
// let test = EventIntegrationTest::new_with_guest_user().await;
// let payload = CreateWorkspacePayloadPB {
// name: "my second workspace".to_owned(),
// desc: "".to_owned(),
// };
// // create a workspace
// let resp_1 = EventBuilder::new(test.clone())
// .event(flowy_folder::event_map::FolderEvent::CreateWorkspace)
// .payload(payload)
// .async_send()
// .await
// .parse::<flowy_folder::entities::WorkspacePB>();
//
// // open the workspace
// let payload = WorkspaceIdPB {
// value: Some(resp_1.id.clone()),
// };
// let resp_2 = EventBuilder::new(test)
// .event(flowy_folder::event_map::FolderEvent::OpenWorkspace)
// .payload(payload)
// .async_send()
// .await
// .parse::<flowy_folder::entities::WorkspacePB>();
//
// assert_eq!(resp_1.id, resp_2.id);
// assert_eq!(resp_1.name, resp_2.name);
// }
#[tokio::test]
async fn create_view_event_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let view = test
.create_view(&current_workspace.id, "My first view".to_string())
.await;
assert_eq!(view.parent_view_id, current_workspace.id);
assert_eq!(view.name, "My first view");
assert_eq!(view.layout, ViewLayoutPB::Document);
}
#[tokio::test]
async fn update_view_event_with_name_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let view = test
.create_view(&current_workspace.id, "My first view".to_string())
.await;
let error = test
.update_view(UpdateViewPayloadPB {
view_id: view.id.clone(),
name: Some("My second view".to_string()),
..Default::default()
})
.await;
assert!(error.is_none());
let view = test.get_view(&view.id).await;
assert_eq!(view.name, "My second view");
}
#[tokio::test]
async fn update_view_icon_event_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let view = test
.create_view(&current_workspace.id, "My first view".to_string())
.await;
let new_icon = ViewIconPB {
ty: ViewIconTypePB::Emoji,
value: "👍".to_owned(),
};
let error = test
.update_view_icon(UpdateViewIconPayloadPB {
view_id: view.id.clone(),
icon: Some(new_icon.clone()),
})
.await;
assert!(error.is_none());
let view = test.get_view(&view.id).await;
assert_eq!(view.icon, Some(new_icon));
}
#[tokio::test]
async fn delete_view_event_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let view = test
.create_view(&current_workspace.id, "My first view".to_string())
.await;
test.delete_view(&view.id).await;
// Try the read the view
let payload = ViewIdPB {
value: view.id.clone(),
};
let error = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::GetView)
.payload(payload)
.async_send()
.await
.error()
.unwrap();
assert_eq!(error.code, ErrorCode::RecordNotFound);
}
#[tokio::test]
async fn put_back_trash_event_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let view = test
.create_view(&current_workspace.id, "My first view".to_string())
.await;
test.delete_view(&view.id).await;
// After delete view, the view will be moved to trash
let payload = ViewIdPB {
value: view.id.clone(),
};
let error = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::GetView)
.payload(payload)
.async_send()
.await
.error()
.unwrap();
assert_eq!(error.code, ErrorCode::RecordNotFound);
let payload = TrashIdPB {
id: view.id.clone(),
};
EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::RestoreTrashItem)
.payload(payload)
.async_send()
.await;
let payload = ViewIdPB {
value: view.id.clone(),
};
let error = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::GetView)
.payload(payload)
.async_send()
.await
.error();
assert!(error.is_none());
}
#[tokio::test]
async fn delete_view_permanently_event_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let view = test
.create_view(&current_workspace.id, "My first view".to_string())
.await;
let payload = RepeatedViewIdPB {
items: vec![view.id.clone()],
};
// delete the view. the view will be moved to trash
EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::DeleteView)
.payload(payload)
.async_send()
.await;
let trash = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::ListTrashItems)
.async_send()
.await
.parse::<flowy_folder::entities::RepeatedTrashPB>()
.items;
assert_eq!(trash.len(), 1);
assert_eq!(trash[0].id, view.id);
// delete the view from trash
let payload = RepeatedTrashIdPB {
items: vec![TrashIdPB {
id: view.id.clone(),
}],
};
EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::PermanentlyDeleteTrashItem)
.payload(payload)
.async_send()
.await;
// After delete the last view, the trash should be empty
let trash = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::ListTrashItems)
.async_send()
.await
.parse::<flowy_folder::entities::RepeatedTrashPB>()
.items;
assert!(trash.is_empty());
}
#[tokio::test]
async fn delete_all_trash_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
for i in 0..3 {
let view = test
.create_view(&current_workspace.id, format!("My {} view", i))
.await;
let payload = RepeatedViewIdPB {
items: vec![view.id.clone()],
};
// delete the view. the view will be moved to trash
EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::DeleteView)
.payload(payload)
.async_send()
.await;
}
let trash = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::ListTrashItems)
.async_send()
.await
.parse::<flowy_folder::entities::RepeatedTrashPB>()
.items;
assert_eq!(trash.len(), 3);
// Delete all the trash
EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::PermanentlyDeleteAllTrashItem)
.async_send()
.await;
// After delete the last view, the trash should be empty
let trash = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::ListTrashItems)
.async_send()
.await
.parse::<flowy_folder::entities::RepeatedTrashPB>()
.items;
assert!(trash.is_empty());
}
#[tokio::test]
async fn multiple_hierarchy_view_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
for i in 1..4 {
let parent = test
.create_view(&current_workspace.id, format!("My {} view", i))
.await;
for j in 1..3 {
let child = test
.create_view(&parent.id, format!("My {}-{} view", i, j))
.await;
for k in 1..2 {
let _sub_child = test
.create_view(&child.id, format!("My {}-{}-{} view", i, j, k))
.await;
}
}
}
let mut views = test.get_all_workspace_views().await;
// There will be one default view when AppFlowy is initialized. So there will be 4 views in total
assert_eq!(views.len(), 4);
views.remove(0);
// workspace
// - view1
// - view1-1
// - view1-1-1
// - view1-2
// - view1-2-1
// - view2
// - view2-1
// - view2-1-1
// - view2-2
// - view2-2-1
// - view3
// - view3-1
// - view3-1-1
// - view3-2
// - view3-2-1
assert_eq!(views[0].name, "My 1 view");
assert_eq!(views[1].name, "My 2 view");
assert_eq!(views[2].name, "My 3 view");
assert_eq!(views[0].child_views.len(), 2);
// By default only the first level of child views will be loaded
assert!(views[0].child_views[0].child_views.is_empty());
for (i, view) in views.into_iter().enumerate() {
for (j, child_view) in view.child_views.into_iter().enumerate() {
let payload = ViewIdPB {
value: child_view.id.clone(),
};
let child = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::GetView)
.payload(payload)
.async_send()
.await
.parse::<flowy_folder::entities::ViewPB>();
assert_eq!(child.name, format!("My {}-{} view", i + 1, j + 1));
assert_eq!(child.child_views.len(), 1);
// By default only the first level of child views will be loaded
assert!(child.child_views[0].child_views.is_empty());
for (k, _child_view) in child_view.child_views.into_iter().enumerate() {
// Get the last level view
let sub_child = test.get_view(&child.id).await;
assert_eq!(child.name, format!("My {}-{}-{} view", i + 1, j + 1, k + 1));
assert!(sub_child.child_views.is_empty());
}
}
}
}
#[tokio::test]
async fn move_view_event_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
for i in 1..4 {
let parent = test
.create_view(&current_workspace.id, format!("My {} view", i))
.await;
for j in 1..3 {
let _ = test
.create_view(&parent.id, format!("My {}-{} view", i, j))
.await;
}
}
let views = test.get_all_workspace_views().await;
// There will be one default view when AppFlowy is initialized. So there will be 4 views in total
assert_eq!(views.len(), 4);
assert_eq!(views[1].name, "My 1 view");
assert_eq!(views[2].name, "My 2 view");
assert_eq!(views[3].name, "My 3 view");
let payload = MoveViewPayloadPB {
view_id: views[1].id.clone(),
from: 1,
to: 2,
};
let _ = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::MoveView)
.payload(payload)
.async_send()
.await;
let views = test.get_all_workspace_views().await;
assert_eq!(views[1].name, "My 2 view");
assert_eq!(views[2].name, "My 1 view");
assert_eq!(views[3].name, "My 3 view");
}
#[tokio::test]
async fn move_view_event_after_delete_view_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
for i in 1..6 {
let _ = test
.create_view(&current_workspace.id, format!("My {} view", i))
.await;
}
let views = test.get_all_workspace_views().await;
assert_eq!(views[1].name, "My 1 view");
assert_eq!(views[2].name, "My 2 view");
assert_eq!(views[3].name, "My 3 view");
assert_eq!(views[4].name, "My 4 view");
assert_eq!(views[5].name, "My 5 view");
test.delete_view(&views[3].id).await;
// There will be one default view when AppFlowy is initialized. So there will be 4 views in total
let views = test.get_all_workspace_views().await;
assert_eq!(views[1].name, "My 1 view");
assert_eq!(views[2].name, "My 2 view");
assert_eq!(views[3].name, "My 4 view");
assert_eq!(views[4].name, "My 5 view");
let payload = MoveViewPayloadPB {
view_id: views[1].id.clone(),
from: 1,
to: 3,
};
let _ = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::MoveView)
.payload(payload)
.async_send()
.await;
let views = test.get_all_workspace_views().await;
assert_eq!(views[1].name, "My 2 view");
assert_eq!(views[2].name, "My 4 view");
assert_eq!(views[3].name, "My 1 view");
assert_eq!(views[4].name, "My 5 view");
}
#[tokio::test]
async fn move_view_event_after_delete_view_test2() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let parent = test
.create_view(&current_workspace.id, "My view".to_string())
.await;
for j in 1..6 {
let _ = test
.create_view(&parent.id, format!("My 1-{} view", j))
.await;
}
let views = test.get_view(&parent.id).await.child_views;
assert_eq!(views.len(), 5);
assert_eq!(views[0].name, "My 1-1 view");
assert_eq!(views[1].name, "My 1-2 view");
assert_eq!(views[2].name, "My 1-3 view");
assert_eq!(views[3].name, "My 1-4 view");
assert_eq!(views[4].name, "My 1-5 view");
test.delete_view(&views[2].id).await;
let payload = MoveViewPayloadPB {
view_id: views[0].id.clone(),
from: 0,
to: 2,
};
let _ = EventBuilder::new(test.clone())
.event(flowy_folder::event_map::FolderEvent::MoveView)
.payload(payload)
.async_send()
.await;
let views = test.get_view(&parent.id).await.child_views;
assert_eq!(views[0].name, "My 1-2 view");
assert_eq!(views[1].name, "My 1-4 view");
assert_eq!(views[2].name, "My 1-1 view");
assert_eq!(views[3].name, "My 1-5 view");
}
#[tokio::test]
async fn create_parent_view_with_invalid_name() {
for (name, code) in invalid_workspace_name_test_case() {
let sdk = EventIntegrationTest::new().await;
let request = CreateWorkspacePayloadPB {
name,
desc: "".to_owned(),
};
assert_eq!(
EventBuilder::new(sdk)
.event(flowy_folder::event_map::FolderEvent::CreateFolderWorkspace)
.payload(request)
.async_send()
.await
.error()
.unwrap()
.code,
code
)
}
}
fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {
vec![
("".to_owned(), ErrorCode::WorkspaceNameInvalid),
("1234".repeat(100), ErrorCode::WorkspaceNameTooLong),
]
}
#[tokio::test]
async fn move_view_across_parent_test() {
let test = EventIntegrationTest::new_anon().await;
let current_workspace = test.get_current_workspace().await;
let parent_1 = test
.create_view(&current_workspace.id, "My view 1".to_string())
.await;
let parent_2 = test
.create_view(&current_workspace.id, "My view 2".to_string())
.await;
for j in 1..6 {
let _ = test
.create_view(&parent_1.id, format!("My 1-{} view 1", j))
.await;
}
let views = test.get_view(&parent_1.id).await.child_views;
// Move `My 1-1 view 1` to `My view 2`
let move_view_id = views[0].id.clone();
let new_parent_id = parent_2.id.clone();
let prev_id = None;
move_folder_nested_view(test.clone(), move_view_id, new_parent_id, prev_id).await;
let parent1_views = test.get_view(&parent_1.id).await.child_views;
let parent2_views = test.get_view(&parent_2.id).await.child_views;
assert_eq!(parent2_views.len(), 1);
assert_eq!(parent2_views[0].name, "My 1-1 view 1");
assert_eq!(parent1_views[0].name, "My 1-2 view 1");
// Move My 1-2 view 1 from My view 1 to the current workspace and insert it after My view 1.
let move_view_id = parent1_views[0].id.clone();
let new_parent_id = current_workspace.id.clone();
let prev_id = Some(parent_1.id.clone());
move_folder_nested_view(test.clone(), move_view_id, new_parent_id, prev_id).await;
let parent1_views = test.get_view(&parent_1.id).await.child_views;
let workspace_views = test.get_all_workspace_views().await;
let workspace_views_len = workspace_views.len();
assert_eq!(parent1_views[0].name, "My 1-3 view 1");
assert_eq!(workspace_views[workspace_views_len - 3].name, "My view 1");
assert_eq!(
workspace_views[workspace_views_len - 2].name,
"My 1-2 view 1"
);
assert_eq!(workspace_views[workspace_views_len - 1].name, "My view 2");
}
async fn move_folder_nested_view(
sdk: EventIntegrationTest,
view_id: String,
new_parent_id: String,
prev_view_id: Option<String>,
) {
let payload = MoveNestedViewPayloadPB {
view_id,
new_parent_id,
prev_view_id,
from_section: None,
to_section: None,
};
EventBuilder::new(sdk)
.event(flowy_folder::event_map::FolderEvent::MoveNestedView)
.payload(payload)
.async_send()
.await;
}