Jelajahi Sumber

refactor test to tokio::test to enable & enable test doc with server

appflowy 3 tahun lalu
induk
melakukan
83acc79cd1
34 mengubah file dengan 454 tambahan dan 373 penghapusan
  1. 7 13
      backend/src/application.rs
  2. 7 14
      backend/src/service/doc/doc.rs
  3. 25 15
      backend/src/service/doc/router.rs
  4. 9 33
      backend/src/service/view/view.rs
  5. 0 1
      backend/src/service/ws/router.rs
  6. 1 6
      backend/tests/document/edit.rs
  7. 43 45
      backend/tests/document/helper.rs
  8. 1 1
      backend/tests/document/mod.rs
  9. 0 0
      flowy-test/temp/flowy/flowy.log.2021-09-27
  10. 1 0
      rust-lib/dart-ffi/Cargo.toml
  11. 4 4
      rust-lib/dart-ffi/src/lib.rs
  12. 3 2
      rust-lib/flowy-dispatch/src/dispatch.rs
  13. 7 2
      rust-lib/flowy-document/src/module.rs
  14. 3 2
      rust-lib/flowy-document/src/services/server/mod.rs
  15. 18 6
      rust-lib/flowy-document/src/services/server/server_api.rs
  16. 12 19
      rust-lib/flowy-net/src/config.rs
  17. 2 3
      rust-lib/flowy-sdk/src/lib.rs
  18. 2 2
      rust-lib/flowy-sdk/src/module.rs
  19. 1 0
      rust-lib/flowy-test/Cargo.toml
  20. 14 1
      rust-lib/flowy-test/src/builder.rs
  21. 20 0
      rust-lib/flowy-test/src/helper.rs
  22. 23 24
      rust-lib/flowy-test/src/lib.rs
  23. 71 45
      rust-lib/flowy-test/src/workspace.rs
  24. 5 1
      rust-lib/flowy-user/src/errors.rs
  25. 3 2
      rust-lib/flowy-user/src/handlers/auth_handler.rs
  26. 1 2
      rust-lib/flowy-user/src/services/server/mod.rs
  27. 36 17
      rust-lib/flowy-user/tests/event/auth_test.rs
  28. 52 40
      rust-lib/flowy-user/tests/event/user_profile_test.rs
  29. 0 1
      rust-lib/flowy-workspace/src/services/app_controller.rs
  30. 20 21
      rust-lib/flowy-workspace/tests/workspace/app_test.rs
  31. 1 1
      rust-lib/flowy-workspace/tests/workspace/main.rs
  32. 23 16
      rust-lib/flowy-workspace/tests/workspace/view_test.rs
  33. 21 16
      rust-lib/flowy-workspace/tests/workspace/workspace_test.rs
  34. 18 18
      rust-lib/flowy-ws/src/ws.rs

+ 7 - 13
backend/src/application.rs

