support run tests on remote

This commit is contained in:
appflowy 2021-09-04 15:12:53 +08:00
parent a6346dffae
commit ccb51234c5
32 changed files with 529 additions and 522 deletions

View File

@ -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"}

View File

@ -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![];

View File

@ -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())
});
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() }),
}
},
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 })
}
}

View File

@ -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()) })
},
}

View File

@ -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)
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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()

View File

@ -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);
},
}
});

View File

@ -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" }

View File

@ -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) }
pub fn construct_with(root: &str) {
FlowySDK::init_log(root);
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);
let dispatch = Arc::new(init_dispatch(root));
let root = root.to_owned();
Self { root, dispatch }
}
fn init_log(directory: &str) {
if !INIT_LOG.load(Ordering::SeqCst) {
INIT_LOG.store(true, Ordering::SeqCst);
pub fn dispatch(&self) -> Arc<EventDispatch> { self.dispatch.clone() }
}
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_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);
let _ = flowy_log::Builder::new("flowy").local(directory).env_filter("info").build();
}
}
fn init_dispatch(root: &str) -> EventDispatch {
let config = ModuleConfig { root: root.to_owned() };
let dispatch = EventDispatch::construct(|| build_modules(config));
dispatch
}

View File

@ -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>>;
impl UserTestBuilder {
pub fn new() -> Self { Builder::test(Box::new(RandomUserTester::<UserError>::new())) }
pub type WorkspaceTestBuilder = Builder<WorkspaceError>;
impl WorkspaceTestBuilder {
pub fn new(sdk: FlowyTestSDK) -> Self { Builder::test(TestContext::new(sdk)) }
}
pub fn sign_up(mut self) -> SignUpContext {
let (user_detail, password) = self.tester.sign_up();
let _ = create_default_workspace_if_need(&user_detail.id);
pub type UserTestBuilder = Builder<UserError>;
impl UserTestBuilder {
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();
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,

View File

@ -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();

View File

@ -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;
INIT.call_once(|| {
FlowySDK::construct_with(&root_dir);
});
#[derive(Clone)]
pub struct TestSDKBuilder {
inner: FlowyTestSDK,
}
impl TestSDKBuilder {
pub fn new() -> Self { Self { inner: init_test_sdk() } }
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)
}

View File

@ -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)); }
}

View File

@ -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()
},
}
}
}
}

View File

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

View File

@ -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 })
}
}

View File

@ -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 {})
}
}

View File

@ -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);

View File

@ -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()),

View File

@ -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();
}
}

View File

@ -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();
}

View File

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

View File

@ -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};

View File

@ -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 {})
}
}

View File

@ -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;

View File

@ -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
}

View File

@ -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()

View File

@ -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
}

View File

@ -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()

View File

@ -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"
"""