| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 | use flowy_folder::entities::view::{RepeatedViewIdPB, ViewIdPB};use flowy_folder::entities::workspace::WorkspaceIdPB;use flowy_folder::entities::{  app::{AppIdPB, CreateAppPayloadPB, UpdateAppPayloadPB},  trash::{RepeatedTrashPB, TrashIdPB, TrashType},  view::{CreateViewPayloadPB, UpdateViewPayloadPB},  workspace::{CreateWorkspacePayloadPB, RepeatedWorkspacePB},  ViewLayoutTypePB,};use flowy_folder::entities::{  app::{AppPB, RepeatedAppPB},  trash::TrashPB,  view::{RepeatedViewPB, ViewDataFormatPB, ViewPB},  workspace::WorkspacePB,};use flowy_folder::event_map::FolderEvent::*;use flowy_folder::{errors::ErrorCode, services::folder_editor::FolderEditor};use flowy_revision::REVISION_WRITE_INTERVAL_IN_MILLIS;use flowy_revision_persistence::RevisionState;use flowy_test::{event_builder::*, FlowySDKTest};use std::{sync::Arc, time::Duration};use tokio::time::sleep;pub enum FolderScript {  // Workspace  ReadAllWorkspaces,  CreateWorkspace {    name: String,    desc: String,  },  // AssertWorkspaceRevisionJson(String),  AssertWorkspace(WorkspacePB),  ReadWorkspace(Option<String>),  // App  CreateApp {    name: String,    desc: String,  },  // AssertAppRevisionJson(String),  AssertApp(AppPB),  ReadApp(String),  UpdateApp {    name: Option<String>,    desc: Option<String>,  },  DeleteApp,  // View  CreateView {    name: String,    desc: String,    data_type: ViewDataFormatPB,  },  AssertView(ViewPB),  ReadView(String),  UpdateView {    name: Option<String>,    desc: Option<String>,  },  DeleteView,  DeleteViews(Vec<String>),  // Trash  RestoreAppFromTrash,  RestoreViewFromTrash,  ReadTrash,  DeleteAllTrash,  // Sync  #[allow(dead_code)]  AssertCurrentRevId(i64),  AssertNextSyncRevId(Option<i64>),  AssertRevisionState {    rev_id: i64,    state: RevisionState,  },}pub struct FolderTest {  pub sdk: FlowySDKTest,  pub all_workspace: Vec<WorkspacePB>,  pub workspace: WorkspacePB,  pub app: AppPB,  pub view: ViewPB,  pub trash: Vec<TrashPB>,  // pub folder_editor:}impl FolderTest {  pub async fn new() -> Self {    let sdk = FlowySDKTest::default();    let _ = sdk.init_user().await;    let mut workspace = create_workspace(&sdk, "FolderWorkspace", "Folder test workspace").await;    let mut app = create_app(&sdk, &workspace.id, "Folder App", "Folder test app").await;    let view = create_view(      &sdk,      &app.id,      "Folder View",      "Folder test view",      ViewLayoutTypePB::Document,    )    .await;    app.belongings = RepeatedViewPB {      items: vec![view.clone()],    };    workspace.apps = RepeatedAppPB {      items: vec![app.clone()],    };    Self {      sdk,      all_workspace: vec![],      workspace,      app,      view,      trash: vec![],    }  }  pub async fn run_scripts(&mut self, scripts: Vec<FolderScript>) {    for script in scripts {      self.run_script(script).await;    }  }  pub async fn run_script(&mut self, script: FolderScript) {    let sdk = &self.sdk;    let folder_editor: Arc<FolderEditor> = sdk.folder_manager.folder_editor().await;    let rev_manager = folder_editor.rev_manager();    let cache = rev_manager.revision_cache().await;    match script {      FolderScript::ReadAllWorkspaces => {        let all_workspace = read_workspace(sdk, None).await;        self.all_workspace = all_workspace;      },      FolderScript::CreateWorkspace { name, desc } => {        let workspace = create_workspace(sdk, &name, &desc).await;        self.workspace = workspace;      },      // FolderScript::AssertWorkspaceRevisionJson(expected_json) => {      //     let workspace = read_workspace(sdk, Some(self.workspace.id.clone()))      //         .await      //         .pop()      //         .unwrap();      //     let workspace_revision: WorkspaceRevision = workspace.into();      //     let json = serde_json::to_string(&workspace_revision).unwrap();      //     assert_eq!(json, expected_json);      // }      FolderScript::AssertWorkspace(workspace) => {        assert_eq!(self.workspace, workspace, "Workspace not equal");      },      FolderScript::ReadWorkspace(workspace_id) => {        let workspace = read_workspace(sdk, workspace_id).await.pop().unwrap();        self.workspace = workspace;      },      FolderScript::CreateApp { name, desc } => {        let app = create_app(sdk, &self.workspace.id, &name, &desc).await;        self.app = app;      },      // FolderScript::AssertAppRevisionJson(expected_json) => {      //     let app_revision: AppRevision = self.app.clone().into();      //     let json = serde_json::to_string(&app_revision).unwrap();      //     assert_eq!(json, expected_json);      // }      FolderScript::AssertApp(app) => {        assert_eq!(self.app, app, "App not equal");      },      FolderScript::ReadApp(app_id) => {        let app = read_app(sdk, &app_id).await;        self.app = app;      },      FolderScript::UpdateApp { name, desc } => {        update_app(sdk, &self.app.id, name, desc).await;      },      FolderScript::DeleteApp => {        delete_app(sdk, &self.app.id).await;      },      FolderScript::CreateView {        name,        desc,        data_type,      } => {        let layout = match data_type {          ViewDataFormatPB::DeltaFormat => ViewLayoutTypePB::Document,          ViewDataFormatPB::NodeFormat => ViewLayoutTypePB::Document,          ViewDataFormatPB::DatabaseFormat => ViewLayoutTypePB::Grid,        };        let view = create_view(sdk, &self.app.id, &name, &desc, layout).await;        self.view = view;      },      FolderScript::AssertView(view) => {        assert_eq!(self.view, view, "View not equal");      },      FolderScript::ReadView(view_id) => {        let view = read_view(sdk, &view_id).await;        self.view = view;      },      FolderScript::UpdateView { name, desc } => {        update_view(sdk, &self.view.id, name, desc).await;      },      FolderScript::DeleteView => {        delete_view(sdk, vec![self.view.id.clone()]).await;      },      FolderScript::DeleteViews(view_ids) => {        delete_view(sdk, view_ids).await;      },      FolderScript::RestoreAppFromTrash => {        restore_app_from_trash(sdk, &self.app.id).await;      },      FolderScript::RestoreViewFromTrash => {        restore_view_from_trash(sdk, &self.view.id).await;      },      FolderScript::ReadTrash => {        let mut trash = read_trash(sdk).await;        self.trash = trash.into_inner();      },      FolderScript::DeleteAllTrash => {        delete_all_trash(sdk).await;        self.trash = vec![];      },      FolderScript::AssertRevisionState { rev_id, state } => {        let record = cache.get(rev_id).await.unwrap();        assert_eq!(record.state, state, "Revision state is not match");        if let RevisionState::Ack = state {          // There is a defer action that writes the revisions to disk, so we wait here.          // Make sure everything is written.          sleep(Duration::from_millis(2 * REVISION_WRITE_INTERVAL_IN_MILLIS)).await;        }      },      FolderScript::AssertCurrentRevId(rev_id) => {        assert_eq!(rev_manager.rev_id(), rev_id, "Current rev_id is not match");      },      FolderScript::AssertNextSyncRevId(rev_id) => {        let next_revision = rev_manager.next_sync_revision().await.unwrap();        if rev_id.is_none() {          assert!(next_revision.is_none(), "Next revision should be None");          return;        }        let next_revision = next_revision.unwrap_or_else(|| {          panic!(            "Expected Next revision is {}, but receive None",            rev_id.unwrap()          )        });        let mut notify = rev_manager.ack_notify();        let _ = notify.recv().await;        assert_eq!(          next_revision.rev_id,          rev_id.unwrap(),          "Revision id not match"        );      },    }  }}pub fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {  vec![    ("".to_owned(), ErrorCode::WorkspaceNameInvalid),    ("1234".repeat(100), ErrorCode::WorkspaceNameTooLong),  ]}pub async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> WorkspacePB {  let request = CreateWorkspacePayloadPB {    name: name.to_owned(),    desc: desc.to_owned(),  };  FolderEventBuilder::new(sdk.clone())    .event(CreateWorkspace)    .payload(request)    .async_send()    .await    .parse::<WorkspacePB>()}pub async fn read_workspace(sdk: &FlowySDKTest, workspace_id: Option<String>) -> Vec<WorkspacePB> {  let request = WorkspaceIdPB {    value: workspace_id,  };  let mut repeated_workspace = FolderEventBuilder::new(sdk.clone())    .event(ReadWorkspaces)    .payload(request.clone())    .async_send()    .await    .parse::<RepeatedWorkspacePB>();  let workspaces;  if let Some(workspace_id) = &request.value {    workspaces = repeated_workspace      .into_inner()      .into_iter()      .filter(|workspace| &workspace.id == workspace_id)      .collect::<Vec<WorkspacePB>>();    debug_assert_eq!(workspaces.len(), 1);  } else {    workspaces = repeated_workspace.items;  }  workspaces}pub async fn create_app(sdk: &FlowySDKTest, workspace_id: &str, name: &str, desc: &str) -> AppPB {  let create_app_request = CreateAppPayloadPB {    workspace_id: workspace_id.to_owned(),    name: name.to_string(),    desc: desc.to_string(),    color_style: Default::default(),  };  FolderEventBuilder::new(sdk.clone())    .event(CreateApp)    .payload(create_app_request)    .async_send()    .await    .parse::<AppPB>()}pub async fn read_app(sdk: &FlowySDKTest, app_id: &str) -> AppPB {  let request = AppIdPB {    value: app_id.to_owned(),  };  FolderEventBuilder::new(sdk.clone())    .event(ReadApp)    .payload(request)    .async_send()    .await    .parse::<AppPB>()}pub async fn update_app(  sdk: &FlowySDKTest,  app_id: &str,  name: Option<String>,  desc: Option<String>,) {  let request = UpdateAppPayloadPB {    app_id: app_id.to_string(),    name,    desc,    color_style: None,    is_trash: None,  };  FolderEventBuilder::new(sdk.clone())    .event(UpdateApp)    .payload(request)    .async_send()    .await;}pub async fn delete_app(sdk: &FlowySDKTest, app_id: &str) {  let request = AppIdPB {    value: app_id.to_string(),  };  FolderEventBuilder::new(sdk.clone())    .event(DeleteApp)    .payload(request)    .async_send()    .await;}pub async fn create_view(  sdk: &FlowySDKTest,  app_id: &str,  name: &str,  desc: &str,  layout: ViewLayoutTypePB,) -> ViewPB {  let request = CreateViewPayloadPB {    belong_to_id: app_id.to_string(),    name: name.to_string(),    desc: desc.to_string(),    thumbnail: None,    layout,    initial_data: vec![],    ext: Default::default(),  };  FolderEventBuilder::new(sdk.clone())    .event(CreateView)    .payload(request)    .async_send()    .await    .parse::<ViewPB>()}pub async fn read_view(sdk: &FlowySDKTest, view_id: &str) -> ViewPB {  let view_id: ViewIdPB = view_id.into();  FolderEventBuilder::new(sdk.clone())    .event(ReadView)    .payload(view_id)    .async_send()    .await    .parse::<ViewPB>()}pub async fn update_view(  sdk: &FlowySDKTest,  view_id: &str,  name: Option<String>,  desc: Option<String>,) {  let request = UpdateViewPayloadPB {    view_id: view_id.to_string(),    name,    desc,    thumbnail: None,  };  FolderEventBuilder::new(sdk.clone())    .event(UpdateView)    .payload(request)    .async_send()    .await;}pub async fn delete_view(sdk: &FlowySDKTest, view_ids: Vec<String>) {  let request = RepeatedViewIdPB { items: view_ids };  FolderEventBuilder::new(sdk.clone())    .event(DeleteView)    .payload(request)    .async_send()    .await;}pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrashPB {  FolderEventBuilder::new(sdk.clone())    .event(ReadTrash)    .async_send()    .await    .parse::<RepeatedTrashPB>()}pub async fn restore_app_from_trash(sdk: &FlowySDKTest, app_id: &str) {  let id = TrashIdPB {    id: app_id.to_owned(),    ty: TrashType::TrashApp,  };  FolderEventBuilder::new(sdk.clone())    .event(PutbackTrash)    .payload(id)    .async_send()    .await;}pub async fn restore_view_from_trash(sdk: &FlowySDKTest, view_id: &str) {  let id = TrashIdPB {    id: view_id.to_owned(),    ty: TrashType::TrashView,  };  FolderEventBuilder::new(sdk.clone())    .event(PutbackTrash)    .payload(id)    .async_send()    .await;}pub async fn delete_all_trash(sdk: &FlowySDKTest) {  FolderEventBuilder::new(sdk.clone())    .event(DeleteAllTrash)    .async_send()    .await;}
 |