@@ -32,10 +32,7 @@ pub struct Application {
 
 impl Application {
     pub async fn build(configuration: Settings) -> Result<Self, std::io::Error> {
-        let address = format!(
-            "{}:{}",
-            configuration.application.host, configuration.application.port
-        );
+        let address = format!("{}:{}", configuration.application.host, configuration.application.port);
         let listener = TcpListener::bind(&address)?;
         let port = listener.local_addr().unwrap().port();
         let app_ctx = init_app_context(&configuration).await;
@@ -122,6 +119,7 @@ fn user_scope() -> Scope {
             .route(web::patch().to(view::update_handler))
         )
         .service(web::resource("/doc")
+            .route(web::get().to(doc::create_handler))
             .route(web::get().to(doc::read_handler))
             .route(web::patch().to(doc::update_handler))
         )
@@ -132,15 +130,11 @@ fn user_scope() -> Scope {
 }
 
 async fn init_app_context(configuration: &Settings) -> AppContext {
-    let _ = crate::service::log::Builder::new("flowy")
-        .env_filter("Trace")
-        .build();
-    let pg_pool = get_connection_pool(&configuration.database)
-        .await
-        .expect(&format!(
-            "Failed to connect to Postgres at {:?}.",
-            configuration.database
-        ));
+    let _ = crate::service::log::Builder::new("flowy").env_filter("Trace").build();
+    let pg_pool = get_connection_pool(&configuration.database).await.expect(&format!(
+        "Failed to connect to Postgres at {:?}.",
+        configuration.database
+    ));
 
     let ws_server = WsServer::new().start();
 

+ 7 - 14
backend/src/service/doc/doc.rs

@@ -9,6 +9,7 @@ use flowy_net::errors::ServerError;
 use sqlx::{postgres::PgArguments, PgPool, Postgres};
 use uuid::Uuid;
 
+#[tracing::instrument(level = "debug", skip(transaction), err)]
 pub(crate) async fn create_doc(
     transaction: &mut DBTransaction<'_>,
     params: CreateDocParams,
@@ -23,6 +24,7 @@ pub(crate) async fn create_doc(
     Ok(())
 }
 
+#[tracing::instrument(level = "debug", skip(pool), err)]
 pub(crate) async fn read_doc(pool: &PgPool, params: QueryDocParams) -> Result<Doc, ServerError> {
     let doc_id = Uuid::parse_str(&params.doc_id)?;
     let mut transaction = pool
@@ -30,9 +32,7 @@ pub(crate) async fn read_doc(pool: &PgPool, params: QueryDocParams) -> Result<Do
         .await
         .context("Failed to acquire a Postgres connection to read doc")?;
 
-    let builder = SqlBuilder::select(DOC_TABLE)
-        .add_field("*")
-        .and_where_eq("id", &doc_id);
+    let builder = SqlBuilder::select(DOC_TABLE).add_field("*").and_where_eq("id", &doc_id);
 
     let (sql, args) = builder.build()?;
     // TODO: benchmark the speed of different documents with different size
@@ -51,10 +51,7 @@ pub(crate) async fn read_doc(pool: &PgPool, params: QueryDocParams) -> Result<Do
 }
 
 #[tracing::instrument(level = "debug", skip(pool, params), err)]
-pub(crate) async fn update_doc(
-    pool: &PgPool,
-    mut params: UpdateDocParams,
-) -> Result<(), ServerError> {
+pub(crate) async fn update_doc(pool: &PgPool, mut params: UpdateDocParams) -> Result<(), ServerError> {
     let doc_id = Uuid::parse_str(&params.doc_id)?;
     let mut transaction = pool
         .begin()
@@ -82,13 +79,9 @@ pub(crate) async fn update_doc(
     Ok(())
 }
 
-pub(crate) async fn delete_doc(
-    transaction: &mut DBTransaction<'_>,
-    doc_id: Uuid,
-) -> Result<(), ServerError> {
-    let (sql, args) = SqlBuilder::delete(DOC_TABLE)
-        .and_where_eq("id", doc_id)
-        .build()?;
+#[tracing::instrument(level = "debug", skip(transaction), err)]
+pub(crate) async fn delete_doc(transaction: &mut DBTransaction<'_>, doc_id: Uuid) -> Result<(), ServerError> {
+    let (sql, args) = SqlBuilder::delete(DOC_TABLE).and_where_eq("id", doc_id).build()?;
 
     let _ = sqlx::query_with(&sql, args)
         .execute(transaction)

+ 25 - 15
backend/src/service/doc/router.rs

@@ -1,32 +1,42 @@
+use crate::service::{
+    doc::{create_doc, read_doc, update_doc},
+    util::parse_from_payload,
+};
 use actix_web::{
     web::{Data, Payload},
     HttpResponse,
 };
+use anyhow::Context;
+use flowy_document::protobuf::{CreateDocParams, QueryDocParams, UpdateDocParams};
+use flowy_net::{errors::ServerError, response::FlowyResponse};
 use sqlx::PgPool;
 
-use flowy_document::protobuf::{QueryDocParams, UpdateDocParams};
-use flowy_net::errors::ServerError;
+pub async fn create_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
+    let params: CreateDocParams = parse_from_payload(payload).await?;
 
-use crate::service::{
-    doc::{read_doc, update_doc},
-    util::parse_from_payload,
-};
-use flowy_net::response::FlowyResponse;
+    let mut transaction = pool
+        .begin()
+        .await
+        .context("Failed to acquire a Postgres connection to create doc")?;
+
+    let _ = create_doc(&mut transaction, params).await?;
+
+    transaction
+        .commit()
+        .await
+        .context("Failed to commit SQL transaction to create doc.")?;
+
+    Ok(FlowyResponse::success().into())
+}
 
-pub async fn read_handler(
-    payload: Payload,
-    pool: Data<PgPool>,
-) -> Result<HttpResponse, ServerError> {
+pub async fn read_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
     let params: QueryDocParams = parse_from_payload(payload).await?;
     let doc = read_doc(pool.get_ref(), params).await?;
     let response = FlowyResponse::success().pb(doc)?;
     Ok(response.into())
 }
 
-pub async fn update_handler(
-    payload: Payload,
-    pool: Data<PgPool>,
-) -> Result<HttpResponse, ServerError> {
+pub async fn update_handler(payload: Payload, pool: Data<PgPool>) -> Result<HttpResponse, ServerError> {
     let params: UpdateDocParams = parse_from_payload(payload).await?;
     let _ = update_doc(pool.get_ref(), params).await?;
     Ok(FlowyResponse::success().into())

+ 9 - 33
backend/src/service/view/view.rs

@@ -24,10 +24,7 @@ use crate::{
     sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
 };
 
-pub(crate) async fn create_view(
-    pool: &PgPool,
-    params: CreateViewParams,
-) -> Result<FlowyResponse, ServerError> {
+pub(crate) async fn create_view(pool: &PgPool, params: CreateViewParams) -> Result<FlowyResponse, ServerError> {
     let mut transaction = pool
         .begin()
         .await
@@ -41,6 +38,7 @@ pub(crate) async fn create_view(
     FlowyResponse::success().pb(view)
 }
 
+#[tracing::instrument(name = "create_view", level = "debug", skip(transaction), err)]
 pub(crate) async fn create_view_with_transaction(
     transaction: &mut DBTransaction<'_>,
     params: CreateViewParams,
@@ -69,10 +67,7 @@ pub(crate) async fn create_view_with_transaction(
     Ok(view)
 }
 
-pub(crate) async fn read_view(
-    pool: &PgPool,
-    params: QueryViewParams,
-) -> Result<FlowyResponse, ServerError> {
+pub(crate) async fn read_view(pool: &PgPool, params: QueryViewParams) -> Result<FlowyResponse, ServerError> {
     let view_id = check_view_id(params.view_id)?;
     let mut transaction = pool
         .begin()
@@ -109,28 +104,17 @@ pub(crate) async fn read_view(
     FlowyResponse::success().pb(view)
 }
 
-pub(crate) async fn update_view(
-    pool: &PgPool,
-    params: UpdateViewParams,
-) -> Result<FlowyResponse, ServerError> {
+pub(crate) async fn update_view(pool: &PgPool, params: UpdateViewParams) -> Result<FlowyResponse, ServerError> {
     let view_id = check_view_id(params.view_id.clone())?;
 
     let name = match params.has_name() {
         false => None,
-        true => Some(
-            ViewName::parse(params.get_name().to_owned())
-                .map_err(invalid_params)?
-                .0,
-        ),
+        true => Some(ViewName::parse(params.get_name().to_owned()).map_err(invalid_params)?.0),
     };
 
     let desc = match params.has_desc() {
         false => None,
-        true => Some(
-            ViewDesc::parse(params.get_desc().to_owned())
-                .map_err(invalid_params)?
-                .0,
-        ),
+        true => Some(ViewDesc::parse(params.get_desc().to_owned()).map_err(invalid_params)?.0),
     };
 
     let thumbnail = match params.has_thumbnail() {
@@ -169,19 +153,14 @@ pub(crate) async fn update_view(
     Ok(FlowyResponse::success())
 }
 
-pub(crate) async fn delete_view(
-    pool: &PgPool,
-    view_id: &str,
-) -> Result<FlowyResponse, ServerError> {
+pub(crate) async fn delete_view(pool: &PgPool, view_id: &str) -> Result<FlowyResponse, ServerError> {
     let view_id = check_view_id(view_id.to_owned())?;
     let mut transaction = pool
         .begin()
         .await
         .context("Failed to acquire a Postgres connection to delete view")?;
 
-    let (sql, args) = SqlBuilder::delete(VIEW_TABLE)
-        .and_where_eq("id", &view_id)
-        .build()?;
+    let (sql, args) = SqlBuilder::delete(VIEW_TABLE).and_where_eq("id", &view_id).build()?;
 
     let _ = sqlx::query_with(&sql, args)
         .execute(&mut transaction)
@@ -215,10 +194,7 @@ pub(crate) async fn read_views_belong_to_id<'c>(
         .await
         .map_err(map_sqlx_error)?;
 
-    let views = tables
-        .into_iter()
-        .map(|table| table.into())
-        .collect::<Vec<View>>();
+    let views = tables.into_iter().map(|table| table.into()).collect::<Vec<View>>();
 
     Ok(views)
 }

+ 0 - 1
backend/src/service/ws/router.rs

@@ -19,7 +19,6 @@ pub async fn establish_ws_connection(
     server: Data<Addr<WsServer>>,
     biz_handlers: Data<WsBizHandlers>,
 ) -> Result<HttpResponse, Error> {
-    log::debug!("establish_ws_connection");
     match LoggedUser::from_token(token.clone()) {
         Ok(user) => {
             let ws_user = WsUser::new(user.clone());

+ 1 - 6
backend/tests/document/ws.rs → backend/tests/document/edit.rs

@@ -1,12 +1,7 @@
 use crate::document::helper::{DocScript, DocumentTest};
-use tokio::time::{interval, Duration};
 
 #[actix_rt::test]
-async fn ws_connect() {
+async fn edit_doc_insert_text() {
     let test = DocumentTest::new().await;
     test.run_scripts(vec![DocScript::SendText("abc")]).await;
-
-    let mut interval = interval(Duration::from_secs(10));
-    interval.tick().await;
-    interval.tick().await;
 }

+ 43 - 45
backend/tests/document/helper.rs

@@ -1,20 +1,23 @@
-use crate::helper::*;
+// use crate::helper::*;
+use crate::helper::{spawn_server, TestServer};
 use flowy_document::{
-    entities::doc::{CreateDocParams, DocDelta, QueryDocParams},
+    entities::doc::{DocDelta, QueryDocParams},
     module::FlowyDocument,
     services::doc::edit_doc_context::EditDocContext,
 };
-use flowy_infra::uuid;
+
+use flowy_net::config::ServerConfig;
 use flowy_ot::core::Delta;
-use flowy_sdk::{FlowySDK, FlowySDKConfig};
-use flowy_test::{prelude::root_dir, FlowyTestSDK};
-use flowy_user::{entities::SignUpParams, services::user::UserSession};
-use flowy_workspace::prelude::DOC_DEFAULT_DATA;
+
+use flowy_test::{workspace::ViewTest, FlowyTest, FlowyTestSDK};
+use flowy_user::services::user::UserSession;
+
 use std::{str::FromStr, sync::Arc};
+use tokio::time::{interval, Duration};
 
 pub struct DocumentTest {
     server: TestServer,
-    sdk: FlowyTestSDK,
+    flowy_test: FlowyTest,
     flowy_document: Arc<FlowyDocument>,
     user_session: Arc<UserSession>,
     edit_context: Arc<EditDocContext>,
@@ -26,50 +29,20 @@ pub enum DocScript {
     SendBinary(Vec<u8>),
 }
 
-async fn create_doc(user_session: Arc<UserSession>, flowy_document: Arc<FlowyDocument>) -> Arc<EditDocContext> {
-    let conn = user_session.db_pool().unwrap().get().unwrap();
-    let doc_id = uuid();
-    let params = CreateDocParams {
-        id: doc_id.clone(),
-        data: DOC_DEFAULT_DATA.to_string(),
-    };
-    let _ = flowy_document.create(params, &*conn).unwrap();
-
-    let edit_context = flowy_document
-        .open(QueryDocParams { doc_id }, user_session.db_pool().unwrap())
-        .await
-        .unwrap();
-
-    edit_context
-}
-
-async fn init_user(user_session: Arc<UserSession>) {
-    let params = SignUpParams {
-        email: format!("{}@gmail.com", uuid()),
-        name: "nathan".to_string(),
-        password: "HelloWorld!@12".to_string(),
-    };
-
-    user_session.sign_up(params).await.unwrap();
-    user_session.init_user().await.unwrap();
-}
-
 impl DocumentTest {
     pub async fn new() -> Self {
         let server = spawn_server().await;
-        let config = FlowySDKConfig::new(&root_dir(), &server.host, "http", "ws").log_filter("debug");
-        let sdk = FlowySDK::new(config);
+        let server_config = ServerConfig::new(&server.host, "http", "ws");
+        let flowy_test = FlowyTest::setup_with(server_config);
 
-        let flowy_document = sdk.flowy_document.clone();
-        let user_session = sdk.user_session.clone();
-
-        init_user(user_session.clone()).await;
-
-        let edit_context = create_doc(user_session.clone(), flowy_document.clone()).await;
+        init_user(&flowy_test).await;
 
+        let edit_context = create_doc(&flowy_test).await;
+        let user_session = flowy_test.sdk.user_session.clone();
+        let flowy_document = flowy_test.sdk.flowy_document.clone();
         Self {
             server,
-            sdk,
+            flowy_test,
             flowy_document,
             user_session,
             edit_context,
@@ -93,5 +66,30 @@ impl DocumentTest {
             }
         }
         std::mem::forget(self);
+
+        let mut interval = interval(Duration::from_secs(5));
+        interval.tick().await;
+        interval.tick().await;
     }
 }
+
+async fn create_doc(flowy_test: &FlowyTest) -> Arc<EditDocContext> {
+    let view_test = ViewTest::new(flowy_test).await;
+    let doc_id = view_test.view.id.clone();
+    let user_session = flowy_test.sdk.user_session.clone();
+    let flowy_document = flowy_test.sdk.flowy_document.clone();
+
+    let edit_context = flowy_document
+        .open(QueryDocParams { doc_id }, user_session.db_pool().unwrap())
+        .await
+        .unwrap();
+
+    edit_context
+}
+
+async fn init_user(flowy_test: &FlowyTest) {
+    let _ = flowy_test.sign_up().await;
+
+    let user_session = flowy_test.sdk.user_session.clone();
+    user_session.init_user().await.unwrap();
+}

+ 1 - 1
backend/tests/document/mod.rs

@@ -1,2 +1,2 @@
+mod edit;
 mod helper;
-mod ws;

+ 0 - 0
flowy-test/temp/flowy/flowy.log.2021-09-27


+ 1 - 0
rust-lib/dart-ffi/Cargo.toml

@@ -31,6 +31,7 @@ flowy-dispatch = {path = "../flowy-dispatch"}
 flowy-sdk = {path = "../flowy-sdk"}
 flowy-derive = {path = "../flowy-derive"}
 flowy-observable = {path = "../flowy-observable"}
+flowy-net = {path = "../flowy-net"}
 
 
 [features]

+ 4 - 4
rust-lib/dart-ffi/src/lib.rs

@@ -24,10 +24,8 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
     let c_str: &CStr = unsafe { CStr::from_ptr(path) };
     let path: &str = c_str.to_str().unwrap();
 
-    let host = "localhost";
-    let http_schema = "http";
-    let ws_schema = "ws";
-    let config = FlowySDKConfig::new(path, host, http_schema, ws_schema).log_filter("debug");
+    let server_config = ServerConfig::default();
+    let config = FlowySDKConfig::new(path, server_config).log_filter("debug");
     *FLOWY_SDK.write() = Some(Arc::new(FlowySDK::new(config)));
 
     return 1;
@@ -72,6 +70,8 @@ pub extern "C" fn set_stream_port(port: i64) -> i32 {
 pub extern "C" fn link_me_please() {}
 
 use flowy_dispatch::prelude::ToBytes;
+use flowy_net::config::ServerConfig;
+
 #[inline(always)]
 async fn post_to_flutter(response: EventResponse, port: i64) {
     let isolate = allo_isolate::Isolate::new(port);

+ 3 - 2
rust-lib/flowy-dispatch/src/dispatch.rs

@@ -8,7 +8,6 @@ use crate::{
 use derivative::*;
 use futures_core::future::BoxFuture;
 use futures_util::task::Context;
-
 use pin_project::pin_project;
 use std::{future::Future, sync::Arc};
 use tokio::macros::support::{Pin, Poll};
@@ -73,7 +72,9 @@ impl EventDispatch {
     }
 
     pub fn sync_send(dispatch: Arc<EventDispatch>, request: ModuleRequest) -> EventResponse {
-        futures::executor::block_on(async { EventDispatch::async_send_with_callback(dispatch, request, |_| Box::pin(async {})).await })
+        futures::executor::block_on(async {
+            EventDispatch::async_send_with_callback(dispatch, request, |_| Box::pin(async {})).await
+        })
     }
 
     pub fn spawn<F>(&self, f: F)

+ 7 - 2
rust-lib/flowy-document/src/module.rs

@@ -14,6 +14,7 @@ use crate::{
         ws::WsDocumentManager,
     },
 };
+use flowy_net::config::ServerConfig;
 
 pub trait DocumentUser: Send + Sync {
     fn user_dir(&self) -> Result<String, DocError>;
@@ -26,8 +27,12 @@ pub struct FlowyDocument {
 }
 
 impl FlowyDocument {
-    pub fn new(user: Arc<dyn DocumentUser>, ws_manager: Arc<RwLock<WsDocumentManager>>) -> FlowyDocument {
-        let server = construct_doc_server();
+    pub fn new(
+        user: Arc<dyn DocumentUser>,
+        ws_manager: Arc<RwLock<WsDocumentManager>>,
+        server_config: &ServerConfig,
+    ) -> FlowyDocument {
+        let server = construct_doc_server(server_config);
         let controller = Arc::new(DocController::new(server.clone(), user.clone(), ws_manager.clone()));
         Self { doc_ctrl: controller }
     }

+ 3 - 2
rust-lib/flowy-document/src/services/server/mod.rs

@@ -9,6 +9,7 @@ use crate::{
     errors::DocError,
 };
 use flowy_infra::future::ResultFuture;
+use flowy_net::config::ServerConfig;
 pub use server_api_mock::*;
 use std::sync::Arc;
 
@@ -23,9 +24,9 @@ pub trait DocumentServerAPI {
     fn delete_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<(), DocError>;
 }
 
-pub(crate) fn construct_doc_server() -> Arc<dyn DocumentServerAPI + Send + Sync> {
+pub(crate) fn construct_doc_server(server_config: &ServerConfig) -> Arc<dyn DocumentServerAPI + Send + Sync> {
     if cfg!(feature = "http_server") {
-        Arc::new(DocServer {})
+        Arc::new(DocServer::new(server_config.clone()))
     } else {
         Arc::new(DocServerMock {})
     }

+ 18 - 6
rust-lib/flowy-document/src/services/server/server_api.rs

@@ -6,31 +6,43 @@ use crate::{
 use flowy_infra::future::ResultFuture;
 use flowy_net::{config::*, request::HttpRequestBuilder};
 
-pub struct DocServer {}
+pub struct DocServer {
+    config: ServerConfig,
+}
+
+impl DocServer {
+    pub fn new(config: ServerConfig) -> Self { Self { config } }
+}
 
 impl DocumentServerAPI for DocServer {
     fn create_doc(&self, token: &str, params: CreateDocParams) -> ResultFuture<(), DocError> {
         let token = token.to_owned();
-        ResultFuture::new(async move { create_doc_request(&token, params, DOC_URL.as_ref()).await })
+        let url = self.config.doc_url();
+        ResultFuture::new(async move { create_doc_request(&token, params, &url).await })
     }
 
     fn read_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<Option<Doc>, DocError> {
         let token = token.to_owned();
-        ResultFuture::new(async move { read_doc_request(&token, params, DOC_URL.as_ref()).await })
+        let url = self.config.doc_url();
+        ResultFuture::new(async move { read_doc_request(&token, params, &url).await })
     }
 
     fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError> {
         let token = token.to_owned();
-        ResultFuture::new(async move { update_doc_request(&token, params, DOC_URL.as_ref()).await })
+        let url = self.config.doc_url();
+        ResultFuture::new(async move { update_doc_request(&token, params, &url).await })
     }
 
     fn delete_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<(), DocError> {
         let token = token.to_owned();
-        ResultFuture::new(async move { delete_doc_request(&token, params, DOC_URL.as_ref()).await })
+        let url = self.config.doc_url();
+        ResultFuture::new(async move { delete_doc_request(&token, params, &url).await })
     }
 }
 
-pub(crate) fn request_builder() -> HttpRequestBuilder { HttpRequestBuilder::new().middleware(super::middleware::MIDDLEWARE.clone()) }
+pub(crate) fn request_builder() -> HttpRequestBuilder {
+    HttpRequestBuilder::new().middleware(super::middleware::MIDDLEWARE.clone())
+}
 
 pub async fn create_doc_request(token: &str, params: CreateDocParams, url: &str) -> Result<(), DocError> {
     let _ = request_builder()

+ 12 - 19
rust-lib/flowy-net/src/config.rs

@@ -1,7 +1,6 @@
-use lazy_static::lazy_static;
-
 pub const HOST: &'static str = "localhost:8000";
-pub const SCHEMA: &'static str = "http://";
+pub const HTTP_SCHEMA: &'static str = "http";
+pub const WS_SCHEMA: &'static str = "ws";
 pub const HEADER_TOKEN: &'static str = "token";
 
 #[derive(Debug, Clone)]
@@ -11,6 +10,16 @@ pub struct ServerConfig {
     ws_schema: String,
 }
 
+impl std::default::Default for ServerConfig {
+    fn default() -> Self {
+        ServerConfig {
+            http_schema: HTTP_SCHEMA.to_string(),
+            host: HOST.to_string(),
+            ws_schema: WS_SCHEMA.to_string(),
+        }
+    }
+}
+
 impl ServerConfig {
     pub fn new(host: &str, http_schema: &str, ws_schema: &str) -> Self {
         Self {
@@ -40,19 +49,3 @@ impl ServerConfig {
 
     pub fn ws_addr(&self) -> String { format!("{}://{}/ws", self.ws_schema, self.host) }
 }
-
-lazy_static! {
-    pub static ref SIGN_UP_URL: String = format!("{}/{}/api/register", SCHEMA, HOST);
-    pub static ref SIGN_IN_URL: String = format!("{}/{}/api/auth", SCHEMA, HOST);
-    pub static ref SIGN_OUT_URL: String = format!("{}/{}/api/auth", SCHEMA, HOST);
-    pub static ref USER_PROFILE_URL: String = format!("{}/{}/api/user", SCHEMA, HOST);
-
-    //
-    pub static ref WORKSPACE_URL: String = format!("{}/{}/api/workspace", SCHEMA, HOST);
-    pub static ref APP_URL: String = format!("{}/{}/api/app", SCHEMA, HOST);
-    pub static ref VIEW_URL: String = format!("{}/{}/api/view", SCHEMA, HOST);
-    pub static ref DOC_URL: String = format!("{}/{}/api/doc", SCHEMA, HOST);
-
-    //
-    pub static ref WS_ADDR: String = format!("ws://{}/ws", HOST);
-}

+ 2 - 3
rust-lib/flowy-sdk/src/lib.rs

@@ -23,8 +23,7 @@ pub struct FlowySDKConfig {
 }
 
 impl FlowySDKConfig {
-    pub fn new(root: &str, host: &str, http_schema: &str, ws_schema: &str) -> Self {
-        let server_config = ServerConfig::new(host, http_schema, ws_schema);
+    pub fn new(root: &str, server_config: ServerConfig) -> Self {
         FlowySDKConfig {
             root: root.to_owned(),
             log_filter: crate_log_filter(None),
@@ -71,7 +70,7 @@ impl FlowySDK {
                 .root_dir(&config.root, &config.server_config)
                 .build(),
         );
-        let flowy_document = build_document_module(user_session.clone());
+        let flowy_document = build_document_module(user_session.clone(), &config.server_config);
         let modules = build_modules(&config.server_config, user_session.clone(), flowy_document.clone());
         let dispatch = Arc::new(EventDispatch::construct(|| modules));
 

+ 2 - 2
rust-lib/flowy-sdk/src/module.rs

@@ -28,9 +28,9 @@ fn build_workspace_module(
     flowy_workspace::module::create(user, database, flowy_document, server_config)
 }
 
-pub fn build_document_module(user_session: Arc<UserSession>) -> Arc<FlowyDocument> {
+pub fn build_document_module(user_session: Arc<UserSession>, server_config: &ServerConfig) -> Arc<FlowyDocument> {
     let document_deps = DocumentDepsResolver::new(user_session.clone());
     let (user, ws_manager) = document_deps.split_into();
-    let document = Arc::new(FlowyDocument::new(user, ws_manager));
+    let document = Arc::new(FlowyDocument::new(user, ws_manager, server_config));
     document
 }

+ 1 - 0
rust-lib/flowy-test/Cargo.toml

@@ -12,6 +12,7 @@ flowy-user = { path = "../flowy-user"}
 flowy-workspace = { path = "../flowy-workspace"}
 flowy-infra = { path = "../flowy-infra"}
 flowy-document = { path = "../flowy-document"}
+flowy-net = { path = "../flowy-net"}
 
 serde = { version = "1.0", features = ["derive"] }
 bincode = { version = "1.3"}

+ 14 - 1
rust-lib/flowy-test/src/builder.rs

@@ -74,6 +74,13 @@ where
         self
     }
 
+    pub async fn async_send(mut self) -> Self {
+        let request = self.get_request();
+        let resp = EventDispatch::async_send(self.dispatch(), request).await;
+        self.context.response = Some(resp);
+        self
+    }
+
     pub fn parse<R>(self) -> R
     where
         R: FromBytes,
@@ -108,7 +115,13 @@ where
 
     fn dispatch(&self) -> Arc<EventDispatch> { self.context.sdk.dispatch() }
 
-    fn get_response(&self) -> EventResponse { self.context.response.as_ref().expect("must call sync_send first").clone() }
+    fn get_response(&self) -> EventResponse {
+        self.context
+            .response
+            .as_ref()
+            .expect("must call sync_send first")
+            .clone()
+    }
 
     fn get_request(&mut self) -> ModuleRequest { self.context.request.take().expect("must call event first") }
 }

+ 20 - 0
rust-lib/flowy-test/src/helper.rs

@@ -97,6 +97,26 @@ pub fn sign_up(dispatch: Arc<EventDispatch>) -> SignUpContext {
         .unwrap()
         .unwrap();
 
+    SignUpContext { user_profile, password }
+}
+
+pub async fn async_sign_up(dispatch: Arc<EventDispatch>) -> SignUpContext {
+    let password = login_password();
+    let payload = SignUpRequest {
+        email: random_email(),
+        name: "app flowy".to_string(),
+        password: password.clone(),
+    }
+    .into_bytes()
+    .unwrap();
+
+    let request = ModuleRequest::new(SignUp).payload(payload);
+    let user_profile = EventDispatch::async_send(dispatch.clone(), request)
+        .await
+        .parse::<UserProfile, UserError>()
+        .unwrap()
+        .unwrap();
+
     // let _ = create_default_workspace_if_need(dispatch.clone(), &user_profile.id);
     SignUpContext { user_profile, password }
 }

+ 23 - 24
rust-lib/flowy-test/src/lib.rs

@@ -1,7 +1,9 @@
 pub mod builder;
 mod helper;
+pub mod workspace;
 
 use crate::helper::*;
+use flowy_net::config::ServerConfig;
 use flowy_sdk::{FlowySDK, FlowySDKConfig};
 use flowy_user::entities::UserProfile;
 
@@ -13,37 +15,34 @@ pub mod prelude {
 pub type FlowyTestSDK = FlowySDK;
 
 #[derive(Clone)]
-pub struct FlowyEnv {
+pub struct FlowyTest {
     pub sdk: FlowyTestSDK,
-    pub user: UserProfile,
-    pub password: String,
 }
 
-impl FlowyEnv {
+impl FlowyTest {
     pub fn setup() -> Self {
-        let host = "localhost";
-        let http_schema = "http";
-        let ws_schema = "ws";
+        let server_config = ServerConfig::default();
+        let test = Self::setup_with(server_config);
+        std::mem::forget(test.sdk.dispatch());
+        test
+    }
 
-        let config = FlowySDKConfig::new(&root_dir(), host, http_schema, ws_schema).log_filter("debug");
-        let sdk = FlowySDK::new(config);
-        let result = sign_up(sdk.dispatch());
-        let env = Self {
-            sdk,
-            user: result.user_profile,
-            password: result.password,
-        };
-        env
+    pub async fn sign_up(&self) -> SignUpContext {
+        let context = async_sign_up(self.sdk.dispatch()).await;
+        context
     }
 
-    pub fn sdk(&self) -> FlowyTestSDK { self.sdk.clone() }
-}
+    pub async fn init_user(&self) -> UserProfile {
+        let context = async_sign_up(self.sdk.dispatch()).await;
+        context.user_profile
+    }
 
-pub fn init_test_sdk() -> FlowyTestSDK {
-    let host = "localhost";
-    let http_schema = "http";
-    let ws_schema = "ws";
+    pub fn setup_with(server_config: ServerConfig) -> Self {
+        let config = FlowySDKConfig::new(&root_dir(), server_config).log_filter("debug");
+        let sdk = FlowySDK::new(config);
+
+        Self { sdk }
+    }
 
-    let config = FlowySDKConfig::new(&root_dir(), host, http_schema, ws_schema).log_filter("debug");
-    FlowySDK::new(config)
+    pub fn sdk(&self) -> FlowyTestSDK { self.sdk.clone() }
 }

+ 71 - 45
rust-lib/flowy-workspace/tests/workspace/helper.rs → rust-lib/flowy-test/src/workspace.rs

@@ -1,5 +1,5 @@
+use crate::prelude::*;
 use flowy_document::entities::doc::Doc;
-use flowy_test::prelude::*;
 use flowy_workspace::{
     entities::{app::*, view::*, workspace::*},
     event::WorkspaceEvent::*,
@@ -11,12 +11,16 @@ pub struct WorkspaceTest {
 }
 
 impl WorkspaceTest {
-    pub fn new() -> Self {
-        let sdk = FlowyEnv::setup().sdk;
-        let workspace = create_workspace(&sdk, "Workspace", "");
-        open_workspace(&sdk, &workspace.id);
-
-        Self { sdk, workspace }
+    pub async fn new() -> Self {
+        let test = FlowyTest::setup();
+        let _ = test.init_user().await;
+        let workspace = create_workspace(&test.sdk, "Workspace", "").await;
+        open_workspace(&test.sdk, &workspace.id).await;
+
+        Self {
+            sdk: test.sdk,
+            workspace,
+        }
     }
 }
 
@@ -27,15 +31,20 @@ pub struct AppTest {
 }
 
 impl AppTest {
-    pub fn new() -> Self {
-        let sdk = FlowyEnv::setup().sdk;
-        let workspace = create_workspace(&sdk, "Workspace", "");
-        open_workspace(&sdk, &workspace.id);
-        let app = create_app(&sdk, "App", "AppFlowy Github Project", &workspace.id);
-        Self { sdk, workspace, app }
+    pub async fn new() -> Self {
+        let test = FlowyTest::setup();
+        let _ = test.init_user().await;
+        let workspace = create_workspace(&test.sdk, "Workspace", "").await;
+        open_workspace(&test.sdk, &workspace.id).await;
+        let app = create_app(&test.sdk, "App", "AppFlowy Github Project", &workspace.id).await;
+        Self {
+            sdk: test.sdk,
+            workspace,
+            app,
+        }
     }
 
-    pub fn move_app_to_trash(&self) {
+    pub async fn move_app_to_trash(&self) {
         let request = UpdateAppRequest {
             app_id: self.app.id.clone(),
             name: None,
@@ -43,11 +52,11 @@ impl AppTest {
             color_style: None,
             is_trash: Some(true),
         };
-        update_app(&self.sdk, request);
+        update_app(&self.sdk, request).await;
     }
 }
 
-pub(crate) struct ViewTest {
+pub struct ViewTest {
     pub sdk: FlowyTestSDK,
     pub workspace: Workspace,
     pub app: App,
@@ -55,16 +64,20 @@ pub(crate) struct ViewTest {
 }
 
 impl ViewTest {
-    pub fn new() -> Self {
-        let sdk = FlowyEnv::setup().sdk;
-        let workspace = create_workspace(&sdk, "Workspace", "");
-        open_workspace(&sdk, &workspace.id);
-        let app = create_app(&sdk, "App", "AppFlowy Github Project", &workspace.id);
-        let view = create_view(&sdk, &app.id);
-        Self { sdk, workspace, app, view }
+    pub async fn new(test: &FlowyTest) -> Self {
+        let workspace = create_workspace(&test.sdk, "Workspace", "").await;
+        open_workspace(&test.sdk, &workspace.id).await;
+        let app = create_app(&test.sdk, "App", "AppFlowy Github Project", &workspace.id).await;
+        let view = create_view(&test.sdk, &app.id).await;
+        Self {
+            sdk: test.sdk.clone(),
+            workspace,
+            app,
+            view,
+        }
     }
 
-    pub fn move_view_to_trash(&self) {
+    pub async fn move_view_to_trash(&self) {
         let request = UpdateViewRequest {
             view_id: self.view.id.clone(),
             name: None,
@@ -72,18 +85,18 @@ impl ViewTest {
             thumbnail: None,
             is_trash: Some(true),
         };
-        update_view(&self.sdk, request);
+        update_view(&self.sdk, request).await;
     }
 }
 
-pub(crate) fn invalid_workspace_name_test_case() -> Vec<String> {
+pub fn invalid_workspace_name_test_case() -> Vec<String> {
     vec!["", "1234".repeat(100).as_str()]
         .iter()
         .map(|s| s.to_string())
         .collect::<Vec<_>>()
 }
 
-pub fn create_workspace(sdk: &FlowyTestSDK, name: &str, desc: &str) -> Workspace {
+pub async fn create_workspace(sdk: &FlowyTestSDK, name: &str, desc: &str) -> Workspace {
     let request = CreateWorkspaceRequest {
         name: name.to_owned(),
         desc: desc.to_owned(),
@@ -92,19 +105,21 @@ pub fn create_workspace(sdk: &FlowyTestSDK, name: &str, desc: &str) -> Workspace
     let workspace = FlowyWorkspaceTest::new(sdk.clone())
         .event(CreateWorkspace)
         .request(request)
-        .sync_send()
+        .async_send()
+        .await
         .parse::<Workspace>();
     workspace
 }
 
-fn open_workspace(sdk: &FlowyTestSDK, workspace_id: &str) {
+async fn open_workspace(sdk: &FlowyTestSDK, workspace_id: &str) {
     let request = QueryWorkspaceRequest {
         workspace_id: Some(workspace_id.to_owned()),
     };
     let _ = FlowyWorkspaceTest::new(sdk.clone())
         .event(OpenWorkspace)
         .request(request)
-        .sync_send();
+        .async_send()
+        .await;
 }
 
 pub fn read_workspace(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest) -> Option<Workspace> {
@@ -129,7 +144,7 @@ pub fn read_workspace(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest) -> Opt
     workspaces.drain(..1).collect::<Vec<Workspace>>().pop()
 }
 
-pub fn create_app(sdk: &FlowyTestSDK, name: &str, desc: &str, workspace_id: &str) -> App {
+pub async fn create_app(sdk: &FlowyTestSDK, name: &str, desc: &str, workspace_id: &str) -> App {
     let create_app_request = CreateAppRequest {
         workspace_id: workspace_id.to_owned(),
         name: name.to_string(),
@@ -140,7 +155,8 @@ pub fn create_app(sdk: &FlowyTestSDK, name: &str, desc: &str, workspace_id: &str
     let app = FlowyWorkspaceTest::new(sdk.clone())
         .event(CreateApp)
         .request(create_app_request)
-        .sync_send()
+        .async_send()
+        .await
         .parse::<App>();
     app
 }
@@ -156,8 +172,12 @@ pub fn delete_app(sdk: &FlowyTestSDK, app_id: &str) {
         .sync_send();
 }
 
-pub fn update_app(sdk: &FlowyTestSDK, request: UpdateAppRequest) {
-    FlowyWorkspaceTest::new(sdk.clone()).event(UpdateApp).request(request).sync_send();
+pub async fn update_app(sdk: &FlowyTestSDK, request: UpdateAppRequest) {
+    FlowyWorkspaceTest::new(sdk.clone())
+        .event(UpdateApp)
+        .request(request)
+        .async_send()
+        .await;
 }
 
 pub fn read_app(sdk: &FlowyTestSDK, request: QueryAppRequest) -> App {
@@ -170,17 +190,17 @@ pub fn read_app(sdk: &FlowyTestSDK, request: QueryAppRequest) -> App {
     app
 }
 
-pub fn create_view_with_request(sdk: &FlowyTestSDK, request: CreateViewRequest) -> View {
+pub async fn create_view_with_request(sdk: &FlowyTestSDK, request: CreateViewRequest) -> View {
     let view = FlowyWorkspaceTest::new(sdk.clone())
         .event(CreateView)
         .request(request)
-        .sync_send()
+        .async_send()
+        .await
         .parse::<View>();
-
     view
 }
 
-pub fn create_view(sdk: &FlowyTestSDK, app_id: &str) -> View {
+pub async fn create_view(sdk: &FlowyTestSDK, app_id: &str) -> View {
     let request = CreateViewRequest {
         belong_to_id: app_id.to_string(),
         name: "View A".to_string(),
@@ -189,25 +209,31 @@ pub fn create_view(sdk: &FlowyTestSDK, app_id: &str) -> View {
         view_type: ViewType::Doc,
     };
 
-    create_view_with_request(sdk, request)
+    create_view_with_request(sdk, request).await
 }
 
-pub fn update_view(sdk: &FlowyTestSDK, request: UpdateViewRequest) {
-    FlowyWorkspaceTest::new(sdk.clone()).event(UpdateView).request(request).sync_send();
+pub async fn update_view(sdk: &FlowyTestSDK, request: UpdateViewRequest) {
+    FlowyWorkspaceTest::new(sdk.clone())
+        .event(UpdateView)
+        .request(request)
+        .async_send()
+        .await;
 }
 
-pub fn read_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> View {
+pub async fn read_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> View {
     FlowyWorkspaceTest::new(sdk.clone())
         .event(ReadView)
         .request(request)
-        .sync_send()
+        .async_send()
+        .await
         .parse::<View>()
 }
 
-pub fn open_view(sdk: &FlowyTestSDK, request: OpenViewRequest) -> Doc {
+pub async fn open_view(sdk: &FlowyTestSDK, request: OpenViewRequest) -> Doc {
     FlowyWorkspaceTest::new(sdk.clone())
         .event(OpenView)
         .request(request)
-        .sync_send()
+        .async_send()
+        .await
         .parse::<Doc>()
 }

+ 5 - 1
rust-lib/flowy-user/src/errors.rs

@@ -2,7 +2,7 @@ use bytes::Bytes;
 use derive_more::Display;
 use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
 use flowy_dispatch::prelude::{EventResponse, ResponseBuilder};
-use std::{convert::TryInto, fmt::Debug};
+use std::{convert::TryInto, fmt, fmt::Debug};
 
 #[derive(Debug, Default, Clone, ProtoBuf)]
 pub struct UserError {
@@ -13,6 +13,10 @@ pub struct UserError {
     pub msg: String,
 }
 
+impl std::fmt::Display for UserError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}: {}", &self.code, &self.msg) }
+}
+
 macro_rules! static_user_error {
     ($name:ident, $status:expr) => {
         #[allow(non_snake_case, missing_docs)]

+ 3 - 2
rust-lib/flowy-user/src/handlers/auth_handler.rs

@@ -3,7 +3,7 @@ use flowy_dispatch::prelude::*;
 use std::{convert::TryInto, sync::Arc};
 
 // tracing instrument 👉🏻 https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html
-#[tracing::instrument(name = "sign_in", skip(data, session), fields(email = %data.email))]
+#[tracing::instrument(name = "sign_in", skip(data, session), fields(email = %data.email), err)]
 pub async fn sign_in(data: Data<SignInRequest>, session: Unit<Arc<UserSession>>) -> DataResult<UserProfile, UserError> {
     let params: SignInParams = data.into_inner().try_into()?;
     let user_profile = session.sign_in(params).await?;
@@ -16,7 +16,8 @@ pub async fn sign_in(data: Data<SignInRequest>, session: Unit<Arc<UserSession>>)
     fields(
         email = %data.email,
         name = %data.name,
-    )
+    ),
+    err
 )]
 pub async fn sign_up(data: Data<SignUpRequest>, session: Unit<Arc<UserSession>>) -> DataResult<UserProfile, UserError> {
     let params: SignUpParams = data.into_inner().try_into()?;

+ 1 - 2
rust-lib/flowy-user/src/services/server/mod.rs

@@ -25,7 +25,6 @@ pub(crate) fn construct_user_server(config: &ServerConfig) -> Arc<dyn UserServer
     if cfg!(feature = "http_server") {
         Arc::new(UserServer::new(config.clone()))
     } else {
-        // Arc::new(UserServerMock {})
-        Arc::new(UserServer::new(config.clone()))
+        Arc::new(UserServerMock {})
     }
 }

+ 36 - 17
rust-lib/flowy-user/tests/event/auth_test.rs

@@ -1,5 +1,5 @@
 use crate::helper::*;
-use flowy_test::{builder::UserTest, init_test_sdk, FlowyEnv};
+use flowy_test::{builder::UserTest, FlowyTest};
 use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
 use serial_test::*;
 
@@ -7,7 +7,7 @@ use serial_test::*;
 #[serial]
 fn sign_up_with_invalid_email() {
     for email in invalid_email_test_case() {
-        let sdk = init_test_sdk();
+        let test = FlowyTest::setup();
         let request = SignUpRequest {
             email: email.to_string(),
             name: valid_name(),
@@ -15,7 +15,12 @@ fn sign_up_with_invalid_email() {
         };
 
         assert_eq!(
-            UserTest::new(sdk).event(SignUp).request(request).sync_send().error().code,
+            UserTest::new(test.sdk)
+                .event(SignUp)
+                .request(request)
+                .sync_send()
+                .error()
+                .code,
             ErrorCode::EmailFormatInvalid
         );
     }
@@ -24,29 +29,33 @@ fn sign_up_with_invalid_email() {
 #[serial]
 fn sign_up_with_invalid_password() {
     for password in invalid_password_test_case() {
-        let sdk = init_test_sdk();
+        let test = FlowyTest::setup();
         let request = SignUpRequest {
             email: random_email(),
             name: valid_name(),
             password,
         };
 
-        UserTest::new(sdk).event(SignUp).request(request).sync_send().assert_error();
+        UserTest::new(test.sdk)
+            .event(SignUp)
+            .request(request)
+            .sync_send()
+            .assert_error();
     }
 }
 
-#[test]
-#[serial]
-fn sign_in_success() {
-    let env = FlowyEnv::setup();
-    let _ = UserTest::new(env.sdk()).event(SignOut).sync_send();
+#[tokio::test]
+async fn sign_in_success() {
+    let test = FlowyTest::setup();
+    let _ = UserTest::new(test.sdk()).event(SignOut).sync_send();
+    let sign_up_context = test.sign_up().await;
 
     let request = SignInRequest {
-        email: env.user.email.clone(),
-        password: env.password.clone(),
+        email: sign_up_context.user_profile.email.clone(),
+        password: sign_up_context.password.clone(),
     };
 
-    let response = UserTest::new(env.sdk())
+    let response = UserTest::new(test.sdk())
         .event(SignIn)
         .request(request)
         .sync_send()
@@ -58,14 +67,19 @@ fn sign_in_success() {
 #[serial]
 fn sign_in_with_invalid_email() {
     for email in invalid_email_test_case() {
-        let sdk = init_test_sdk();
+        let test = FlowyTest::setup();
         let request = SignInRequest {
             email: email.to_string(),
             password: login_password(),
         };
 
         assert_eq!(
-            UserTest::new(sdk).event(SignIn).request(request).sync_send().error().code,
+            UserTest::new(test.sdk)
+                .event(SignIn)
+                .request(request)
+                .sync_send()
+                .error()
+                .code,
             ErrorCode::EmailFormatInvalid
         );
     }
@@ -75,12 +89,17 @@ fn sign_in_with_invalid_email() {
 #[serial]
 fn sign_in_with_invalid_password() {
     for password in invalid_password_test_case() {
-        let sdk = init_test_sdk();
+        let test = FlowyTest::setup();
+
         let request = SignInRequest {
             email: random_email(),
             password,
         };
 
-        UserTest::new(sdk).event(SignIn).request(request).sync_send().assert_error();
+        UserTest::new(test.sdk)
+            .event(SignIn)
+            .request(request)
+            .sync_send()
+            .assert_error();
     }
 }

+ 52 - 40
rust-lib/flowy-user/tests/event/user_profile_test.rs

@@ -1,37 +1,39 @@
 use crate::helper::*;
 use flowy_infra::uuid;
-use flowy_test::{builder::UserTest, init_test_sdk, FlowyEnv};
+use flowy_test::{builder::UserTest, FlowyTest};
 use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
 use serial_test::*;
 
 #[test]
 #[serial]
 fn user_profile_get_failed() {
-    let sdk = init_test_sdk();
-    let result = UserTest::new(sdk).event(GetUserProfile).assert_error().sync_send();
+    let test = FlowyTest::setup();
+    let result = UserTest::new(test.sdk).event(GetUserProfile).assert_error().sync_send();
     assert!(result.user_profile().is_none())
 }
 
-#[test]
+#[tokio::test]
 #[serial]
-fn user_profile_get() {
-    let env = FlowyEnv::setup();
-    let user = UserTest::new(env.sdk.clone())
+async fn user_profile_get() {
+    let test = FlowyTest::setup();
+    let user_profile = test.init_user().await;
+    let user = UserTest::new(test.sdk.clone())
         .event(GetUserProfile)
         .sync_send()
         .parse::<UserProfile>();
-    assert_eq!(env.user, user);
+    assert_eq!(user_profile, user);
 }
 
-#[test]
+#[tokio::test]
 #[serial]
-fn user_update_with_name() {
-    let env = FlowyEnv::setup();
+async fn user_update_with_name() {
+    let test = FlowyTest::setup();
+    let user = test.init_user().await;
     let new_name = "hello_world".to_owned();
-    let request = UpdateUserRequest::new(&env.user.id).name(&new_name);
-    let _ = UserTest::new(env.sdk()).event(UpdateUser).request(request).sync_send();
+    let request = UpdateUserRequest::new(&user.id).name(&new_name);
+    let _ = UserTest::new(test.sdk()).event(UpdateUser).request(request).sync_send();
 
-    let user_profile = UserTest::new(env.sdk())
+    let user_profile = UserTest::new(test.sdk())
         .event(GetUserProfile)
         .assert_error()
         .sync_send()
@@ -40,14 +42,15 @@ fn user_update_with_name() {
     assert_eq!(user_profile.name, new_name,);
 }
 
-#[test]
+#[tokio::test]
 #[serial]
-fn user_update_with_email() {
-    let env = FlowyEnv::setup();
+async fn user_update_with_email() {
+    let test = FlowyTest::setup();
+    let user = test.init_user().await;
     let new_email = format!("{}@gmai.com", uuid());
-    let request = UpdateUserRequest::new(&env.user.id).email(&new_email);
-    let _ = UserTest::new(env.sdk()).event(UpdateUser).request(request).sync_send();
-    let user_profile = UserTest::new(env.sdk())
+    let request = UpdateUserRequest::new(&user.id).email(&new_email);
+    let _ = UserTest::new(test.sdk()).event(UpdateUser).request(request).sync_send();
+    let user_profile = UserTest::new(test.sdk())
         .event(GetUserProfile)
         .assert_error()
         .sync_send()
@@ -56,41 +59,49 @@ fn user_update_with_email() {
     assert_eq!(user_profile.email, new_email,);
 }
 
-#[test]
+#[tokio::test]
 #[serial]
-fn user_update_with_password() {
-    let env = FlowyEnv::setup();
+async fn user_update_with_password() {
+    let test = FlowyTest::setup();
+    let user = test.init_user().await;
     let new_password = "H123world!".to_owned();
-    let request = UpdateUserRequest::new(&env.user.id).password(&new_password);
+    let request = UpdateUserRequest::new(&user.id).password(&new_password);
 
-    let _ = UserTest::new(env.sdk())
+    let _ = UserTest::new(test.sdk())
         .event(UpdateUser)
         .request(request)
         .sync_send()
         .assert_success();
 }
 
-#[test]
+#[tokio::test]
 #[serial]
-fn user_update_with_invalid_email() {
-    let env = FlowyEnv::setup();
+async fn user_update_with_invalid_email() {
+    let test = FlowyTest::setup();
+    let user = test.init_user().await;
     for email in invalid_email_test_case() {
-        let request = UpdateUserRequest::new(&env.user.id).email(&email);
+        let request = UpdateUserRequest::new(&user.id).email(&email);
         assert_eq!(
-            UserTest::new(env.sdk()).event(UpdateUser).request(request).sync_send().error().code,
+            UserTest::new(test.sdk())
+                .event(UpdateUser)
+                .request(request)
+                .sync_send()
+                .error()
+                .code,
             ErrorCode::EmailFormatInvalid
         );
     }
 }
 
-#[test]
+#[tokio::test]
 #[serial]
-fn user_update_with_invalid_password() {
-    let env = FlowyEnv::setup();
+async fn user_update_with_invalid_password() {
+    let test = FlowyTest::setup();
+    let user = test.init_user().await;
     for password in invalid_password_test_case() {
-        let request = UpdateUserRequest::new(&env.user.id).password(&password);
+        let request = UpdateUserRequest::new(&user.id).password(&password);
 
-        UserTest::new(env.sdk())
+        UserTest::new(test.sdk())
             .event(UpdateUser)
             .request(request)
             .sync_send()
@@ -98,12 +109,13 @@ fn user_update_with_invalid_password() {
     }
 }
 
-#[test]
+#[tokio::test]
 #[serial]
-fn user_update_with_invalid_name() {
-    let env = FlowyEnv::setup();
-    let request = UpdateUserRequest::new(&env.user.id).name("");
-    UserTest::new(env.sdk())
+async fn user_update_with_invalid_name() {
+    let test = FlowyTest::setup();
+    let user = test.init_user().await;
+    let request = UpdateUserRequest::new(&user.id).name("");
+    UserTest::new(test.sdk())
         .event(UpdateUser)
         .request(request)
         .sync_send()

+ 0 - 1
rust-lib/flowy-workspace/src/services/app_controller.rs

@@ -101,7 +101,6 @@ impl AppController {
     async fn create_app_on_server(&self, params: CreateAppParams) -> Result<App, WorkspaceError> {
         let token = self.user.token()?;
         let app = self.server.create_app(&token, params).await?;
-        log::info!("😁 {:?}", app);
         Ok(app)
     }
 

+ 20 - 21
rust-lib/flowy-workspace/tests/workspace/app_test.rs

@@ -1,27 +1,26 @@
-use crate::helper::*;
-
+use flowy_test::workspace::*;
 use flowy_workspace::entities::{app::QueryAppRequest, view::*};
 
-#[test]
+#[tokio::test]
 #[should_panic]
-fn app_delete() {
-    let test = AppTest::new();
+async fn app_delete() {
+    let test = AppTest::new().await;
     delete_app(&test.sdk, &test.app.id);
     let query = QueryAppRequest::new(&test.app.id);
     let _ = read_app(&test.sdk, query);
 }
 
-#[test]
-fn app_read() {
-    let test = AppTest::new();
+#[tokio::test]
+async fn app_read() {
+    let test = AppTest::new().await;
     let query = QueryAppRequest::new(&test.app.id);
     let app_from_db = read_app(&test.sdk, query);
     assert_eq!(app_from_db, test.app);
 }
 
-#[test]
-fn app_create_with_view() {
-    let test = AppTest::new();
+#[tokio::test]
+async fn app_create_with_view() {
+    let test = AppTest::new().await;
     let request_a = CreateViewRequest {
         belong_to_id: test.app.id.clone(),
         name: "View A".to_string(),
@@ -38,8 +37,8 @@ fn app_create_with_view() {
         view_type: ViewType::Doc,
     };
 
-    let view_a = create_view_with_request(&test.sdk, request_a);
-    let view_b = create_view_with_request(&test.sdk, request_b);
+    let view_a = create_view_with_request(&test.sdk, request_a).await;
+    let view_b = create_view_with_request(&test.sdk, request_b).await;
 
     let query = QueryAppRequest::new(&test.app.id).read_views();
     let view_from_db = read_app(&test.sdk, query);
@@ -48,20 +47,20 @@ fn app_create_with_view() {
     assert_eq!(view_from_db.belongings[1], view_b);
 }
 
-#[test]
-fn app_set_trash_flag() {
-    let test = AppTest::new();
-    test.move_app_to_trash();
+#[tokio::test]
+async fn app_set_trash_flag() {
+    let test = AppTest::new().await;
+    test.move_app_to_trash().await;
 
     let query = QueryAppRequest::new(&test.app.id).trash();
     let _ = read_app(&test.sdk, query);
 }
 
-#[test]
+#[tokio::test]
 #[should_panic]
-fn app_set_trash_flag_2() {
-    let test = AppTest::new();
-    test.move_app_to_trash();
+async fn app_set_trash_flag_2() {
+    let test = AppTest::new().await;
+    test.move_app_to_trash().await;
     let query = QueryAppRequest::new(&test.app.id);
     let _ = read_app(&test.sdk, query);
 }

+ 1 - 1
rust-lib/flowy-workspace/tests/workspace/main.rs

@@ -1,4 +1,4 @@
 mod app_test;
-mod helper;
+// mod helper;
 mod view_test;
 mod workspace_test;

+ 23 - 16
rust-lib/flowy-workspace/tests/workspace/view_test.rs

@@ -1,34 +1,41 @@
-use crate::helper::*;
-
+use flowy_test::{workspace::*, FlowyTest};
 use flowy_workspace::entities::view::*;
 
-#[test]
-fn view_move_to_trash() {
-    let test = ViewTest::new();
-    test.move_view_to_trash();
+#[tokio::test]
+async fn view_move_to_trash() {
+    let test = FlowyTest::setup();
+    let _ = test.init_user().await;
+
+    let test = ViewTest::new(&test).await;
+    test.move_view_to_trash().await;
 
     let query = QueryViewRequest::new(&test.view.id).trash();
-    let view = read_view(&test.sdk, query);
+    let view = read_view(&test.sdk, query).await;
     assert_eq!(view, test.view);
 }
 
-#[test]
+#[tokio::test]
 #[should_panic]
-fn view_move_to_trash2() {
-    let test = ViewTest::new();
-    test.move_view_to_trash();
+async fn view_move_to_trash2() {
+    let test = FlowyTest::setup();
+    let _ = test.init_user();
+
+    let test = ViewTest::new(&test).await;
+    test.move_view_to_trash().await;
     let query = QueryViewRequest::new(&test.view.id);
-    let _ = read_view(&test.sdk, query);
+    let _ = read_view(&test.sdk, query).await;
 }
 
-#[test]
-fn view_open_doc() {
-    let test = ViewTest::new();
+#[tokio::test]
+async fn view_open_doc() {
+    let test = FlowyTest::setup();
+    let _ = test.init_user().await;
 
+    let test = ViewTest::new(&test).await;
     let request = OpenViewRequest {
         view_id: test.view.id.clone(),
     };
-    let _ = open_view(&test.sdk, request);
+    let _ = open_view(&test.sdk, request).await;
 }
 
 #[test]

+ 21 - 16
rust-lib/flowy-workspace/tests/workspace/workspace_test.rs

@@ -1,30 +1,29 @@
-use crate::helper::*;
-use flowy_test::{builder::*, FlowyEnv};
+use flowy_test::{builder::*, workspace::*, FlowyTest};
 use flowy_workspace::{
     entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest},
     event::WorkspaceEvent::*,
     prelude::*,
 };
 
-#[test]
-fn workspace_read_all() {
-    let test = WorkspaceTest::new();
+#[tokio::test]
+async fn workspace_read_all() {
+    let test = WorkspaceTest::new().await;
     let workspace = read_workspace(&test.sdk, QueryWorkspaceRequest::new()).unwrap();
     assert_eq!(test.workspace, workspace);
 }
 
-#[test]
-fn workspace_read() {
-    let test = WorkspaceTest::new();
+#[tokio::test]
+async fn workspace_read() {
+    let test = WorkspaceTest::new().await;
     let request = QueryWorkspaceRequest::new().workspace_id(&test.workspace.id);
     let workspace = read_workspace(&test.sdk, request).unwrap();
     assert_eq!(test.workspace, workspace);
 }
 
-#[test]
-fn workspace_create_with_apps() {
-    let test = WorkspaceTest::new();
-    let app = create_app(&test.sdk, "App A", "AppFlowy Github Project", &test.workspace.id);
+#[tokio::test]
+async fn workspace_create_with_apps() {
+    let test = WorkspaceTest::new().await;
+    let app = create_app(&test.sdk, "App A", "AppFlowy Github Project", &test.workspace.id).await;
     let request = QueryWorkspaceRequest::new().workspace_id(&test.workspace.id);
     let workspace_from_db = read_workspace(&test.sdk, request).unwrap();
     assert_eq!(&app, workspace_from_db.apps.first_or_crash());
@@ -33,8 +32,11 @@ fn workspace_create_with_apps() {
 #[test]
 fn workspace_create_with_invalid_name() {
     for name in invalid_workspace_name_test_case() {
-        let sdk = FlowyEnv::setup().sdk;
-        let request = CreateWorkspaceRequest { name, desc: "".to_owned() };
+        let sdk = FlowyTest::setup().sdk;
+        let request = CreateWorkspaceRequest {
+            name,
+            desc: "".to_owned(),
+        };
         assert_eq!(
             FlowyWorkspaceTest::new(sdk)
                 .event(CreateWorkspace)
@@ -49,9 +51,12 @@ fn workspace_create_with_invalid_name() {
 
 #[test]
 fn workspace_update_with_invalid_name() {
-    let sdk = FlowyEnv::setup().sdk;
+    let sdk = FlowyTest::setup().sdk;
     for name in invalid_workspace_name_test_case() {
-        let request = CreateWorkspaceRequest { name, desc: "".to_owned() };
+        let request = CreateWorkspaceRequest {
+            name,
+            desc: "".to_owned(),
+        };
         assert_eq!(
             FlowyWorkspaceTest::new(sdk.clone())
                 .event(CreateWorkspace)

+ 18 - 18
rust-lib/flowy-ws/src/ws.rs

@@ -260,21 +260,21 @@ impl WsSender {
     }
 }
 
-#[cfg(test)]
-mod tests {
-    use super::WsController;
-
-    #[tokio::test]
-    async fn connect() {
-        std::env::set_var("RUST_LOG", "Debug");
-        env_logger::init();
-
-        let mut controller = WsController::new();
-        let addr = format!("{}/123", flowy_net::config::WS_ADDR.as_str());
-        let (a, b) = controller.make_connect(addr);
-        tokio::select! {
-            r = a => println!("write completed {:?}", r),
-            _ = b => println!("read completed"),
-        };
-    }
-}
+// #[cfg(test)]
+// mod tests {
+//     use super::WsController;
+//
+//     #[tokio::test]
+//     async fn connect() {
+//         std::env::set_var("RUST_LOG", "Debug");
+//         env_logger::init();
+//
+//         let mut controller = WsController::new();
+//         let addr = format!("{}/123", flowy_net::config::WS_ADDR.as_str());
+//         let (a, b) = controller.make_connect(addr);
+//         tokio::select! {
+//             r = a => println!("write completed {:?}", r),
+//             _ = b => println!("read completed"),
+//         };
+//     }
+// }