Browse Source

support run tests on remote

appflowy 3 năm trước cách đây
mục cha
commit
ccb51234c5
32 tập tin đã thay đổi với 526 bổ sung519 xóa
  1. 1 0
      rust-lib/dart-ffi/Cargo.toml
  2. 12 10
      rust-lib/dart-ffi/src/lib.rs
  3. 70 47
      rust-lib/flowy-dispatch/src/dispatch.rs
  4. 8 34
      rust-lib/flowy-dispatch/src/module/module.rs
  5. 2 2
      rust-lib/flowy-dispatch/tests/api/helper.rs
  6. 6 4
      rust-lib/flowy-dispatch/tests/api/module.rs
  7. 8 8
      rust-lib/flowy-document/tests/editor/doc_test.rs
  8. 7 6
      rust-lib/flowy-document/tests/editor/helper.rs
  9. 2 3
      rust-lib/flowy-net/src/request/request.rs
  10. 2 2
      rust-lib/flowy-sdk/Cargo.toml
  11. 30 20
      rust-lib/flowy-sdk/src/lib.rs
  12. 121 68
      rust-lib/flowy-test/src/builder.rs
  13. 6 6
      rust-lib/flowy-test/src/helper.rs
  14. 23 10
      rust-lib/flowy-test/src/lib.rs
  15. 0 142
      rust-lib/flowy-test/src/tester.rs
  16. 60 0
      rust-lib/flowy-test/src/workspace_builder.rs
  17. 1 1
      rust-lib/flowy-user/Cargo.toml
  18. 5 22
      rust-lib/flowy-user/src/entities/user_detail.rs
  19. 3 3
      rust-lib/flowy-user/src/services/server/mod.rs
  20. 1 1
      rust-lib/flowy-user/src/services/user/database.rs
  21. 1 1
      rust-lib/flowy-user/src/services/user/user_session.rs
  22. 13 15
      rust-lib/flowy-user/tests/event/auth_test.rs
  23. 42 21
      rust-lib/flowy-user/tests/event/user_profile_test.rs
  24. 1 1
      rust-lib/flowy-workspace/Cargo.toml
  25. 1 5
      rust-lib/flowy-workspace/src/handlers/workspace_handler.rs
  26. 3 3
      rust-lib/flowy-workspace/src/services/server/mod.rs
  27. 1 1
      rust-lib/flowy-workspace/src/services/workspace_controller.rs
  28. 29 30
      rust-lib/flowy-workspace/tests/workspace/app_test.rs
  29. 30 20
      rust-lib/flowy-workspace/tests/workspace/helper.rs
  30. 14 15
      rust-lib/flowy-workspace/tests/workspace/view_test.rs
  31. 20 14
      rust-lib/flowy-workspace/tests/workspace/workspace_test.rs
  32. 3 4
      scripts/makefile/tests.toml

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

@@ -25,6 +25,7 @@ log = "0.4.14"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = {version = "1.0"}
 bytes = { version = "1.0" }
+parking_lot = "0.11"
 
 flowy-dispatch = {path = "../flowy-dispatch"}
 flowy-sdk = {path = "../flowy-sdk"}

+ 12 - 10
rust-lib/dart-ffi/src/lib.rs

@@ -9,15 +9,22 @@ use crate::{
 };
 use flowy_dispatch::prelude::*;
 use flowy_sdk::*;
+use lazy_static::lazy_static;
+use parking_lot::RwLock;
+use std::{ffi::CStr, os::raw::c_char, sync::Arc};
 
-use std::{ffi::CStr, os::raw::c_char};
+lazy_static! {
+    static ref FLOWY_SDK: RwLock<Option<Arc<FlowySDK>>> = RwLock::new(None);
+}
+
+fn dispatch() -> Arc<EventDispatch> { FLOWY_SDK.read().as_ref().unwrap().dispatch() }
 
 #[no_mangle]
 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();
     log::info!("🔥 FlowySDK start running");
-    FlowySDK::new(path).construct();
+    *FLOWY_SDK.write() = Some(Arc::new(FlowySDK::new(path)));
 
     return 1;
 }
@@ -25,14 +32,9 @@ pub extern "C" fn init_sdk(path: *mut c_char) -> i64 {
 #[no_mangle]
 pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
     let request: ModuleRequest = FFIRequest::from_u8_pointer(input, len).into();
-    log::trace!(
-        "[FFI]: {} Async Event: {:?} with {} port",
-        &request.id,
-        &request.event,
-        port
-    );
+    log::trace!("[FFI]: {} Async Event: {:?} with {} port", &request.id, &request.event, port);
 
-    let _ = EventDispatch::async_send_with_callback(request, move |resp: EventResponse| {
+    let _ = EventDispatch::async_send_with_callback(dispatch(), request, move |resp: EventResponse| {
         log::trace!("[FFI]: Post data to dart through {} port", port);
         Box::pin(post_to_flutter(resp, port))
     });
@@ -42,7 +44,7 @@ pub extern "C" fn async_command(port: i64, input: *const u8, len: usize) {
 pub extern "C" fn sync_command(input: *const u8, len: usize) -> *const u8 {
     let request: ModuleRequest = FFIRequest::from_u8_pointer(input, len).into();
     log::trace!("[FFI]: {} Sync Event: {:?}", &request.id, &request.event,);
-    let _response = EventDispatch::sync_send(request);
+    let _response = EventDispatch::sync_send(dispatch(), request);
 
     // FFIResponse {  }
     let response_bytes = vec![];

+ 70 - 47
rust-lib/flowy-dispatch/src/dispatch.rs

@@ -8,22 +8,17 @@ use crate::{
 use derivative::*;
 use futures_core::future::BoxFuture;
 use futures_util::task::Context;
-use lazy_static::lazy_static;
+
 use pin_project::pin_project;
-use std::{future::Future, sync::RwLock};
+use std::{future::Future, sync::Arc};
 use tokio::macros::support::{Pin, Poll};
-
-lazy_static! {
-    static ref EVENT_DISPATCH: RwLock<Option<EventDispatch>> = RwLock::new(None);
-}
-
 pub struct EventDispatch {
     module_map: ModuleMap,
     runtime: tokio::runtime::Runtime,
 }
 
 impl EventDispatch {
-    pub fn construct<F>(module_factory: F)
+    pub fn construct<F>(module_factory: F) -> EventDispatch
     where
         F: FnOnce() -> Vec<Module>,
     {
@@ -32,60 +27,88 @@ impl EventDispatch {
         let module_map = as_module_map(modules);
         let runtime = tokio_default_runtime().unwrap();
         let dispatch = EventDispatch { module_map, runtime };
-        *(EVENT_DISPATCH.write().unwrap()) = Some(dispatch);
+        dispatch
     }
 
-    pub fn async_send<Req>(request: Req) -> DispatchFuture<EventResponse>
+    pub fn async_send<Req>(dispatch: Arc<EventDispatch>, request: Req) -> DispatchFuture<EventResponse>
     where
         Req: std::convert::Into<ModuleRequest>,
     {
-        EventDispatch::async_send_with_callback(request, |_| Box::pin(async {}))
+        EventDispatch::async_send_with_callback(dispatch, request, |_| Box::pin(async {}))
     }
 
-    pub fn async_send_with_callback<Req, Callback>(request: Req, callback: Callback) -> DispatchFuture<EventResponse>
+    pub fn async_send_with_callback<Req, Callback>(
+        dispatch: Arc<EventDispatch>,
+        request: Req,
+        callback: Callback,
+    ) -> DispatchFuture<EventResponse>
     where
         Req: std::convert::Into<ModuleRequest>,
         Callback: FnOnce(EventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync,
     {
         let request: ModuleRequest = request.into();
-        match EVENT_DISPATCH.read() {
-            Ok(dispatch) => {
-                let dispatch = dispatch.as_ref().unwrap();
-                let module_map = dispatch.module_map.clone();
-                let service = Box::new(DispatchService { module_map });
-                log::trace!("Async event: {:?}", &request.event);
-                let service_ctx = DispatchContext {
-                    request,
-                    callback: Some(Box::new(callback)),
-                };
-                let join_handle = dispatch.runtime.spawn(async move {
-                    service
-                        .call(service_ctx)
-                        .await
-                        .unwrap_or_else(|e| InternalError::Other(format!("{:?}", e)).as_response())
-                });
-
-                DispatchFuture {
-                    fut: Box::pin(async move {
-                        join_handle.await.unwrap_or_else(|e| {
-                            let error = InternalError::JoinError(format!("EVENT_DISPATCH join error: {:?}", e));
-                            error.as_response()
-                        })
-                    }),
-                }
-            },
-
-            Err(e) => {
-                let msg = format!("EVENT_DISPATCH read failed. {:?}", e);
-                DispatchFuture {
-                    fut: Box::pin(async { InternalError::Lock(msg).as_response() }),
-                }
-            },
+        let module_map = dispatch.module_map.clone();
+        let service = Box::new(DispatchService { module_map });
+        log::trace!("Async event: {:?}", &request.event);
+        let service_ctx = DispatchContext {
+            request,
+            callback: Some(Box::new(callback)),
+        };
+        let join_handle = dispatch.runtime.spawn(async move {
+            service
+                .call(service_ctx)
+                .await
+                .unwrap_or_else(|e| InternalError::Other(format!("{:?}", e)).as_response())
+        });
+
+        DispatchFuture {
+            fut: Box::pin(async move {
+                join_handle.await.unwrap_or_else(|e| {
+                    let error = InternalError::JoinError(format!("EVENT_DISPATCH join error: {:?}", e));
+                    error.as_response()
+                })
+            }),
         }
+        // match dispatch.read() {
+        //     Ok(dispatch) => {
+        //         let dispatch = dispatch.as_ref().unwrap();
+        //         let module_map = dispatch.module_map.clone();
+        //         let service = Box::new(DispatchService { module_map });
+        //         log::trace!("Async event: {:?}", &request.event);
+        //         let service_ctx = DispatchContext {
+        //             request,
+        //             callback: Some(Box::new(callback)),
+        //         };
+        //         let join_handle = dispatch.runtime.spawn(async move {
+        //             service
+        //                 .call(service_ctx)
+        //                 .await
+        //                 .unwrap_or_else(|e|
+        // InternalError::Other(format!("{:?}", e)).as_response())
+        //         });
+        //
+        //         DispatchFuture {
+        //             fut: Box::pin(async move {
+        //                 join_handle.await.unwrap_or_else(|e| {
+        //                     let error =
+        // InternalError::JoinError(format!("EVENT_DISPATCH join error: {:?}",
+        // e));                     error.as_response()
+        //                 })
+        //             }),
+        //         }
+        //     },
+        //
+        //     Err(e) => {
+        //         let msg = format!("EVENT_DISPATCH read failed. {:?}", e);
+        //         DispatchFuture {
+        //             fut: Box::pin(async {
+        // InternalError::Lock(msg).as_response() }),         }
+        //     },
+        // }
     }
 
-    pub fn sync_send(request: ModuleRequest) -> EventResponse {
-        futures::executor::block_on(async { EventDispatch::async_send_with_callback(request, |_| Box::pin(async {})).await })
+    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 })
     }
 }
 

+ 8 - 34
rust-lib/flowy-dispatch/src/module/module.rs

@@ -16,17 +16,7 @@ use crate::{
     module::{container::ModuleDataMap, Unit},
     request::{payload::Payload, EventRequest, FromRequest},
     response::{EventResponse, Responder},
-    service::{
-        factory,
-        BoxService,
-        BoxServiceFactory,
-        Handler,
-        HandlerService,
-        Service,
-        ServiceFactory,
-        ServiceRequest,
-        ServiceResponse,
-    },
+    service::{factory, BoxService, BoxServiceFactory, Handler, HandlerService, Service, ServiceFactory, ServiceRequest, ServiceResponse},
 };
 use futures_core::future::BoxFuture;
 use std::sync::Arc;
@@ -51,8 +41,7 @@ impl<T: Display + Eq + Hash + Debug + Clone> std::convert::From<T> for Event {
     fn from(t: T) -> Self { Event(format!("{}", t)) }
 }
 
-pub type EventServiceFactory =
-    BoxServiceFactory<(), ServiceRequest, ServiceResponse, DispatchError>;
+pub type EventServiceFactory = BoxServiceFactory<(), ServiceRequest, ServiceResponse, DispatchError>;
 
 pub struct Module {
     pub name: String,
@@ -75,9 +64,7 @@ impl Module {
     }
 
     pub fn data<D: 'static + Send + Sync>(mut self, data: D) -> Self {
-        Arc::get_mut(&mut self.module_data)
-            .unwrap()
-            .insert(Unit::new(data));
+        Arc::get_mut(&mut self.module_data).unwrap().insert(Unit::new(data));
 
         self
     }
@@ -102,15 +89,10 @@ impl Module {
         self
     }
 
-    pub fn events(&self) -> Vec<Event> {
-        self.service_map
-            .keys()
-            .map(|key| key.clone())
-            .collect::<Vec<_>>()
-    }
+    pub fn events(&self) -> Vec<Event> { self.service_map.keys().map(|key| key.clone()).collect::<Vec<_>>() }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct ModuleRequest {
     pub id: String,
     pub event: Event,
@@ -139,9 +121,7 @@ impl ModuleRequest {
 }
 
 impl std::fmt::Display for ModuleRequest {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}:{:?}", self.id, self.event)
-    }
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{:?}", self.id, self.event) }
 }
 
 impl ServiceFactory<ModuleRequest> for Module {
@@ -155,10 +135,7 @@ impl ServiceFactory<ModuleRequest> for Module {
         let service_map = self.service_map.clone();
         let module_data = self.module_data.clone();
         Box::pin(async move {
-            let service = ModuleService {
-                service_map,
-                module_data,
-            };
+            let service = ModuleService { service_map, module_data };
             let module_service = Box::new(service) as Self::Service;
             Ok(module_service)
         })
@@ -193,10 +170,7 @@ impl Service<ModuleRequest> for ModuleService {
                 Box::pin(async move { Ok(fut.await.unwrap_or_else(|e| e.into())) })
             },
             None => {
-                let msg = format!(
-                    "Can not find service factory for event: {:?}",
-                    request.event
-                );
+                let msg = format!("Can not find service factory for event: {:?}", request.event);
                 Box::pin(async { Err(InternalError::ServiceNotFound(msg).into()) })
             },
         }

+ 2 - 2
rust-lib/flowy-dispatch/tests/api/helper.rs

@@ -10,9 +10,9 @@ pub fn setup_env() {
     });
 }
 
-pub fn init_dispatch<F>(module_factory: F)
+pub fn init_dispatch<F>(module_factory: F) -> EventDispatch
 where
     F: FnOnce() -> Vec<Module>,
 {
-    EventDispatch::construct(module_factory);
+    EventDispatch::construct(module_factory)
 }

+ 6 - 4
rust-lib/flowy-dispatch/tests/api/module.rs

@@ -1,19 +1,21 @@
 use crate::helper::*;
 use flowy_dispatch::prelude::*;
+use std::sync::Arc;
 
 pub async fn hello() -> String { "say hello".to_string() }
 
 #[tokio::test]
-async fn test_init() {
+async fn test() {
     setup_env();
     let event = "1";
-    init_dispatch(|| vec![Module::new().event(event, hello)]);
-
+    let dispatch = Arc::new(init_dispatch(|| vec![Module::new().event(event, hello)]));
     let request = ModuleRequest::new(event);
-    let _ = EventDispatch::async_send_with_callback(request, |resp| {
+    let _ = EventDispatch::async_send_with_callback(dispatch.clone(), request, |resp| {
         Box::pin(async move {
             dbg!(&resp);
         })
     })
     .await;
+
+    std::mem::forget(dispatch);
 }

+ 8 - 8
rust-lib/flowy-document/tests/editor/doc_test.rs

@@ -1,25 +1,25 @@
 use crate::helper::*;
-use flowy_test::builder::UserTestBuilder;
+use flowy_test::TestSDKBuilder;
 
 #[test]
 fn file_create_test() {
-    let _ = UserTestBuilder::new().sign_up();
-    let doc_desc = create_doc("hello world", "flutter ❤️ rust", "123");
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let doc_desc = create_doc(&sdk, "hello world", "flutter ❤️ rust", "123");
     dbg!(&doc_desc);
 
-    let doc = read_doc_data(&doc_desc.id, &doc_desc.path);
+    let doc = read_doc_data(&sdk, &doc_desc.id, &doc_desc.path);
     assert_eq!(doc.text, "123".to_owned());
 }
 
 #[test]
 fn file_update_text_test() {
-    let _ = UserTestBuilder::new().sign_up();
-    let doc_desc = create_doc("hello world", "flutter ❤️ rust", "");
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let doc_desc = create_doc(&sdk, "hello world", "flutter ❤️ rust", "");
     dbg!(&doc_desc);
 
     let content = "😁😁😁😁😁😁😁😁😁😁".to_owned();
-    save_doc(&doc_desc, &content);
+    save_doc(&sdk, &doc_desc, &content);
 
-    let doc = read_doc_data(&doc_desc.id, &doc_desc.path);
+    let doc = read_doc_data(&sdk, &doc_desc.id, &doc_desc.path);
     assert_eq!(doc.text, content);
 }

+ 7 - 6
rust-lib/flowy-document/tests/editor/helper.rs

@@ -2,8 +2,9 @@ use flowy_test::builder::DocTestBuilder;
 
 use flowy_document::{entities::doc::*, event::EditorEvent::*};
 use flowy_infra::uuid;
+use flowy_test::prelude::*;
 
-pub fn create_doc(name: &str, desc: &str, text: &str) -> DocInfo {
+pub fn create_doc(sdk: &FlowyTestSDK, name: &str, desc: &str, text: &str) -> DocInfo {
     let request = CreateDocRequest {
         id: uuid(),
         name: name.to_owned(),
@@ -11,7 +12,7 @@ pub fn create_doc(name: &str, desc: &str, text: &str) -> DocInfo {
         text: text.to_owned(),
     };
 
-    let doc = DocTestBuilder::new()
+    let doc = DocTestBuilder::new(sdk.clone())
         .event(CreateDoc)
         .request(request)
         .sync_send()
@@ -19,7 +20,7 @@ pub fn create_doc(name: &str, desc: &str, text: &str) -> DocInfo {
     doc
 }
 
-pub fn save_doc(desc: &DocInfo, content: &str) {
+pub fn save_doc(sdk: &FlowyTestSDK, desc: &DocInfo, content: &str) {
     let request = UpdateDocRequest {
         id: desc.id.clone(),
         name: Some(desc.name.clone()),
@@ -27,7 +28,7 @@ pub fn save_doc(desc: &DocInfo, content: &str) {
         text: Some(content.to_owned()),
     };
 
-    let _ = DocTestBuilder::new().event(UpdateDoc).request(request).sync_send();
+    let _ = DocTestBuilder::new(sdk.clone()).event(UpdateDoc).request(request).sync_send();
 }
 
 // #[allow(dead_code)]
@@ -45,13 +46,13 @@ pub fn save_doc(desc: &DocInfo, content: &str) {
 //     doc
 // }
 
-pub(crate) fn read_doc_data(doc_id: &str, path: &str) -> DocData {
+pub(crate) fn read_doc_data(sdk: &FlowyTestSDK, doc_id: &str, path: &str) -> DocData {
     let request = QueryDocDataRequest {
         doc_id: doc_id.to_string(),
         path: path.to_string(),
     };
 
-    let doc = DocTestBuilder::new()
+    let doc = DocTestBuilder::new(sdk.clone())
         .event(ReadDocData)
         .request(request)
         .sync_send()

+ 2 - 3
rust-lib/flowy-net/src/request/request.rs

@@ -80,8 +80,7 @@ impl HttpRequestBuilder {
         // reqwest client is not 'Sync' by channel is.
         tokio::spawn(async move {
             let client = default_client();
-            let mut builder = client.request(method, url).headers(headers);
-
+            let mut builder = client.request(method.clone(), url).headers(headers);
             if let Some(body) = body {
                 builder = builder.body(body);
             }
@@ -90,7 +89,7 @@ impl HttpRequestBuilder {
             match tx.send(response) {
                 Ok(_) => {},
                 Err(e) => {
-                    log::error!("Send http response failed: {:?}", e)
+                    log::error!("[{}] Send http request failed: {:?}", method, e);
                 },
             }
         });

+ 2 - 2
rust-lib/flowy-sdk/Cargo.toml

@@ -7,8 +7,8 @@ edition = "2018"
 
 [dependencies]
 flowy-dispatch = { path = "../flowy-dispatch", features = ["use_tracing"]}
-#flowy-log = { path = "../flowy-log" }
-flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
+flowy-log = { path = "../flowy-log" }
+#flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
 flowy-user = { path = "../flowy-user" }
 flowy-infra = { path = "../flowy-infra" }
 flowy-workspace = { path = "../flowy-workspace" }

+ 30 - 20
rust-lib/flowy-sdk/src/lib.rs

@@ -5,39 +5,49 @@ pub mod module;
 use flowy_dispatch::prelude::*;
 use module::build_modules;
 pub use module::*;
-use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::{
+    atomic::{AtomicBool, Ordering},
+    Arc,
+};
 
 static INIT_LOG: AtomicBool = AtomicBool::new(false);
+#[derive(Clone)]
 pub struct FlowySDK {
     root: String,
+    dispatch: Arc<EventDispatch>,
 }
 
 impl FlowySDK {
-    pub fn new(root: &str) -> Self { Self { root: root.to_owned() } }
+    pub fn new(root: &str) -> Self {
+        init_log(root);
+        init_kv(root);
 
-    pub fn construct(self) { FlowySDK::construct_with(&self.root) }
+        let dispatch = Arc::new(init_dispatch(root));
+        let root = root.to_owned();
+        Self { root, dispatch }
+    }
 
-    pub fn construct_with(root: &str) {
-        FlowySDK::init_log(root);
+    pub fn dispatch(&self) -> Arc<EventDispatch> { self.dispatch.clone() }
+}
 
-        tracing::info!("🔥 Root path: {}", root);
-        match flowy_infra::kv::KV::init(root) {
-            Ok(_) => {},
-            Err(e) => tracing::error!("Init kv store failedL: {}", e),
-        }
-        FlowySDK::init_modules(root);
+fn init_kv(root: &str) {
+    tracing::info!("🔥 Root path: {}", root);
+    match flowy_infra::kv::KV::init(root) {
+        Ok(_) => {},
+        Err(e) => tracing::error!("Init kv store failedL: {}", e),
     }
+}
 
-    fn init_log(directory: &str) {
-        if !INIT_LOG.load(Ordering::SeqCst) {
-            INIT_LOG.store(true, Ordering::SeqCst);
+fn init_log(directory: &str) {
+    if !INIT_LOG.load(Ordering::SeqCst) {
+        INIT_LOG.store(true, Ordering::SeqCst);
 
-            let _ = flowy_log::Builder::new("flowy").local(directory).env_filter("info").build();
-        }
+        let _ = flowy_log::Builder::new("flowy").local(directory).env_filter("info").build();
     }
+}
 
-    fn init_modules(root: &str) {
-        let config = ModuleConfig { root: root.to_owned() };
-        EventDispatch::construct(|| build_modules(config));
-    }
+fn init_dispatch(root: &str) -> EventDispatch {
+    let config = ModuleConfig { root: root.to_owned() };
+    let dispatch = EventDispatch::construct(|| build_modules(config));
+    dispatch
 }

+ 121 - 68
rust-lib/flowy-test/src/builder.rs

@@ -1,137 +1,190 @@
-use flowy_dispatch::prelude::{FromBytes, ToBytes};
+use flowy_dispatch::prelude::{EventDispatch, EventResponse, FromBytes, ModuleRequest, StatusCode, ToBytes};
 use flowy_user::entities::UserDetail;
 use std::{
     fmt::{Debug, Display},
     hash::Hash,
 };
 
-use crate::{
-    helper::{create_default_workspace_if_need, login_email, login_password},
-    init_test_sdk,
-    tester::{TesterContext, TesterTrait},
-};
+use crate::helper::{create_default_workspace_if_need, login_email, login_password, random_email};
+use flowy_dispatch::prelude::*;
 use flowy_document::errors::DocError;
-use flowy_user::errors::UserError;
+pub use flowy_sdk::*;
+use flowy_user::{
+    errors::UserError,
+    event::UserEvent::{SignOut, SignUp},
+    prelude::*,
+};
 use flowy_workspace::errors::WorkspaceError;
-use std::marker::PhantomData;
+use std::{marker::PhantomData, sync::Arc};
 
-pub type WorkspaceTestBuilder = Builder<RandomUserTester<WorkspaceError>>;
-impl WorkspaceTestBuilder {
-    pub fn new() -> Self { Builder::test(Box::new(RandomUserTester::<WorkspaceError>::new())) }
-}
+use crate::FlowyTestSDK;
+use flowy_user::event::UserEvent::SignIn;
+use std::convert::TryFrom;
 
-pub type DocTestBuilder = Builder<RandomUserTester<DocError>>;
+pub type DocTestBuilder = Builder<DocError>;
 impl DocTestBuilder {
-    pub fn new() -> Self { Builder::test(Box::new(RandomUserTester::<DocError>::new())) }
+    pub fn new(sdk: FlowyTestSDK) -> Self { Builder::test(TestContext::new(sdk)) }
 }
 
-pub type UserTestBuilder = Builder<RandomUserTester<UserError>>;
+pub type WorkspaceTestBuilder = Builder<WorkspaceError>;
+impl WorkspaceTestBuilder {
+    pub fn new(sdk: FlowyTestSDK) -> Self { Builder::test(TestContext::new(sdk)) }
+}
+
+pub type UserTestBuilder = Builder<UserError>;
 impl UserTestBuilder {
-    pub fn new() -> Self { Builder::test(Box::new(RandomUserTester::<UserError>::new())) }
+    pub fn new(sdk: FlowyTestSDK) -> Self { Builder::test(TestContext::new(sdk)) }
+
+    pub fn sign_up(self) -> 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_detail = EventDispatch::sync_send(self.dispatch(), request)
+            .parse::<UserDetail, UserError>()
+            .unwrap()
+            .unwrap();
 
-    pub fn sign_up(mut self) -> SignUpContext {
-        let (user_detail, password) = self.tester.sign_up();
-        let _ = create_default_workspace_if_need(&user_detail.id);
+        let _ = create_default_workspace_if_need(self.dispatch(), &user_detail.id);
         SignUpContext { user_detail, password }
     }
 
-    pub fn sign_in(mut self) -> Self {
-        let user_detail = self.tester.sign_in();
+    #[allow(dead_code)]
+    fn sign_in(mut self) -> Self {
+        let payload = SignInRequest {
+            email: login_email(),
+            password: login_password(),
+        }
+        .into_bytes()
+        .unwrap();
+
+        let request = ModuleRequest::new(SignIn).payload(payload);
+        let user_detail = EventDispatch::sync_send(self.dispatch(), request)
+            .parse::<UserDetail, UserError>()
+            .unwrap()
+            .unwrap();
+
         self.user_detail = Some(user_detail);
         self
     }
 
-    fn login_if_need(&mut self) {
-        let user_detail = self.tester.login_if_need();
-        self.user_detail = Some(user_detail);
-    }
+    #[allow(dead_code)]
+    fn logout(&self) { let _ = EventDispatch::sync_send(self.dispatch(), ModuleRequest::new(SignOut)); }
 
-    pub fn get_user_detail(&self) -> &Option<UserDetail> { &self.user_detail }
+    pub fn user_detail(&self) -> &Option<UserDetail> { &self.user_detail }
 }
 
-pub struct Builder<T: TesterTrait> {
-    pub tester: Box<T>,
+#[derive(Clone)]
+pub struct Builder<E> {
+    context: TestContext,
     user_detail: Option<UserDetail>,
+    err_phantom: PhantomData<E>,
 }
 
-impl<T> Builder<T>
+impl<E> Builder<E>
 where
-    T: TesterTrait,
+    E: FromBytes + Debug,
 {
-    fn test(tester: Box<T>) -> Self {
-        init_test_sdk();
-        Self { tester, user_detail: None }
+    pub(crate) fn test(context: TestContext) -> Self {
+        Self {
+            context,
+            user_detail: None,
+            err_phantom: PhantomData,
+        }
     }
 
-    pub fn request<P>(mut self, request: P) -> Self
+    pub fn request<P>(mut self, payload: P) -> Self
     where
         P: ToBytes,
     {
-        self.tester.set_payload(request);
+        match payload.into_bytes() {
+            Ok(bytes) => {
+                let module_request = self.get_request();
+                self.context.request = Some(module_request.payload(bytes))
+            },
+            Err(e) => {
+                log::error!("Set payload failed: {:?}", e);
+            },
+        }
         self
     }
 
-    pub fn event<E>(mut self, event: E) -> Self
+    pub fn event<Event>(mut self, event: Event) -> Self
     where
-        E: Eq + Hash + Debug + Clone + Display,
+        Event: Eq + Hash + Debug + Clone + Display,
     {
-        self.tester.set_event(event);
+        self.context.request = Some(ModuleRequest::new(event));
         self
     }
 
     pub fn sync_send(mut self) -> Self {
-        self.tester.sync_send();
+        let request = self.get_request();
+        let resp = EventDispatch::sync_send(self.dispatch(), request);
+        self.context.response = Some(resp);
         self
     }
 
-    pub fn parse<R>(mut self) -> R
+    pub fn parse<R>(self) -> R
     where
         R: FromBytes,
     {
-        self.tester.parse::<R>()
+        let response = self.get_response();
+        match response.parse::<R, E>() {
+            Ok(Ok(data)) => data,
+            Ok(Err(e)) => {
+                panic!("parse failed: {:?}", e)
+            },
+            Err(e) => panic!("Internal error: {:?}", e),
+        }
     }
 
-    pub fn error(mut self) -> <T as TesterTrait>::Error { self.tester.error() }
+    pub fn error(self) -> E {
+        let response = self.get_response();
+        assert_eq!(response.status_code, StatusCode::Err);
+        <Data<E>>::try_from(response.payload).unwrap().into_inner()
+    }
 
-    pub fn assert_error(mut self) -> Self {
-        self.tester.assert_error();
+    pub fn assert_error(self) -> Self {
+        // self.context.assert_error();
         self
     }
 
-    pub fn assert_success(mut self) -> Self {
-        self.tester.assert_success();
+    pub fn assert_success(self) -> Self {
+        // self.context.assert_success();
         self
     }
+
+    pub fn sdk(&self) -> FlowySDK { self.context.sdk.clone() }
+
+    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_request(&mut self) -> ModuleRequest { self.context.request.take().expect("must call event first") }
 }
 
-pub struct RandomUserTester<Error> {
-    context: TesterContext,
-    err_phantom: PhantomData<Error>,
+#[derive(Clone)]
+pub struct TestContext {
+    sdk: FlowyTestSDK,
+    request: Option<ModuleRequest>,
+    response: Option<EventResponse>,
 }
 
-impl<Error> RandomUserTester<Error>
-where
-    Error: FromBytes + Debug,
-{
-    pub fn new() -> Self {
+impl TestContext {
+    pub fn new(sdk: FlowyTestSDK) -> Self {
         Self {
-            context: TesterContext::default(),
-            err_phantom: PhantomData,
+            sdk,
+            request: None,
+            response: None,
         }
     }
 }
-
-impl<Error> TesterTrait for RandomUserTester<Error>
-where
-    Error: FromBytes + Debug,
-{
-    type Error = Error;
-
-    fn mut_context(&mut self) -> &mut TesterContext { &mut self.context }
-
-    fn context(&self) -> &TesterContext { &self.context }
-}
-
 pub struct SignUpContext {
     pub user_detail: UserDetail,
     pub password: String,

+ 6 - 6
rust-lib/flowy-test/src/helper.rs

@@ -1,13 +1,14 @@
 use bytes::Bytes;
-use flowy_dispatch::prelude::{DispatchError, EventDispatch, ModuleRequest, ToBytes};
+use flowy_dispatch::prelude::{EventDispatch, ModuleRequest, ToBytes};
 use flowy_infra::{kv::KV, uuid};
+
 use flowy_user::errors::{ErrorBuilder, ErrorCode, UserError};
 use flowy_workspace::{
     entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest, Workspace},
     errors::WorkspaceError,
     event::WorkspaceEvent::{CreateWorkspace, OpenWorkspace},
 };
-use std::{fs, path::PathBuf};
+use std::{fs, path::PathBuf, sync::Arc};
 
 pub fn root_dir() -> String {
     // https://doc.rust-lang.org/cargo/reference/environment-variables.html
@@ -35,7 +36,7 @@ const DEFAULT_WORKSPACE_NAME: &'static str = "My workspace";
 const DEFAULT_WORKSPACE_DESC: &'static str = "This is your first workspace";
 const DEFAULT_WORKSPACE: &'static str = "Default_Workspace";
 
-pub(crate) fn create_default_workspace_if_need(user_id: &str) -> Result<(), UserError> {
+pub(crate) fn create_default_workspace_if_need(dispatch: Arc<EventDispatch>, user_id: &str) -> Result<(), UserError> {
     let key = format!("{}{}", user_id, DEFAULT_WORKSPACE);
     if KV::get_bool(&key).unwrap_or(false) {
         return Err(ErrorBuilder::new(ErrorCode::DefaultWorkspaceAlreadyExist).build());
@@ -50,12 +51,11 @@ pub(crate) fn create_default_workspace_if_need(user_id: &str) -> Result<(), User
     .unwrap();
 
     let request = ModuleRequest::new(CreateWorkspace).payload(payload);
-    let result = EventDispatch::sync_send(request)
+    let result = EventDispatch::sync_send(dispatch.clone(), request)
         .parse::<Workspace, WorkspaceError>()
         .map_err(|e| ErrorBuilder::new(ErrorCode::CreateDefaultWorkspaceFailed).error(e).build())?;
 
     let workspace = result.map_err(|e| ErrorBuilder::new(ErrorCode::CreateDefaultWorkspaceFailed).error(e).build())?;
-
     let query: Bytes = QueryWorkspaceRequest {
         workspace_id: Some(workspace.id.clone()),
     }
@@ -63,7 +63,7 @@ pub(crate) fn create_default_workspace_if_need(user_id: &str) -> Result<(), User
     .unwrap();
 
     let request = ModuleRequest::new(OpenWorkspace).payload(query);
-    let _result = EventDispatch::sync_send(request)
+    let _result = EventDispatch::sync_send(dispatch.clone(), request)
         .parse::<Workspace, WorkspaceError>()
         .unwrap()
         .unwrap();

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

@@ -1,21 +1,34 @@
 pub mod builder;
 mod helper;
-mod tester;
+// pub mod workspace_builder;
 
-use crate::helper::root_dir;
+use crate::{builder::UserTestBuilder, helper::root_dir};
 use flowy_sdk::FlowySDK;
-use std::sync::Once;
 
 pub mod prelude {
-    pub use crate::{builder::*, helper::*};
+    pub use crate::{builder::*, helper::*, *};
     pub use flowy_dispatch::prelude::*;
 }
 
-static INIT: Once = Once::new();
-pub fn init_test_sdk() {
-    let root_dir = root_dir();
+pub type FlowyTestSDK = FlowySDK;
+
+#[derive(Clone)]
+pub struct TestSDKBuilder {
+    inner: FlowyTestSDK,
+}
+
+impl TestSDKBuilder {
+    pub fn new() -> Self { Self { inner: init_test_sdk() } }
 
-    INIT.call_once(|| {
-        FlowySDK::construct_with(&root_dir);
-    });
+    pub fn sign_up(self) -> Self {
+        let _ = UserTestBuilder::new(self.inner.clone()).sign_up();
+        self
+    }
+
+    pub fn build(self) -> FlowyTestSDK { self.inner }
+}
+
+pub fn init_test_sdk() -> FlowyTestSDK {
+    let root_dir = root_dir();
+    FlowySDK::new(&root_dir)
 }

+ 0 - 142
rust-lib/flowy-test/src/tester.rs

@@ -1,142 +0,0 @@
-use crate::{
-    helper::{login_password, random_email},
-    init_test_sdk,
-};
-use flowy_dispatch::prelude::*;
-pub use flowy_sdk::*;
-use flowy_user::{
-    errors::UserError,
-    event::UserEvent::{GetUserProfile, SignOut, SignUp},
-    prelude::*,
-};
-
-use crate::helper::login_email;
-use flowy_user::event::UserEvent::SignIn;
-use std::{
-    convert::TryFrom,
-    fmt::{Debug, Display},
-    hash::Hash,
-};
-
-#[allow(dead_code)]
-pub struct TesterContext {
-    request: Option<ModuleRequest>,
-    response: Option<EventResponse>,
-    status_code: StatusCode,
-}
-
-impl TesterContext {
-    pub fn new(email: String) -> Self { TesterContext::default() }
-}
-
-impl std::default::Default for TesterContext {
-    fn default() -> Self {
-        Self {
-            request: None,
-            status_code: StatusCode::Ok,
-            response: None,
-        }
-    }
-}
-
-pub trait TesterTrait {
-    type Error: FromBytes + Debug;
-
-    fn mut_context(&mut self) -> &mut TesterContext;
-
-    fn context(&self) -> &TesterContext;
-
-    fn assert_error(&mut self) { self.mut_context().status_code = StatusCode::Err; }
-
-    fn assert_success(&mut self) { self.mut_context().status_code = StatusCode::Ok; }
-
-    fn set_event<E>(&mut self, event: E)
-    where
-        E: Eq + Hash + Debug + Clone + Display,
-    {
-        self.mut_context().request = Some(ModuleRequest::new(event));
-    }
-
-    fn set_payload<P>(&mut self, payload: P)
-    where
-        P: ToBytes,
-    {
-        match payload.into_bytes() {
-            Ok(bytes) => {
-                let module_request = self.mut_context().request.take().unwrap();
-                self.mut_context().request = Some(module_request.payload(bytes));
-            },
-            Err(e) => {
-                log::error!("Set payload failed: {:?}", e);
-            },
-        }
-    }
-
-    fn sync_send(&mut self) {
-        let resp = EventDispatch::sync_send(self.mut_context().request.take().unwrap());
-        self.mut_context().response = Some(resp);
-    }
-
-    // TODO: support return Option<R>
-    fn parse<R>(&mut self) -> R
-    where
-        R: FromBytes,
-    {
-        let response = self.mut_context().response.clone().unwrap();
-        match response.parse::<R, Self::Error>() {
-            Ok(Ok(data)) => data,
-            Ok(Err(e)) => {
-                panic!("parse failed: {:?}", e)
-            },
-            Err(e) => panic!("Internal error: {:?}", e),
-        }
-    }
-
-    fn error(&mut self) -> Self::Error {
-        let response = self.mut_context().response.clone().unwrap();
-        assert_eq!(response.status_code, StatusCode::Err);
-        <Data<Self::Error>>::try_from(response.payload).unwrap().into_inner()
-    }
-
-    fn sign_up(&self) -> (UserDetail, String) {
-        let password = login_password();
-        let payload = SignUpRequest {
-            email: login_email(),
-            name: "app flowy".to_string(),
-            password: password.clone(),
-        }
-        .into_bytes()
-        .unwrap();
-
-        let request = ModuleRequest::new(SignUp).payload(payload);
-        let user_detail = EventDispatch::sync_send(request).parse::<UserDetail, UserError>().unwrap().unwrap();
-
-        (user_detail, password)
-    }
-
-    fn sign_in(&self) -> UserDetail {
-        let payload = SignInRequest {
-            email: login_email(),
-            password: login_password(),
-        }
-        .into_bytes()
-        .unwrap();
-
-        let request = ModuleRequest::new(SignIn).payload(payload);
-        let user_detail = EventDispatch::sync_send(request).parse::<UserDetail, UserError>().unwrap().unwrap();
-
-        user_detail
-    }
-
-    fn login_if_need(&self) -> UserDetail {
-        match EventDispatch::sync_send(ModuleRequest::new(GetUserProfile))
-            .parse::<UserDetail, UserError>()
-            .unwrap()
-        {
-            Ok(user_detail) => user_detail,
-            Err(_e) => self.sign_in(),
-        }
-    }
-
-    fn logout(&self) { let _ = EventDispatch::sync_send(ModuleRequest::new(SignOut)); }
-}

+ 60 - 0
rust-lib/flowy-test/src/workspace_builder.rs

@@ -0,0 +1,60 @@
+use super::builder::Builder;
+use crate::{builder::TestContext, helper::FlowyTestSDK};
+use flowy_workspace::{
+    entities::{app::App, view::View, workspace::*},
+    errors::WorkspaceError,
+    event::WorkspaceEvent::*,
+};
+
+pub enum WorkspaceAction {
+    CreateWorkspace(CreateWorkspaceRequest),
+    ReadWorkspace(QueryWorkspaceRequest),
+}
+
+type Inner = Builder<WorkspaceError>;
+
+pub struct WorkspaceTestBuilder {
+    workspace: Option<Workspace>,
+    app: Option<App>,
+    view: Option<View>,
+    inner: Builder<WorkspaceError>,
+}
+
+impl WorkspaceTestBuilder {
+    pub fn new(sdk: FlowyTestSDK) -> Self {
+        Self {
+            workspace: None,
+            app: None,
+            view: None,
+            inner: Builder::test(TestContext::new(sdk)),
+        }
+    }
+
+    pub fn run(mut self, actions: Vec<WorkspaceAction>) {
+        let inner = self.inner;
+        for action in actions {
+            match action {
+                WorkspaceAction::CreateWorkspace(request) => {
+                    let workspace = inner
+                        .clone()
+                        .event(CreateWorkspace)
+                        .request(request)
+                        .sync_send()
+                        .parse::<Workspace>();
+                    self.workspace = Some(workspace);
+                },
+                WorkspaceAction::ReadWorkspace(request) => {
+                    let mut repeated_workspace = inner
+                        .clone()
+                        .event(ReadWorkspaces)
+                        .request(request)
+                        .sync_send()
+                        .parse::<RepeatedWorkspace>();
+
+                    debug_assert_eq!(repeated_workspace.len(), 1, "Default workspace not found");
+                    repeated_workspace.drain(..1).collect::<Vec<Workspace>>().pop()
+                },
+            }
+        }
+    }
+}

+ 1 - 1
rust-lib/flowy-user/Cargo.toml

@@ -49,4 +49,4 @@ futures = "0.3.15"
 serial_test = "0.5.1"
 
 [features]
-mock_server = []
+http_server = []

+ 5 - 22
rust-lib/flowy-user/src/entities/user_detail.rs

@@ -17,7 +17,7 @@ impl std::default::Default for UserStatus {
     fn default() -> Self { UserStatus::Unknown }
 }
 
-#[derive(ProtoBuf, Default, Debug, PartialEq, Eq)]
+#[derive(ProtoBuf, Default, Debug, PartialEq, Eq, Clone)]
 pub struct UserDetail {
     #[pb(index = 1)]
     pub id: String,
@@ -135,36 +135,19 @@ impl TryInto<UpdateUserParams> for UpdateUserRequest {
 
         let name = match self.name {
             None => None,
-            Some(name) => Some(
-                UserName::parse(name)
-                    .map_err(|e| ErrorBuilder::new(e).build())?
-                    .0,
-            ),
+            Some(name) => Some(UserName::parse(name).map_err(|e| ErrorBuilder::new(e).build())?.0),
         };
 
         let email = match self.email {
             None => None,
-            Some(email) => Some(
-                UserEmail::parse(email)
-                    .map_err(|e| ErrorBuilder::new(e).build())?
-                    .0,
-            ),
+            Some(email) => Some(UserEmail::parse(email).map_err(|e| ErrorBuilder::new(e).build())?.0),
         };
 
         let password = match self.password {
             None => None,
-            Some(password) => Some(
-                UserPassword::parse(password)
-                    .map_err(|e| ErrorBuilder::new(e).build())?
-                    .0,
-            ),
+            Some(password) => Some(UserPassword::parse(password).map_err(|e| ErrorBuilder::new(e).build())?.0),
         };
 
-        Ok(UpdateUserParams {
-            id,
-            name,
-            email,
-            password,
-        })
+        Ok(UpdateUserParams { id, name, email, password })
     }
 }

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

@@ -20,9 +20,9 @@ pub trait UserServerAPI {
 }
 
 pub(crate) fn construct_user_server() -> Arc<dyn UserServerAPI + Send + Sync> {
-    if cfg!(feature = "mock_server") {
-        Arc::new(UserServerMock {})
-    } else {
+    if cfg!(feature = "http_server") {
         Arc::new(UserServer {})
+    } else {
+        Arc::new(UserServerMock {})
     }
 }

+ 1 - 1
rust-lib/flowy-user/src/services/user/database.rs

@@ -3,7 +3,7 @@ use flowy_database::{DBConnection, Database};
 use flowy_sqlite::ConnectionPool;
 use lazy_static::lazy_static;
 use once_cell::sync::Lazy;
-use parking_lot::{lock_api::RwLockReadGuard, Mutex, RawRwLock, RwLock};
+use parking_lot::{Mutex, RwLock};
 use std::{collections::HashMap, sync::Arc, time::Duration};
 lazy_static! {
     static ref DB: RwLock<Option<Database>> = RwLock::new(None);

+ 1 - 1
rust-lib/flowy-user/src/services/user/user_session.rs

@@ -170,7 +170,7 @@ impl UserSession {
     }
 
     fn set_session(&self, session: Option<Session>) -> Result<(), UserError> {
-        log::trace!("Update user session: {:?}", session);
+        log::debug!("Update user session: {:?}", session);
         match &session {
             None => KV::remove(SESSION_CACHE_KEY).map_err(|e| UserError::new(ErrorCode::SqlInternalError, &e))?,
             Some(session) => KV::set_str(SESSION_CACHE_KEY, session.clone().into()),

+ 13 - 15
rust-lib/flowy-user/tests/event/auth_test.rs

@@ -1,19 +1,13 @@
 use crate::helper::*;
-use flowy_test::builder::UserTestBuilder;
+use flowy_test::{builder::UserTestBuilder, init_test_sdk};
 use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
 use serial_test::*;
 
-#[test]
-#[serial]
-fn sign_up_success() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
-    log::info!("{:?}", user_detail);
-}
-
 #[test]
 #[serial]
 fn sign_up_with_invalid_email() {
     for email in invalid_email_test_case() {
+        let sdk = init_test_sdk();
         let request = SignUpRequest {
             email: email.to_string(),
             name: valid_name(),
@@ -21,7 +15,7 @@ fn sign_up_with_invalid_email() {
         };
 
         assert_eq!(
-            UserTestBuilder::new().event(SignUp).request(request).sync_send().error().code,
+            UserTestBuilder::new(sdk).event(SignUp).request(request).sync_send().error().code,
             ErrorCode::EmailFormatInvalid
         );
     }
@@ -30,28 +24,30 @@ 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 request = SignUpRequest {
             email: random_email(),
             name: valid_name(),
             password,
         };
 
-        UserTestBuilder::new().event(SignUp).request(request).sync_send().assert_error();
+        UserTestBuilder::new(sdk).event(SignUp).request(request).sync_send().assert_error();
     }
 }
 
 #[test]
 #[serial]
 fn sign_in_success() {
-    let context = UserTestBuilder::new().sign_up();
-    let _ = UserTestBuilder::new().event(SignOut).sync_send();
+    let sdk = init_test_sdk();
+    let context = UserTestBuilder::new(sdk.clone()).sign_up();
+    let _ = UserTestBuilder::new(sdk.clone()).event(SignOut).sync_send();
 
     let request = SignInRequest {
         email: context.user_detail.email,
         password: context.password,
     };
 
-    let response = UserTestBuilder::new()
+    let response = UserTestBuilder::new(sdk)
         .event(SignIn)
         .request(request)
         .sync_send()
@@ -63,13 +59,14 @@ fn sign_in_success() {
 #[serial]
 fn sign_in_with_invalid_email() {
     for email in invalid_email_test_case() {
+        let sdk = init_test_sdk();
         let request = SignInRequest {
             email: email.to_string(),
             password: login_password(),
         };
 
         assert_eq!(
-            UserTestBuilder::new().event(SignIn).request(request).sync_send().error().code,
+            UserTestBuilder::new(sdk).event(SignIn).request(request).sync_send().error().code,
             ErrorCode::EmailFormatInvalid
         );
     }
@@ -79,11 +76,12 @@ 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 request = SignInRequest {
             email: random_email(),
             password,
         };
 
-        UserTestBuilder::new().event(SignIn).request(request).sync_send().assert_error();
+        UserTestBuilder::new(sdk).event(SignIn).request(request).sync_send().assert_error();
     }
 }

+ 42 - 21
rust-lib/flowy-user/tests/event/user_profile_test.rs

@@ -1,22 +1,24 @@
 use crate::helper::*;
 use flowy_infra::uuid;
-use flowy_test::builder::UserTestBuilder;
+use flowy_test::{builder::UserTestBuilder, init_test_sdk};
 use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
 use serial_test::*;
 
 #[test]
 #[serial]
-fn user_status_get_failed() {
-    let tester = UserTestBuilder::new().event(GetUserProfile).assert_error().sync_send();
-    assert!(tester.get_user_detail().is_none())
+fn user_profile_get_failed() {
+    let sdk = init_test_sdk();
+    let result = UserTestBuilder::new(sdk).event(GetUserProfile).assert_error().sync_send();
+    assert!(result.user_detail().is_none())
 }
 
 #[test]
 #[serial]
-fn user_detail_get() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
+fn user_profile_get() {
+    let sdk = init_test_sdk();
+    let user_detail = UserTestBuilder::new(sdk.clone()).sign_up().user_detail;
 
-    let user_detail2 = UserTestBuilder::new().event(GetUserProfile).sync_send().parse::<UserDetail>();
+    let user_detail2 = UserTestBuilder::new(sdk).event(GetUserProfile).sync_send().parse::<UserDetail>();
 
     assert_eq!(user_detail, user_detail2);
 }
@@ -24,12 +26,13 @@ fn user_detail_get() {
 #[test]
 #[serial]
 fn user_update_with_name() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
+    let sdk = init_test_sdk();
+    let user_detail = UserTestBuilder::new(sdk.clone()).sign_up().user_detail;
     let new_name = "hello_world".to_owned();
     let request = UpdateUserRequest::new(&user_detail.id).name(&new_name);
-    let _ = UserTestBuilder::new().event(UpdateUser).request(request).sync_send();
+    let _ = UserTestBuilder::new(sdk.clone()).event(UpdateUser).request(request).sync_send();
 
-    let user_detail = UserTestBuilder::new()
+    let user_detail = UserTestBuilder::new(sdk)
         .event(GetUserProfile)
         .assert_error()
         .sync_send()
@@ -41,13 +44,14 @@ fn user_update_with_name() {
 #[test]
 #[serial]
 fn user_update_with_email() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
+    let sdk = init_test_sdk();
+    let user_detail = UserTestBuilder::new(sdk.clone()).sign_up().user_detail;
     let new_email = format!("{}@gmai.com", uuid());
     let request = UpdateUserRequest::new(&user_detail.id).email(&new_email);
 
-    let _ = UserTestBuilder::new().event(UpdateUser).request(request).sync_send();
+    let _ = UserTestBuilder::new(sdk.clone()).event(UpdateUser).request(request).sync_send();
 
-    let user_detail = UserTestBuilder::new()
+    let user_detail = UserTestBuilder::new(sdk)
         .event(GetUserProfile)
         .assert_error()
         .sync_send()
@@ -59,11 +63,12 @@ fn user_update_with_email() {
 #[test]
 #[serial]
 fn user_update_with_password() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
+    let sdk = init_test_sdk();
+    let user_detail = UserTestBuilder::new(sdk.clone()).sign_up().user_detail;
     let new_password = "H123world!".to_owned();
     let request = UpdateUserRequest::new(&user_detail.id).password(&new_password);
 
-    let _ = UserTestBuilder::new()
+    let _ = UserTestBuilder::new(sdk)
         .event(UpdateUser)
         .request(request)
         .sync_send()
@@ -73,11 +78,17 @@ fn user_update_with_password() {
 #[test]
 #[serial]
 fn user_update_with_invalid_email() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
+    let sdk = init_test_sdk();
+    let user_detail = UserTestBuilder::new(sdk.clone()).sign_up().user_detail;
     for email in invalid_email_test_case() {
         let request = UpdateUserRequest::new(&user_detail.id).email(&email);
         assert_eq!(
-            UserTestBuilder::new().event(UpdateUser).request(request).sync_send().error().code,
+            UserTestBuilder::new(sdk.clone())
+                .event(UpdateUser)
+                .request(request)
+                .sync_send()
+                .error()
+                .code,
             ErrorCode::EmailFormatInvalid
         );
     }
@@ -86,19 +97,29 @@ fn user_update_with_invalid_email() {
 #[test]
 #[serial]
 fn user_update_with_invalid_password() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
+    let sdk = init_test_sdk();
+    let user_detail = UserTestBuilder::new(sdk.clone()).sign_up().user_detail;
     for password in invalid_password_test_case() {
         let request = UpdateUserRequest::new(&user_detail.id).password(&password);
 
-        UserTestBuilder::new().event(UpdateUser).request(request).sync_send().assert_error();
+        UserTestBuilder::new(sdk.clone())
+            .event(UpdateUser)
+            .request(request)
+            .sync_send()
+            .assert_error();
     }
 }
 
 #[test]
 #[serial]
 fn user_update_with_invalid_name() {
-    let user_detail = UserTestBuilder::new().sign_up().user_detail;
+    let sdk = init_test_sdk();
+    let user_detail = UserTestBuilder::new(sdk.clone()).sign_up().user_detail;
     let request = UpdateUserRequest::new(&user_detail.id).name("");
 
-    UserTestBuilder::new().event(UpdateUser).request(request).sync_send().assert_error();
+    UserTestBuilder::new(sdk)
+        .event(UpdateUser)
+        .request(request)
+        .sync_send()
+        .assert_error();
 }

+ 1 - 1
rust-lib/flowy-workspace/Cargo.toml

@@ -37,4 +37,4 @@ serial_test = "0.5.1"
 
 
 [features]
-mock_server = []
+http_server = []

+ 1 - 5
rust-lib/flowy-workspace/src/handlers/workspace_handler.rs

@@ -1,8 +1,4 @@
-use crate::{
-    entities::workspace::*,
-    errors::{ErrorBuilder, ErrorCode, WorkspaceError},
-    services::WorkspaceController,
-};
+use crate::{entities::workspace::*, errors::WorkspaceError, services::WorkspaceController};
 use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
 use std::{convert::TryInto, sync::Arc};
 

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

@@ -51,9 +51,9 @@ pub trait WorkspaceServerAPI {
 }
 
 pub(crate) fn construct_workspace_server() -> Arc<dyn WorkspaceServerAPI + Send + Sync> {
-    if cfg!(feature = "mock_server") {
-        Arc::new(WorkspaceServerMock {})
-    } else {
+    if cfg!(feature = "http_server") {
         Arc::new(WorkspaceServer {})
+    } else {
+        Arc::new(WorkspaceServerMock {})
     }
 }

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

@@ -6,7 +6,7 @@ use crate::{
     services::{helper::spawn, server::Server, AppController},
     sql_tables::workspace::{WorkspaceSql, WorkspaceTable, WorkspaceTableChangeset},
 };
-use flowy_dispatch::prelude::DispatchFuture;
+
 use flowy_infra::kv::KV;
 
 use std::sync::Arc;

+ 29 - 30
rust-lib/flowy-workspace/tests/workspace/app_test.rs

@@ -1,6 +1,6 @@
 use crate::helper::*;
+use flowy_test::prelude::*;
 
-use flowy_test::builder::UserTestBuilder;
 use flowy_workspace::entities::{
     app::{QueryAppRequest, UpdateAppRequest},
     view::*,
@@ -8,40 +8,39 @@ use flowy_workspace::entities::{
 
 #[test]
 fn app_create() {
-    let _ = UserTestBuilder::new().sign_up();
-    let workspace = create_workspace("Workspace", "");
-    let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let workspace = create_workspace(&sdk, "Workspace", "");
+    let app = create_app(&sdk, "App A", "AppFlowy Github Project", &workspace.id);
     dbg!(&app);
 }
 
 #[test]
 #[should_panic]
 fn app_delete() {
-    let _ = UserTestBuilder::new().sign_up();
-
-    let workspace = create_workspace("Workspace", "");
-    let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
-    delete_app(&app.id);
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let workspace = create_workspace(&sdk, "Workspace", "");
+    let app = create_app(&sdk, "App A", "AppFlowy Github Project", &workspace.id);
+    delete_app(&sdk, &app.id);
     let query = QueryAppRequest::new(&app.id);
-    let _ = read_app(query);
+    let _ = read_app(&sdk, query);
 }
 
 #[test]
 fn app_read() {
-    let _ = UserTestBuilder::new().sign_up();
+    let sdk = TestSDKBuilder::new().sign_up().build();
 
-    let workspace = create_workspace("Workspace", "");
-    let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
+    let workspace = create_workspace(&sdk, "Workspace", "");
+    let app = create_app(&sdk, "App A", "AppFlowy Github Project", &workspace.id);
     let query = QueryAppRequest::new(&app.id);
-    let app_from_db = read_app(query);
+    let app_from_db = read_app(&sdk, query);
     assert_eq!(app_from_db, app);
 }
 
 #[test]
 fn app_create_with_view() {
-    let _a = UserTestBuilder::new().sign_up();
-    let workspace = create_workspace("Workspace", "");
-    let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let workspace = create_workspace(&sdk, "Workspace", "");
+    let app = create_app(&sdk, "App A", "AppFlowy Github Project", &workspace.id);
     let request_a = CreateViewRequest {
         belong_to_id: app.id.clone(),
         name: "View A".to_string(),
@@ -58,11 +57,11 @@ fn app_create_with_view() {
         view_type: ViewType::Doc,
     };
 
-    let view_a = create_view_with_request(request_a);
-    let view_b = create_view_with_request(request_b);
+    let view_a = create_view_with_request(&sdk, request_a);
+    let view_b = create_view_with_request(&sdk, request_b);
 
     let query = QueryAppRequest::new(&app.id).set_read_views(true);
-    let view_from_db = read_app(query);
+    let view_from_db = read_app(&sdk, query);
 
     assert_eq!(view_from_db.belongings[0], view_a);
     assert_eq!(view_from_db.belongings[1], view_b);
@@ -70,24 +69,24 @@ fn app_create_with_view() {
 
 #[test]
 fn app_set_trash_flag() {
-    let _ = UserTestBuilder::new().sign_up();
-    let app_id = create_app_with_trash_flag();
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let app_id = create_app_with_trash_flag(&sdk);
     let query = QueryAppRequest::new(&app_id).set_is_trash(true);
-    let _ = read_app(query);
+    let _ = read_app(&sdk, query);
 }
 
 #[test]
 #[should_panic]
 fn app_set_trash_flag_2() {
-    let _ = UserTestBuilder::new().sign_up();
-    let app_id = create_app_with_trash_flag();
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let app_id = create_app_with_trash_flag(&sdk);
     let query = QueryAppRequest::new(&app_id);
-    let _ = read_app(query);
+    let _ = read_app(&sdk, query);
 }
 
-fn create_app_with_trash_flag() -> String {
-    let workspace = create_workspace("Workspace", "");
-    let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
+fn create_app_with_trash_flag(sdk: &FlowyTestSDK) -> String {
+    let workspace = create_workspace(sdk, "Workspace", "");
+    let app = create_app(sdk, "App A", "AppFlowy Github Project", &workspace.id);
     let request = UpdateAppRequest {
         app_id: app.id.clone(),
         name: None,
@@ -95,7 +94,7 @@ fn create_app_with_trash_flag() -> String {
         color_style: None,
         is_trash: Some(true),
     };
-    update_app(request);
+    update_app(sdk, request);
 
     app.id
 }

+ 30 - 20
rust-lib/flowy-workspace/tests/workspace/helper.rs

@@ -1,4 +1,4 @@
-use flowy_test::builder::{UserTestBuilder, WorkspaceTestBuilder};
+use flowy_test::prelude::*;
 use flowy_workspace::{
     entities::{app::*, view::*, workspace::*},
     event::WorkspaceEvent::*,
@@ -11,13 +11,13 @@ pub(crate) fn invalid_workspace_name_test_case() -> Vec<String> {
         .collect::<Vec<_>>()
 }
 
-pub fn create_workspace(name: &str, desc: &str) -> Workspace {
+pub fn create_workspace(sdk: &FlowyTestSDK, name: &str, desc: &str) -> Workspace {
     let request = CreateWorkspaceRequest {
         name: name.to_owned(),
         desc: desc.to_owned(),
     };
 
-    let workspace = WorkspaceTestBuilder::new()
+    let workspace = WorkspaceTestBuilder::new(sdk.clone())
         .event(CreateWorkspace)
         .request(request)
         .sync_send()
@@ -25,8 +25,8 @@ pub fn create_workspace(name: &str, desc: &str) -> Workspace {
     workspace
 }
 
-pub fn read_workspaces(request: QueryWorkspaceRequest) -> Option<Workspace> {
-    let mut repeated_workspace = WorkspaceTestBuilder::new()
+pub fn read_workspaces(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest) -> Option<Workspace> {
+    let mut repeated_workspace = WorkspaceTestBuilder::new(sdk.clone())
         .event(ReadWorkspaces)
         .request(request)
         .sync_send()
@@ -36,7 +36,7 @@ pub fn read_workspaces(request: QueryWorkspaceRequest) -> Option<Workspace> {
     repeated_workspace.drain(..1).collect::<Vec<Workspace>>().pop()
 }
 
-pub fn create_app(name: &str, desc: &str, workspace_id: &str) -> App {
+pub 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(),
@@ -44,7 +44,7 @@ pub fn create_app(name: &str, desc: &str, workspace_id: &str) -> App {
         color_style: Default::default(),
     };
 
-    let app = WorkspaceTestBuilder::new()
+    let app = WorkspaceTestBuilder::new(sdk.clone())
         .event(CreateApp)
         .request(create_app_request)
         .sync_send()
@@ -52,18 +52,23 @@ pub fn create_app(name: &str, desc: &str, workspace_id: &str) -> App {
     app
 }
 
-pub fn delete_app(app_id: &str) {
+pub fn delete_app(sdk: &FlowyTestSDK, app_id: &str) {
     let delete_app_request = DeleteAppRequest {
         app_id: app_id.to_string(),
     };
 
-    WorkspaceTestBuilder::new().event(DeleteApp).request(delete_app_request).sync_send();
+    WorkspaceTestBuilder::new(sdk.clone())
+        .event(DeleteApp)
+        .request(delete_app_request)
+        .sync_send();
 }
 
-pub fn update_app(request: UpdateAppRequest) { WorkspaceTestBuilder::new().event(UpdateApp).request(request).sync_send(); }
+pub fn update_app(sdk: &FlowyTestSDK, request: UpdateAppRequest) {
+    WorkspaceTestBuilder::new(sdk.clone()).event(UpdateApp).request(request).sync_send();
+}
 
-pub fn read_app(request: QueryAppRequest) -> App {
-    let app = WorkspaceTestBuilder::new()
+pub fn read_app(sdk: &FlowyTestSDK, request: QueryAppRequest) -> App {
+    let app = WorkspaceTestBuilder::new(sdk.clone())
         .event(ReadApp)
         .request(request)
         .sync_send()
@@ -72,8 +77,8 @@ pub fn read_app(request: QueryAppRequest) -> App {
     app
 }
 
-pub fn create_view_with_request(request: CreateViewRequest) -> View {
-    let view = WorkspaceTestBuilder::new()
+pub fn create_view_with_request(sdk: &FlowyTestSDK, request: CreateViewRequest) -> View {
+    let view = WorkspaceTestBuilder::new(sdk.clone())
         .event(CreateView)
         .request(request)
         .sync_send()
@@ -82,8 +87,8 @@ pub fn create_view_with_request(request: CreateViewRequest) -> View {
     view
 }
 
-pub fn create_view(workspace_id: &str) -> View {
-    let app = create_app("App A", "AppFlowy Github Project", workspace_id);
+pub fn create_view(sdk: &FlowyTestSDK, workspace_id: &str) -> View {
+    let app = create_app(sdk, "App A", "AppFlowy Github Project", workspace_id);
     let request = CreateViewRequest {
         belong_to_id: app.id.clone(),
         name: "View A".to_string(),
@@ -92,13 +97,18 @@ pub fn create_view(workspace_id: &str) -> View {
         view_type: ViewType::Doc,
     };
 
-    create_view_with_request(request)
+    create_view_with_request(sdk, request)
 }
 
-pub fn update_view(request: UpdateViewRequest) { WorkspaceTestBuilder::new().event(UpdateView).request(request).sync_send(); }
+pub fn update_view(sdk: &FlowyTestSDK, request: UpdateViewRequest) {
+    WorkspaceTestBuilder::new(sdk.clone())
+        .event(UpdateView)
+        .request(request)
+        .sync_send();
+}
 
-pub fn read_view(request: QueryViewRequest) -> View {
-    WorkspaceTestBuilder::new()
+pub fn read_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> View {
+    WorkspaceTestBuilder::new(sdk.clone())
         .event(ReadView)
         .request(request)
         .sync_send()

+ 14 - 15
rust-lib/flowy-workspace/tests/workspace/view_test.rs

@@ -1,37 +1,36 @@
 use crate::helper::*;
 
-use flowy_test::builder::UserTestBuilder;
+use flowy_test::{FlowyTestSDK, TestSDKBuilder};
 use flowy_workspace::entities::view::*;
 
 #[test]
 fn view_create() {
-    let _ = UserTestBuilder::new().sign_up();
-
-    let workspace = create_workspace("Workspace", "");
-    let _ = create_view(&workspace.id);
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let workspace = create_workspace(&sdk, "Workspace", "");
+    let _ = create_view(&sdk, &workspace.id);
 }
 
 #[test]
 fn view_set_trash_flag() {
-    let _ = UserTestBuilder::new().sign_up();
-    let view_id = create_view_with_trash_flag();
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let view_id = create_view_with_trash_flag(&sdk);
     let query = QueryViewRequest::new(&view_id).set_is_trash(true);
-    let _ = read_view(query);
+    let _ = read_view(&sdk, query);
 }
 
 #[test]
 #[should_panic]
 fn view_set_trash_flag2() {
-    let _ = UserTestBuilder::new().sign_up();
+    let sdk = TestSDKBuilder::new().sign_up().build();
 
-    let view_id = create_view_with_trash_flag();
+    let view_id = create_view_with_trash_flag(&sdk);
     let query = QueryViewRequest::new(&view_id);
-    let _ = read_view(query);
+    let _ = read_view(&sdk, query);
 }
 
-fn create_view_with_trash_flag() -> String {
-    let workspace = create_workspace("Workspace", "");
-    let view = create_view(&workspace.id);
+fn create_view_with_trash_flag(sdk: &FlowyTestSDK) -> String {
+    let workspace = create_workspace(sdk, "Workspace", "");
+    let view = create_view(sdk, &workspace.id);
     let request = UpdateViewRequest {
         view_id: view.id.clone(),
         name: None,
@@ -39,7 +38,7 @@ fn create_view_with_trash_flag() -> String {
         thumbnail: None,
         is_trash: Some(true),
     };
-    update_view(request);
+    update_view(sdk, request);
 
     view.id
 }

+ 20 - 14
rust-lib/flowy-workspace/tests/workspace/workspace_test.rs

@@ -1,5 +1,5 @@
 use crate::helper::*;
-use flowy_test::builder::*;
+use flowy_test::{builder::*, TestSDKBuilder};
 use flowy_workspace::{
     entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest, RepeatedWorkspace},
     event::WorkspaceEvent::*,
@@ -7,14 +7,17 @@ use flowy_workspace::{
 };
 
 #[test]
-fn workspace_create_success() { let _ = create_workspace("First workspace", ""); }
+fn workspace_create_success() {
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let _ = create_workspace(&sdk, "First workspace", "");
+}
 
 #[test]
 fn workspace_read_all() {
-    let _ = UserTestBuilder::new().sign_up();
-    let _ = create_workspace("Workspace A", "workspace_create_and_then_get_workspace_success");
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let _ = create_workspace(&sdk, "Workspace A", "workspace_create_and_then_get_workspace_success");
 
-    let workspaces = WorkspaceTestBuilder::new()
+    let workspaces = WorkspaceTestBuilder::new(sdk.clone())
         .event(ReadWorkspaces)
         .request(QueryWorkspaceRequest::new())
         .sync_send()
@@ -25,29 +28,32 @@ fn workspace_read_all() {
 
 #[test]
 fn workspace_create_and_then_get_workspace() {
-    let workspace = create_workspace("Workspace A", "workspace_create_and_then_get_workspace_success");
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let workspace = create_workspace(&sdk, "Workspace A", "workspace_create_and_then_get_workspace_success");
     let request = QueryWorkspaceRequest::new().workspace_id(&workspace.id);
-    let workspace_from_db = read_workspaces(request).unwrap();
+    let workspace_from_db = read_workspaces(&sdk, request).unwrap();
     assert_eq!(workspace.name, workspace_from_db.name);
 }
 
 #[test]
 fn workspace_create_with_apps() {
-    let workspace = create_workspace("Workspace", "");
-    let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
+    let sdk = TestSDKBuilder::new().sign_up().build();
+    let workspace = create_workspace(&sdk, "Workspace", "");
+    let app = create_app(&sdk, "App A", "AppFlowy Github Project", &workspace.id);
 
     let request = QueryWorkspaceRequest::new().workspace_id(&workspace.id);
-    let workspace_from_db = read_workspaces(request).unwrap();
+    let workspace_from_db = read_workspaces(&sdk, request).unwrap();
     assert_eq!(&app, workspace_from_db.apps.first_or_crash());
 }
 
 #[test]
 fn workspace_create_with_invalid_name() {
+    let sdk = TestSDKBuilder::new().sign_up().build();
     for name in invalid_workspace_name_test_case() {
-        let _ = UserTestBuilder::new().sign_up();
+        let _ = UserTestBuilder::new(sdk.clone()).sign_up();
         let request = CreateWorkspaceRequest { name, desc: "".to_owned() };
         assert_eq!(
-            WorkspaceTestBuilder::new()
+            WorkspaceTestBuilder::new(sdk.clone())
                 .event(CreateWorkspace)
                 .request(request)
                 .sync_send()
@@ -60,11 +66,11 @@ fn workspace_create_with_invalid_name() {
 
 #[test]
 fn workspace_update_with_invalid_name() {
-    let _ = UserTestBuilder::new().sign_up();
+    let sdk = TestSDKBuilder::new().sign_up().build();
     for name in invalid_workspace_name_test_case() {
         let request = CreateWorkspaceRequest { name, desc: "".to_owned() };
         assert_eq!(
-            WorkspaceTestBuilder::new()
+            WorkspaceTestBuilder::new(sdk.clone())
                 .event(CreateWorkspace)
                 .request(request)
                 .sync_send()

+ 3 - 4
scripts/makefile/tests.toml

@@ -5,16 +5,15 @@ dependencies = ["rm_cache"]
 description = "Build desktop targets."
 script = '''
 cd rust-lib
-cargo test --features "flowy-workspace/mock_server","flowy-user/mock_server"
+cargo test
 '''
 
 
 [tasks.test_remote]
-dependencies = ["rm_cache", "run_server"]
-[tasks.run_test_remote_command]
+dependencies = ["rm_cache"]
 script = """
 cd rust-lib
-cargo test
+cargo test --features "flowy-workspace/http_server","flowy-user/http_server"
 """