mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix multithread test issue of database init
This commit is contained in:
parent
b66b3b3dc2
commit
d27e2b9475
@ -12,8 +12,8 @@ import 'package:protobuf/protobuf.dart' as $pb;
|
||||
class ErrorCode extends $pb.ProtobufEnum {
|
||||
static const ErrorCode Unknown = ErrorCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'Unknown');
|
||||
static const ErrorCode UserDatabaseInitFailed = ErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseInitFailed');
|
||||
static const ErrorCode UserDatabaseWriteLocked = ErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseWriteLocked');
|
||||
static const ErrorCode UserDatabaseReadLocked = ErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseReadLocked');
|
||||
static const ErrorCode AcquireWriteLockedFailed = ErrorCode._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AcquireWriteLockedFailed');
|
||||
static const ErrorCode AcquireReadLockedFailed = ErrorCode._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AcquireReadLockedFailed');
|
||||
static const ErrorCode UserDatabaseDidNotMatch = ErrorCode._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseDidNotMatch');
|
||||
static const ErrorCode UserDatabaseInternalError = ErrorCode._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserDatabaseInternalError');
|
||||
static const ErrorCode SqlInternalError = ErrorCode._(6, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'SqlInternalError');
|
||||
@ -41,8 +41,8 @@ class ErrorCode extends $pb.ProtobufEnum {
|
||||
static const $core.List<ErrorCode> values = <ErrorCode> [
|
||||
Unknown,
|
||||
UserDatabaseInitFailed,
|
||||
UserDatabaseWriteLocked,
|
||||
UserDatabaseReadLocked,
|
||||
AcquireWriteLockedFailed,
|
||||
AcquireReadLockedFailed,
|
||||
UserDatabaseDidNotMatch,
|
||||
UserDatabaseInternalError,
|
||||
SqlInternalError,
|
||||
|
@ -14,8 +14,8 @@ const ErrorCode$json = const {
|
||||
'2': const [
|
||||
const {'1': 'Unknown', '2': 0},
|
||||
const {'1': 'UserDatabaseInitFailed', '2': 1},
|
||||
const {'1': 'UserDatabaseWriteLocked', '2': 2},
|
||||
const {'1': 'UserDatabaseReadLocked', '2': 3},
|
||||
const {'1': 'AcquireWriteLockedFailed', '2': 2},
|
||||
const {'1': 'AcquireReadLockedFailed', '2': 3},
|
||||
const {'1': 'UserDatabaseDidNotMatch', '2': 4},
|
||||
const {'1': 'UserDatabaseInternalError', '2': 5},
|
||||
const {'1': 'SqlInternalError', '2': 6},
|
||||
@ -43,7 +43,7 @@ const ErrorCode$json = const {
|
||||
};
|
||||
|
||||
/// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||
final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSCwoHVW5rbm93bhAAEhoKFlVzZXJEYXRhYmFzZUluaXRGYWlsZWQQARIbChdVc2VyRGF0YWJhc2VXcml0ZUxvY2tlZBACEhoKFlVzZXJEYXRhYmFzZVJlYWRMb2NrZWQQAxIbChdVc2VyRGF0YWJhc2VEaWROb3RNYXRjaBAEEh0KGVVzZXJEYXRhYmFzZUludGVybmFsRXJyb3IQBRIUChBTcWxJbnRlcm5hbEVycm9yEAYSGAoURGF0YWJhc2VDb25uZWN0RXJyb3IQBxITCg9Vc2VyTm90TG9naW5ZZXQQChIXChNSZWFkQ3VycmVudElkRmFpbGVkEAsSGAoUV3JpdGVDdXJyZW50SWRGYWlsZWQQDBIQCgxFbWFpbElzRW1wdHkQFBIWChJFbWFpbEZvcm1hdEludmFsaWQQFRIWChJFbWFpbEFscmVhZHlFeGlzdHMQFhITCg9QYXNzd29yZElzRW1wdHkQHhITCg9QYXNzd29yZFRvb0xvbmcQHxIkCiBQYXNzd29yZENvbnRhaW5zRm9yYmlkQ2hhcmFjdGVycxAgEhkKFVBhc3N3b3JkRm9ybWF0SW52YWxpZBAhEhQKEFBhc3N3b3JkTm90TWF0Y2gQIhITCg9Vc2VyTmFtZVRvb0xvbmcQKBInCiNVc2VyTmFtZUNvbnRhaW5zRm9yYmlkZGVuQ2hhcmFjdGVycxApEhMKD1VzZXJOYW1lSXNFbXB0eRAqEhgKFFVzZXJXb3Jrc3BhY2VJbnZhbGlkEDISEQoNVXNlcklkSW52YWxpZBAzEiAKHENyZWF0ZURlZmF1bHRXb3Jrc3BhY2VGYWlsZWQQNBIgChxEZWZhdWx0V29ya3NwYWNlQWxyZWFkeUV4aXN0EDUSDwoLU2VydmVyRXJyb3IQZA==');
|
||||
final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSCwoHVW5rbm93bhAAEhoKFlVzZXJEYXRhYmFzZUluaXRGYWlsZWQQARIcChhBY3F1aXJlV3JpdGVMb2NrZWRGYWlsZWQQAhIbChdBY3F1aXJlUmVhZExvY2tlZEZhaWxlZBADEhsKF1VzZXJEYXRhYmFzZURpZE5vdE1hdGNoEAQSHQoZVXNlckRhdGFiYXNlSW50ZXJuYWxFcnJvchAFEhQKEFNxbEludGVybmFsRXJyb3IQBhIYChREYXRhYmFzZUNvbm5lY3RFcnJvchAHEhMKD1VzZXJOb3RMb2dpbllldBAKEhcKE1JlYWRDdXJyZW50SWRGYWlsZWQQCxIYChRXcml0ZUN1cnJlbnRJZEZhaWxlZBAMEhAKDEVtYWlsSXNFbXB0eRAUEhYKEkVtYWlsRm9ybWF0SW52YWxpZBAVEhYKEkVtYWlsQWxyZWFkeUV4aXN0cxAWEhMKD1Bhc3N3b3JkSXNFbXB0eRAeEhMKD1Bhc3N3b3JkVG9vTG9uZxAfEiQKIFBhc3N3b3JkQ29udGFpbnNGb3JiaWRDaGFyYWN0ZXJzECASGQoVUGFzc3dvcmRGb3JtYXRJbnZhbGlkECESFAoQUGFzc3dvcmROb3RNYXRjaBAiEhMKD1VzZXJOYW1lVG9vTG9uZxAoEicKI1VzZXJOYW1lQ29udGFpbnNGb3JiaWRkZW5DaGFyYWN0ZXJzECkSEwoPVXNlck5hbWVJc0VtcHR5ECoSGAoUVXNlcldvcmtzcGFjZUludmFsaWQQMhIRCg1Vc2VySWRJbnZhbGlkEDMSIAocQ3JlYXRlRGVmYXVsdFdvcmtzcGFjZUZhaWxlZBA0EiAKHERlZmF1bHRXb3Jrc3BhY2VBbHJlYWR5RXhpc3QQNRIPCgtTZXJ2ZXJFcnJvchBk');
|
||||
@$core.Deprecated('Use userErrorDescriptor instead')
|
||||
const UserError$json = const {
|
||||
'1': 'UserError',
|
||||
|
@ -24,6 +24,7 @@ dyn-clone = "1.0"
|
||||
derivative = "2.2.0"
|
||||
serde_json = {version = "1.0"}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
dashmap = "4.0"
|
||||
|
||||
#optional crate
|
||||
bincode = { version = "1.3", optional = true}
|
||||
|
@ -14,7 +14,7 @@ use std::{future::Future, sync::RwLock};
|
||||
use tokio::macros::support::{Pin, Poll};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref EVENT_DISPATCH: RwLock<Option<EventDispatch>> = RwLock::new(None);
|
||||
static ref EVENT_DISPATCH: RwLock<Option<EventDispatch>> = RwLock::new(None);
|
||||
}
|
||||
|
||||
pub struct EventDispatch {
|
||||
@ -31,10 +31,7 @@ impl EventDispatch {
|
||||
log::trace!("{}", module_info(&modules));
|
||||
let module_map = as_module_map(modules);
|
||||
let runtime = tokio_default_runtime().unwrap();
|
||||
let dispatch = EventDispatch {
|
||||
module_map,
|
||||
runtime,
|
||||
};
|
||||
let dispatch = EventDispatch { module_map, runtime };
|
||||
*(EVENT_DISPATCH.write().unwrap()) = Some(dispatch);
|
||||
}
|
||||
|
||||
@ -45,10 +42,7 @@ impl EventDispatch {
|
||||
EventDispatch::async_send_with_callback(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>(request: Req, callback: Callback) -> DispatchFuture<EventResponse>
|
||||
where
|
||||
Req: std::convert::Into<ModuleRequest>,
|
||||
Callback: FnOnce(EventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync,
|
||||
@ -74,10 +68,7 @@ impl EventDispatch {
|
||||
DispatchFuture {
|
||||
fut: Box::pin(async move {
|
||||
join_handle.await.unwrap_or_else(|e| {
|
||||
let error = InternalError::JoinError(format!(
|
||||
"EVENT_DISPATCH join error: {:?}",
|
||||
e
|
||||
));
|
||||
let error = InternalError::JoinError(format!("EVENT_DISPATCH join error: {:?}", e));
|
||||
error.as_response()
|
||||
})
|
||||
}),
|
||||
@ -94,9 +85,7 @@ impl EventDispatch {
|
||||
}
|
||||
|
||||
pub fn sync_send(request: ModuleRequest) -> EventResponse {
|
||||
futures::executor::block_on(async {
|
||||
EventDispatch::async_send_with_callback(request, |_| Box::pin(async {})).await
|
||||
})
|
||||
futures::executor::block_on(async { EventDispatch::async_send_with_callback(request, |_| Box::pin(async {})).await })
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,8 +109,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub type BoxFutureCallback =
|
||||
Box<dyn FnOnce(EventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync>;
|
||||
pub type BoxFutureCallback = Box<dyn FnOnce(EventResponse) -> BoxFuture<'static, ()> + 'static + Send + Sync>;
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug)]
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::helper::*;
|
||||
use flowy_test::builder::{TestBuilder, UserTestBuilder};
|
||||
|
||||
#[test]
|
||||
fn file_create_test() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
let doc_desc = create_doc("hello world", "flutter ❤️ rust", "123");
|
||||
dbg!(&doc_desc);
|
||||
|
||||
@ -11,6 +13,7 @@ fn file_create_test() {
|
||||
|
||||
#[test]
|
||||
fn file_update_text_test() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
let doc_desc = create_doc("hello world", "flutter ❤️ rust", "");
|
||||
dbg!(&doc_desc);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use flowy_test::builder::AnnieTestBuilder;
|
||||
use flowy_test::builder::{AnnieTestBuilder, DocTestBuilder, TestBuilder};
|
||||
|
||||
use flowy_document::{entities::doc::*, event::EditorEvent::*};
|
||||
use flowy_infra::uuid;
|
||||
@ -11,13 +11,12 @@ pub fn create_doc(name: &str, desc: &str, text: &str) -> DocInfo {
|
||||
text: text.to_owned(),
|
||||
};
|
||||
|
||||
let doc_desc = AnnieTestBuilder::new()
|
||||
let doc = DocTestBuilder::new()
|
||||
.event(CreateDoc)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.parse::<DocInfo>();
|
||||
|
||||
doc_desc
|
||||
doc
|
||||
}
|
||||
|
||||
pub fn save_doc(desc: &DocInfo, content: &str) {
|
||||
@ -28,34 +27,31 @@ pub fn save_doc(desc: &DocInfo, content: &str) {
|
||||
text: Some(content.to_owned()),
|
||||
};
|
||||
|
||||
let _ = AnnieTestBuilder::new()
|
||||
.event(UpdateDoc)
|
||||
.request(request)
|
||||
.sync_send();
|
||||
let _ = DocTestBuilder::new().event(UpdateDoc).request(request).sync_send();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn read_doc(doc_id: &str) -> DocInfo {
|
||||
let request = QueryDocRequest {
|
||||
doc_id: doc_id.to_string(),
|
||||
};
|
||||
// #[allow(dead_code)]
|
||||
// pub fn read_doc(doc_id: &str) -> DocInfo {
|
||||
// let request = QueryDocRequest {
|
||||
// doc_id: doc_id.to_string(),
|
||||
// };
|
||||
//
|
||||
// let doc = AnnieTestBuilder::new()
|
||||
// .event(ReadDocInfo)
|
||||
// .request(request)
|
||||
// .sync_send()
|
||||
// .parse::<DocInfo>();
|
||||
//
|
||||
// doc
|
||||
// }
|
||||
|
||||
let doc = AnnieTestBuilder::new()
|
||||
.event(ReadDocInfo)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.parse::<DocInfo>();
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
pub fn read_doc_data(doc_id: &str, path: &str) -> DocData {
|
||||
pub(crate) fn read_doc_data(doc_id: &str, path: &str) -> DocData {
|
||||
let request = QueryDocDataRequest {
|
||||
doc_id: doc_id.to_string(),
|
||||
path: path.to_string(),
|
||||
};
|
||||
|
||||
let doc = AnnieTestBuilder::new()
|
||||
let doc = DocTestBuilder::new()
|
||||
.event(ReadDocData)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
|
@ -11,6 +11,7 @@ flowy-dispatch = { path = "../flowy-dispatch"}
|
||||
flowy-user = { path = "../flowy-user"}
|
||||
flowy-workspace = { path = "../flowy-workspace"}
|
||||
flowy-infra = { path = "../flowy-infra"}
|
||||
flowy-document = { path = "../flowy-document"}
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
bincode = { version = "1.3"}
|
||||
|
@ -6,45 +6,32 @@ use std::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
helper::{create_default_workspace_if_need, valid_email},
|
||||
helper::{create_default_workspace_if_need, login_email, login_password},
|
||||
init_test_sdk,
|
||||
tester::{TesterContext, TesterTrait},
|
||||
};
|
||||
use flowy_document::errors::DocError;
|
||||
use flowy_user::errors::UserError;
|
||||
use flowy_workspace::errors::WorkspaceError;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub type AnnieTestBuilder = Builder<FlowyAnnie<WorkspaceError>>;
|
||||
impl AnnieTestBuilder {
|
||||
pub fn new() -> Self {
|
||||
let mut builder = Builder::test(Box::new(FlowyAnnie::<WorkspaceError>::new()));
|
||||
builder.setup_default_workspace();
|
||||
builder
|
||||
}
|
||||
|
||||
pub fn setup_default_workspace(&mut self) {
|
||||
self.login_if_need();
|
||||
let user_id = self.user_detail.as_ref().unwrap().id.clone();
|
||||
let _ = create_default_workspace_if_need(&user_id);
|
||||
}
|
||||
pub type WorkspaceTestBuilder = Builder<RandomUserTester<WorkspaceError>>;
|
||||
impl WorkspaceTestBuilder {
|
||||
pub fn new() -> Self { Builder::test(Box::new(RandomUserTester::<WorkspaceError>::new())) }
|
||||
}
|
||||
pub type TestBuilder = Builder<RandomUserTester<UserError>>;
|
||||
impl TestBuilder {
|
||||
|
||||
pub type DocTestBuilder = Builder<RandomUserTester<DocError>>;
|
||||
impl DocTestBuilder {
|
||||
pub fn new() -> Self { Builder::test(Box::new(RandomUserTester::<DocError>::new())) }
|
||||
}
|
||||
|
||||
pub type UserTestBuilder = Builder<RandomUserTester<UserError>>;
|
||||
impl UserTestBuilder {
|
||||
pub fn new() -> Self { Builder::test(Box::new(RandomUserTester::<UserError>::new())) }
|
||||
}
|
||||
|
||||
pub struct Builder<T: TesterTrait> {
|
||||
pub tester: Box<T>,
|
||||
pub user_detail: Option<UserDetail>,
|
||||
}
|
||||
|
||||
impl<T> Builder<T>
|
||||
where
|
||||
T: TesterTrait,
|
||||
{
|
||||
fn test(tester: Box<T>) -> Self { Self { tester, user_detail: None } }
|
||||
|
||||
pub fn sign_up(self) -> SignUpContext {
|
||||
pub fn sign_up(mut self) -> SignUpContext {
|
||||
let (user_detail, password) = self.tester.sign_up();
|
||||
let _ = create_default_workspace_if_need(&user_detail.id);
|
||||
SignUpContext { user_detail, password }
|
||||
}
|
||||
|
||||
@ -59,6 +46,23 @@ where
|
||||
self.user_detail = Some(user_detail);
|
||||
}
|
||||
|
||||
pub fn get_user_detail(&self) -> &Option<UserDetail> { &self.user_detail }
|
||||
}
|
||||
|
||||
pub struct Builder<T: TesterTrait> {
|
||||
pub tester: Box<T>,
|
||||
user_detail: Option<UserDetail>,
|
||||
}
|
||||
|
||||
impl<T> Builder<T>
|
||||
where
|
||||
T: TesterTrait,
|
||||
{
|
||||
fn test(tester: Box<T>) -> Self {
|
||||
init_test_sdk();
|
||||
Self { tester, user_detail: None }
|
||||
}
|
||||
|
||||
pub fn request<P>(mut self, request: P) -> Self
|
||||
where
|
||||
P: ToBytes,
|
||||
@ -128,34 +132,6 @@ where
|
||||
fn context(&self) -> &TesterContext { &self.context }
|
||||
}
|
||||
|
||||
pub struct FlowyAnnie<Error> {
|
||||
context: TesterContext,
|
||||
err_phantom: PhantomData<Error>,
|
||||
}
|
||||
|
||||
impl<Error> FlowyAnnie<Error>
|
||||
where
|
||||
Error: FromBytes + Debug,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
context: TesterContext::new(valid_email()),
|
||||
err_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Error> TesterTrait for FlowyAnnie<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,
|
||||
|
@ -4,6 +4,7 @@ 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};
|
||||
@ -26,9 +27,9 @@ pub fn root_dir() -> String {
|
||||
|
||||
pub fn random_email() -> String { format!("{}@appflowy.io", uuid()) }
|
||||
|
||||
pub fn valid_email() -> String { "annie@appflowy.io".to_string() }
|
||||
pub fn login_email() -> String { "annie@appflowy.io".to_string() }
|
||||
|
||||
pub fn valid_password() -> String { "HelloWorld!123".to_string() }
|
||||
pub fn login_password() -> String { "HelloWorld!123".to_string() }
|
||||
|
||||
const DEFAULT_WORKSPACE_NAME: &'static str = "My workspace";
|
||||
const DEFAULT_WORKSPACE_DESC: &'static str = "This is your first workspace";
|
||||
@ -50,7 +51,7 @@ pub(crate) fn create_default_workspace_if_need(user_id: &str) -> Result<(), User
|
||||
|
||||
let request = ModuleRequest::new(CreateWorkspace).payload(payload);
|
||||
let result = EventDispatch::sync_send(request)
|
||||
.parse::<Workspace, DispatchError>()
|
||||
.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())?;
|
||||
@ -63,7 +64,7 @@ pub(crate) fn create_default_workspace_if_need(user_id: &str) -> Result<(), User
|
||||
|
||||
let request = ModuleRequest::new(OpenWorkspace).payload(query);
|
||||
let _result = EventDispatch::sync_send(request)
|
||||
.parse::<Workspace, DispatchError>()
|
||||
.parse::<Workspace, WorkspaceError>()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
|
@ -7,10 +7,7 @@ use flowy_sdk::FlowySDK;
|
||||
use std::sync::Once;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{
|
||||
builder::{TestBuilder, *},
|
||||
helper::*,
|
||||
};
|
||||
pub use crate::{builder::*, helper::*};
|
||||
pub use flowy_dispatch::prelude::*;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
helper::{random_email, valid_password},
|
||||
helper::{login_password, random_email},
|
||||
init_test_sdk,
|
||||
};
|
||||
use flowy_dispatch::prelude::*;
|
||||
@ -10,6 +10,7 @@ use flowy_user::{
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::helper::login_email;
|
||||
use flowy_user::event::UserEvent::SignIn;
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
@ -22,15 +23,10 @@ pub struct TesterContext {
|
||||
request: Option<ModuleRequest>,
|
||||
response: Option<EventResponse>,
|
||||
status_code: StatusCode,
|
||||
user_email: String,
|
||||
}
|
||||
|
||||
impl TesterContext {
|
||||
pub fn new(email: String) -> Self {
|
||||
let mut ctx = TesterContext::default();
|
||||
ctx.user_email = email;
|
||||
ctx
|
||||
}
|
||||
pub fn new(email: String) -> Self { TesterContext::default() }
|
||||
}
|
||||
|
||||
impl std::default::Default for TesterContext {
|
||||
@ -39,7 +35,6 @@ impl std::default::Default for TesterContext {
|
||||
request: None,
|
||||
status_code: StatusCode::Ok,
|
||||
response: None,
|
||||
user_email: random_email(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,7 +54,6 @@ pub trait TesterTrait {
|
||||
where
|
||||
E: Eq + Hash + Debug + Clone + Display,
|
||||
{
|
||||
init_test_sdk();
|
||||
self.mut_context().request = Some(ModuleRequest::new(event));
|
||||
}
|
||||
|
||||
@ -101,16 +95,13 @@ pub trait TesterTrait {
|
||||
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()
|
||||
<Data<Self::Error>>::try_from(response.payload).unwrap().into_inner()
|
||||
}
|
||||
|
||||
fn sign_up(&self) -> (UserDetail, String) {
|
||||
init_test_sdk();
|
||||
let password = valid_password();
|
||||
let password = login_password();
|
||||
let payload = SignUpRequest {
|
||||
email: self.context().user_email.clone(),
|
||||
email: random_email(),
|
||||
name: "app flowy".to_string(),
|
||||
password: password.clone(),
|
||||
}
|
||||
@ -118,34 +109,26 @@ pub trait TesterTrait {
|
||||
.unwrap();
|
||||
|
||||
let request = ModuleRequest::new(SignUp).payload(payload);
|
||||
let user_detail = EventDispatch::sync_send(request)
|
||||
.parse::<UserDetail, UserError>()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let user_detail = EventDispatch::sync_send(request).parse::<UserDetail, UserError>().unwrap().unwrap();
|
||||
|
||||
(user_detail, password)
|
||||
}
|
||||
|
||||
fn sign_in(&self) -> UserDetail {
|
||||
init_test_sdk();
|
||||
let payload = SignInRequest {
|
||||
email: self.context().user_email.clone(),
|
||||
password: valid_password(),
|
||||
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();
|
||||
let user_detail = EventDispatch::sync_send(request).parse::<UserDetail, UserError>().unwrap().unwrap();
|
||||
|
||||
user_detail
|
||||
}
|
||||
|
||||
fn login_if_need(&self) -> UserDetail {
|
||||
init_test_sdk();
|
||||
match EventDispatch::sync_send(ModuleRequest::new(GetUserProfile))
|
||||
.parse::<UserDetail, UserError>()
|
||||
.unwrap()
|
||||
@ -155,8 +138,5 @@ pub trait TesterTrait {
|
||||
}
|
||||
}
|
||||
|
||||
fn logout(&self) {
|
||||
init_test_sdk();
|
||||
let _ = EventDispatch::sync_send(ModuleRequest::new(SignOut));
|
||||
}
|
||||
fn logout(&self) { let _ = EventDispatch::sync_send(ModuleRequest::new(SignOut)); }
|
||||
}
|
||||
|
@ -27,10 +27,10 @@ pub enum ErrorCode {
|
||||
Unknown = 0,
|
||||
#[display(fmt = "Database init failed")]
|
||||
UserDatabaseInitFailed = 1,
|
||||
#[display(fmt = "Get database write lock failed")]
|
||||
UserDatabaseWriteLocked = 2,
|
||||
#[display(fmt = "Get database read lock failed")]
|
||||
UserDatabaseReadLocked = 3,
|
||||
#[display(fmt = "Acquire database write lock failed")]
|
||||
AcquireWriteLockedFailed = 2,
|
||||
#[display(fmt = "Acquire database read lock failed")]
|
||||
AcquireReadLockedFailed = 3,
|
||||
#[display(fmt = "Opening database is not belonging to the current user")]
|
||||
UserDatabaseDidNotMatch = 4,
|
||||
#[display(fmt = "Database internal error")]
|
||||
|
@ -4,13 +4,9 @@ use std::{convert::TryInto, sync::Arc};
|
||||
|
||||
// tracing instrument 👉🏻 https://docs.rs/tracing/0.1.26/tracing/attr.instrument.html
|
||||
#[tracing::instrument(name = "sign_in", skip(data, session), fields(email = %data.email))]
|
||||
pub async fn sign_in(
|
||||
data: Data<SignInRequest>,
|
||||
session: Unit<Arc<UserSession>>,
|
||||
) -> DataResult<UserDetail, UserError> {
|
||||
pub async fn sign_in(data: Data<SignInRequest>, session: Unit<Arc<UserSession>>) -> DataResult<UserDetail, UserError> {
|
||||
let params: SignInParams = data.into_inner().try_into()?;
|
||||
let user = session.sign_in(params).await?;
|
||||
let user_detail = UserDetail::from(user);
|
||||
let user_detail = session.sign_in(params).await?;
|
||||
data_result(user_detail)
|
||||
}
|
||||
|
||||
@ -22,12 +18,9 @@ pub async fn sign_in(
|
||||
name = %data.name,
|
||||
)
|
||||
)]
|
||||
pub async fn sign_up(
|
||||
data: Data<SignUpRequest>,
|
||||
session: Unit<Arc<UserSession>>,
|
||||
) -> DataResult<UserDetail, UserError> {
|
||||
pub async fn sign_up(data: Data<SignUpRequest>, session: Unit<Arc<UserSession>>) -> DataResult<UserDetail, UserError> {
|
||||
let params: SignUpParams = data.into_inner().try_into()?;
|
||||
let user = session.sign_up(params).await?;
|
||||
let user_detail = UserDetail::from(user);
|
||||
let user_detail = session.sign_up(params).await?;
|
||||
|
||||
data_result(user_detail)
|
||||
}
|
||||
|
@ -217,8 +217,8 @@ impl ::protobuf::reflect::ProtobufValue for UserError {
|
||||
pub enum ErrorCode {
|
||||
Unknown = 0,
|
||||
UserDatabaseInitFailed = 1,
|
||||
UserDatabaseWriteLocked = 2,
|
||||
UserDatabaseReadLocked = 3,
|
||||
AcquireWriteLockedFailed = 2,
|
||||
AcquireReadLockedFailed = 3,
|
||||
UserDatabaseDidNotMatch = 4,
|
||||
UserDatabaseInternalError = 5,
|
||||
SqlInternalError = 6,
|
||||
@ -253,8 +253,8 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(ErrorCode::Unknown),
|
||||
1 => ::std::option::Option::Some(ErrorCode::UserDatabaseInitFailed),
|
||||
2 => ::std::option::Option::Some(ErrorCode::UserDatabaseWriteLocked),
|
||||
3 => ::std::option::Option::Some(ErrorCode::UserDatabaseReadLocked),
|
||||
2 => ::std::option::Option::Some(ErrorCode::AcquireWriteLockedFailed),
|
||||
3 => ::std::option::Option::Some(ErrorCode::AcquireReadLockedFailed),
|
||||
4 => ::std::option::Option::Some(ErrorCode::UserDatabaseDidNotMatch),
|
||||
5 => ::std::option::Option::Some(ErrorCode::UserDatabaseInternalError),
|
||||
6 => ::std::option::Option::Some(ErrorCode::SqlInternalError),
|
||||
@ -286,8 +286,8 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
|
||||
static values: &'static [ErrorCode] = &[
|
||||
ErrorCode::Unknown,
|
||||
ErrorCode::UserDatabaseInitFailed,
|
||||
ErrorCode::UserDatabaseWriteLocked,
|
||||
ErrorCode::UserDatabaseReadLocked,
|
||||
ErrorCode::AcquireWriteLockedFailed,
|
||||
ErrorCode::AcquireReadLockedFailed,
|
||||
ErrorCode::UserDatabaseDidNotMatch,
|
||||
ErrorCode::UserDatabaseInternalError,
|
||||
ErrorCode::SqlInternalError,
|
||||
@ -341,10 +341,10 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\x0cerrors.proto\"=\n\tUserError\x12\x1e\n\x04code\x18\x01\x20\x01(\
|
||||
\x0e2\n.ErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*\
|
||||
\xb9\x05\n\tErrorCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16UserDataba\
|
||||
seInitFailed\x10\x01\x12\x1b\n\x17UserDatabaseWriteLocked\x10\x02\x12\
|
||||
\x1a\n\x16UserDatabaseReadLocked\x10\x03\x12\x1b\n\x17UserDatabaseDidNot\
|
||||
Match\x10\x04\x12\x1d\n\x19UserDatabaseInternalError\x10\x05\x12\x14\n\
|
||||
\xbb\x05\n\tErrorCode\x12\x0b\n\x07Unknown\x10\0\x12\x1a\n\x16UserDataba\
|
||||
seInitFailed\x10\x01\x12\x1c\n\x18AcquireWriteLockedFailed\x10\x02\x12\
|
||||
\x1b\n\x17AcquireReadLockedFailed\x10\x03\x12\x1b\n\x17UserDatabaseDidNo\
|
||||
tMatch\x10\x04\x12\x1d\n\x19UserDatabaseInternalError\x10\x05\x12\x14\n\
|
||||
\x10SqlInternalError\x10\x06\x12\x18\n\x14DatabaseConnectError\x10\x07\
|
||||
\x12\x13\n\x0fUserNotLoginYet\x10\n\x12\x17\n\x13ReadCurrentIdFailed\x10\
|
||||
\x0b\x12\x18\n\x14WriteCurrentIdFailed\x10\x0c\x12\x10\n\x0cEmailIsEmpty\
|
||||
@ -369,10 +369,10 @@ static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\x04\x0b\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\x0e\x0f\n\x0b\n\x04\x05\
|
||||
\0\x02\x01\x12\x03\x08\x04\x1f\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\
|
||||
\x04\x1a\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x08\x1d\x1e\n\x0b\n\x04\
|
||||
\x05\0\x02\x02\x12\x03\t\x04\x20\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\
|
||||
\x04\x1b\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\x1e\x1f\n\x0b\n\x04\x05\
|
||||
\0\x02\x03\x12\x03\n\x04\x1f\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\
|
||||
\x1a\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\x1d\x1e\n\x0b\n\x04\x05\0\
|
||||
\x05\0\x02\x02\x12\x03\t\x04!\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\
|
||||
\x04\x1c\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\x1f\x20\n\x0b\n\x04\x05\
|
||||
\0\x02\x03\x12\x03\n\x04\x20\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\
|
||||
\x1b\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\x1e\x1f\n\x0b\n\x04\x05\0\
|
||||
\x02\x04\x12\x03\x0b\x04\x20\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\
|
||||
\x04\x1b\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x1e\x1f\n\x0b\n\x04\
|
||||
\x05\0\x02\x05\x12\x03\x0c\x04\"\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\
|
||||
|
@ -7,8 +7,8 @@ message UserError {
|
||||
enum ErrorCode {
|
||||
Unknown = 0;
|
||||
UserDatabaseInitFailed = 1;
|
||||
UserDatabaseWriteLocked = 2;
|
||||
UserDatabaseReadLocked = 3;
|
||||
AcquireWriteLockedFailed = 2;
|
||||
AcquireReadLockedFailed = 3;
|
||||
UserDatabaseDidNotMatch = 4;
|
||||
UserDatabaseInternalError = 5;
|
||||
SqlInternalError = 6;
|
||||
|
@ -15,21 +15,22 @@ impl UserServerAPI for UserServerMock {
|
||||
let uid = uuid();
|
||||
ResultFuture::new(async move {
|
||||
Ok(SignUpResponse {
|
||||
user_id: uid,
|
||||
user_id: uid.clone(),
|
||||
name: params.name,
|
||||
email: params.email,
|
||||
token: "fake token".to_owned(),
|
||||
token: uid,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_in(&self, params: SignInParams) -> ResultFuture<SignInResponse, UserError> {
|
||||
let user_id = uuid();
|
||||
ResultFuture::new(async {
|
||||
Ok(SignInResponse {
|
||||
uid: uuid(),
|
||||
uid: user_id.clone(),
|
||||
name: "fake name".to_owned(),
|
||||
email: params.email,
|
||||
token: "fake token".to_string(),
|
||||
token: user_id,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -3,12 +3,8 @@ use flowy_database::{DBConnection, Database};
|
||||
use flowy_sqlite::ConnectionPool;
|
||||
use lazy_static::lazy_static;
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use parking_lot::{lock_api::RwLockReadGuard, Mutex, RawRwLock, RwLock};
|
||||
use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
lazy_static! {
|
||||
static ref DB: RwLock<Option<Database>> = RwLock::new(None);
|
||||
}
|
||||
@ -18,46 +14,42 @@ pub(crate) struct UserDB {
|
||||
}
|
||||
|
||||
impl UserDB {
|
||||
pub(crate) fn new(db_dir: &str) -> Self {
|
||||
Self {
|
||||
db_dir: db_dir.to_owned(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn new(db_dir: &str) -> Self { Self { db_dir: db_dir.to_owned() } }
|
||||
|
||||
fn open_user_db(&self, user_id: &str) -> Result<(), UserError> {
|
||||
if user_id.is_empty() {
|
||||
return Err(ErrorBuilder::new(ErrorCode::UserDatabaseInitFailed)
|
||||
.msg("user id is empty")
|
||||
.build());
|
||||
return Err(ErrorBuilder::new(ErrorCode::UserDatabaseInitFailed).msg("user id is empty").build());
|
||||
}
|
||||
|
||||
log::info!("open user db {}", user_id);
|
||||
let dir = format!("{}/{}", self.db_dir, user_id);
|
||||
let db = flowy_database::init(&dir).map_err(|e| {
|
||||
log::error!("flowy_database::init failed, {:?}", e);
|
||||
ErrorBuilder::new(ErrorCode::UserDatabaseInitFailed)
|
||||
.error(e)
|
||||
.build()
|
||||
log::error!("init user db failed, {:?}, user_id: {}", e, user_id);
|
||||
ErrorBuilder::new(ErrorCode::UserDatabaseInitFailed).error(e).build()
|
||||
})?;
|
||||
|
||||
let mut db_map = DB_MAP.write().map_err(|e| {
|
||||
ErrorBuilder::new(ErrorCode::UserDatabaseWriteLocked)
|
||||
.error(e)
|
||||
.build()
|
||||
})?;
|
||||
|
||||
db_map.insert(user_id.to_owned(), db);
|
||||
Ok(())
|
||||
match DB_MAP.try_write_for(Duration::from_millis(300)) {
|
||||
None => Err(ErrorBuilder::new(ErrorCode::AcquireWriteLockedFailed)
|
||||
.msg(format!("Open user db failed"))
|
||||
.build()),
|
||||
Some(mut write_guard) => {
|
||||
write_guard.insert(user_id.to_owned(), db);
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn close_user_db(&self, user_id: &str) -> Result<(), UserError> {
|
||||
let mut db_map = DB_MAP.write().map_err(|e| {
|
||||
ErrorBuilder::new(ErrorCode::UserDatabaseWriteLocked)
|
||||
.msg(format!("Close user db failed. {:?}", e))
|
||||
.build()
|
||||
})?;
|
||||
set_user_db_init(false, user_id);
|
||||
db_map.remove(user_id);
|
||||
Ok(())
|
||||
match DB_MAP.try_write_for(Duration::from_millis(300)) {
|
||||
None => Err(ErrorBuilder::new(ErrorCode::AcquireWriteLockedFailed)
|
||||
.msg(format!("Close user db failed"))
|
||||
.build()),
|
||||
Some(mut write_guard) => {
|
||||
set_user_db_init(false, user_id);
|
||||
write_guard.remove(user_id);
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_connection(&self, user_id: &str) -> Result<DBConnection, UserError> {
|
||||
@ -66,22 +58,28 @@ impl UserDB {
|
||||
}
|
||||
|
||||
pub(crate) fn get_pool(&self, user_id: &str) -> Result<Arc<ConnectionPool>, UserError> {
|
||||
if !is_user_db_init(user_id) {
|
||||
let _ = self.open_user_db(user_id)?;
|
||||
set_user_db_init(true, user_id);
|
||||
// Opti: INIT_LOCK try to lock the INIT_RECORD accesses. Because the write guard
|
||||
// can not nested in the read guard that will cause the deadlock.
|
||||
match INIT_LOCK.try_lock_for(Duration::from_millis(300)) {
|
||||
None => log::error!("get_pool fail"),
|
||||
Some(_) => {
|
||||
if !is_user_db_init(user_id) {
|
||||
let _ = self.open_user_db(user_id)?;
|
||||
set_user_db_init(true, user_id);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
let db_map = DB_MAP.read().map_err(|e| {
|
||||
ErrorBuilder::new(ErrorCode::UserDatabaseReadLocked)
|
||||
.error(e)
|
||||
.build()
|
||||
})?;
|
||||
|
||||
match db_map.get(user_id) {
|
||||
None => Err(ErrorBuilder::new(ErrorCode::UserDatabaseInitFailed)
|
||||
.msg("Get connection failed. The database is not initialization")
|
||||
match DB_MAP.try_read_for(Duration::from_millis(300)) {
|
||||
None => Err(ErrorBuilder::new(ErrorCode::AcquireReadLockedFailed)
|
||||
.msg(format!("Read user db failed"))
|
||||
.build()),
|
||||
Some(database) => Ok(database.get_pool()),
|
||||
Some(read_guard) => match read_guard.get(user_id) {
|
||||
None => Err(ErrorBuilder::new(ErrorCode::UserDatabaseInitFailed)
|
||||
.msg("Get connection failed. The database is not initialization")
|
||||
.build()),
|
||||
Some(database) => Ok(database.get_pool()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,14 +88,15 @@ lazy_static! {
|
||||
static ref DB_MAP: RwLock<HashMap<String, Database>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
static INIT_FLAG_MAP: Lazy<Mutex<HashMap<String, bool>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static INIT_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||
static INIT_RECORD: Lazy<Mutex<HashMap<String, bool>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
fn set_user_db_init(is_init: bool, user_id: &str) {
|
||||
let mut flag_map = INIT_FLAG_MAP.lock();
|
||||
flag_map.insert(user_id.to_owned(), is_init);
|
||||
let mut record = INIT_RECORD.lock();
|
||||
record.insert(user_id.to_owned(), is_init);
|
||||
}
|
||||
|
||||
fn is_user_db_init(user_id: &str) -> bool {
|
||||
match INIT_FLAG_MAP.lock().get(user_id) {
|
||||
match INIT_RECORD.lock().get(user_id) {
|
||||
None => false,
|
||||
Some(flag) => flag.clone(),
|
||||
}
|
||||
|
@ -69,22 +69,36 @@ impl UserSession {
|
||||
self.database.get_pool(&user_id)
|
||||
}
|
||||
|
||||
pub async fn sign_in(&self, params: SignInParams) -> Result<UserTable, UserError> {
|
||||
let resp = self.server.sign_in(params).await?;
|
||||
let session = Session::new(&resp.uid, &resp.token);
|
||||
let _ = self.set_session(Some(session))?;
|
||||
let user_table = self.save_user(resp.into()).await?;
|
||||
|
||||
Ok(user_table)
|
||||
pub async fn sign_in(&self, params: SignInParams) -> Result<UserDetail, UserError> {
|
||||
if self.is_login(¶ms.email) {
|
||||
self.user_detail().await
|
||||
} else {
|
||||
let resp = self.server.sign_in(params).await?;
|
||||
let session = Session::new(&resp.uid, &resp.token, &resp.email);
|
||||
let _ = self.set_session(Some(session))?;
|
||||
let user_table = self.save_user(resp.into()).await?;
|
||||
let user_detail = UserDetail::from(user_table);
|
||||
Ok(user_detail)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sign_up(&self, params: SignUpParams) -> Result<UserTable, UserError> {
|
||||
pub async fn sign_up(&self, params: SignUpParams) -> Result<UserDetail, UserError> {
|
||||
// if self.is_login(¶ms.email) {
|
||||
// self.user_detail().await
|
||||
// } else {
|
||||
// let resp = self.server.sign_up(params).await?;
|
||||
// let session = Session::new(&resp.user_id, &resp.token, &resp.email);
|
||||
// let _ = self.set_session(Some(session))?;
|
||||
// let user_table = self.save_user(resp.into()).await?;
|
||||
// let user_detail = UserDetail::from(user_table);
|
||||
// Ok(user_detail)
|
||||
// }
|
||||
let resp = self.server.sign_up(params).await?;
|
||||
let session = Session::new(&resp.user_id, &resp.token);
|
||||
let session = Session::new(&resp.user_id, &resp.token, &resp.email);
|
||||
let _ = self.set_session(Some(session))?;
|
||||
let user_table = self.save_user(resp.into()).await?;
|
||||
|
||||
Ok(user_table)
|
||||
let user_detail = UserDetail::from(user_table);
|
||||
Ok(user_detail)
|
||||
}
|
||||
|
||||
pub async fn sign_out(&self) -> Result<(), UserError> {
|
||||
@ -170,6 +184,7 @@ impl UserSession {
|
||||
*self.session.write() = session;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_session(&self) -> Result<Session, UserError> {
|
||||
let mut session = { (*self.session.read()).clone() };
|
||||
if session.is_none() {
|
||||
@ -187,6 +202,13 @@ impl UserSession {
|
||||
Some(session) => Ok(session),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_login(&self, email: &str) -> bool {
|
||||
match self.get_session() {
|
||||
Ok(session) => session.email == email,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_user(_server: Server, pool: Arc<ConnectionPool>, params: UpdateUserParams) -> Result<(), UserError> {
|
||||
@ -213,13 +235,15 @@ const SESSION_CACHE_KEY: &str = "session_cache_key";
|
||||
struct Session {
|
||||
user_id: String,
|
||||
token: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn new(user_id: &str, token: &str) -> Self {
|
||||
pub fn new(user_id: &str, token: &str, email: &str) -> Self {
|
||||
Self {
|
||||
user_id: user_id.to_owned(),
|
||||
token: token.to_owned(),
|
||||
email: email.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
use crate::helper::*;
|
||||
use flowy_test::builder::UserTestBuilder;
|
||||
use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
|
||||
use serial_test::*;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn sign_up_success() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
log::info!("{:?}", user_detail);
|
||||
}
|
||||
|
||||
@ -16,16 +17,11 @@ fn sign_up_with_invalid_email() {
|
||||
let request = SignUpRequest {
|
||||
email: email.to_string(),
|
||||
name: valid_name(),
|
||||
password: valid_password(),
|
||||
password: login_password(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
TestBuilder::new()
|
||||
.event(SignUp)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
UserTestBuilder::new().event(SignUp).request(request).sync_send().error().code,
|
||||
ErrorCode::EmailFormatInvalid
|
||||
);
|
||||
}
|
||||
@ -40,27 +36,23 @@ fn sign_up_with_invalid_password() {
|
||||
password,
|
||||
};
|
||||
|
||||
TestBuilder::new()
|
||||
.event(SignUp)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
UserTestBuilder::new().event(SignUp).request(request).sync_send().assert_error();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn sign_in_success() {
|
||||
let context = TestBuilder::new().sign_up();
|
||||
let context = UserTestBuilder::new().sign_up();
|
||||
|
||||
let _ = TestBuilder::new().event(SignOut).sync_send();
|
||||
let _ = UserTestBuilder::new().event(SignOut).sync_send();
|
||||
|
||||
let request = SignInRequest {
|
||||
email: context.user_detail.email,
|
||||
password: context.password,
|
||||
};
|
||||
|
||||
let response = TestBuilder::new()
|
||||
let response = UserTestBuilder::new()
|
||||
.event(SignIn)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
@ -74,16 +66,11 @@ fn sign_in_with_invalid_email() {
|
||||
for email in invalid_email_test_case() {
|
||||
let request = SignInRequest {
|
||||
email: email.to_string(),
|
||||
password: valid_password(),
|
||||
password: login_password(),
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
TestBuilder::new()
|
||||
.event(SignIn)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
UserTestBuilder::new().event(SignIn).request(request).sync_send().error().code,
|
||||
ErrorCode::EmailFormatInvalid
|
||||
);
|
||||
}
|
||||
@ -98,10 +85,6 @@ fn sign_in_with_invalid_password() {
|
||||
password,
|
||||
};
|
||||
|
||||
TestBuilder::new()
|
||||
.event(SignIn)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
UserTestBuilder::new().event(SignIn).request(request).sync_send().assert_error();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
pub use flowy_test::builder::TestBuilder;
|
||||
|
||||
pub use flowy_test::prelude::{random_email, valid_password};
|
||||
pub use flowy_test::{
|
||||
builder::*,
|
||||
prelude::{login_password, random_email},
|
||||
};
|
||||
|
||||
pub(crate) fn invalid_email_test_case() -> Vec<String> {
|
||||
// https://gist.github.com/cjaoude/fd9910626629b53c4d25
|
||||
|
@ -1,28 +1,22 @@
|
||||
use crate::helper::*;
|
||||
use flowy_infra::uuid;
|
||||
use flowy_test::builder::UserTestBuilder;
|
||||
use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
|
||||
use serial_test::*;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_status_get_failed() {
|
||||
let user_detail = TestBuilder::new()
|
||||
.event(GetUserProfile)
|
||||
.assert_error()
|
||||
.sync_send()
|
||||
.user_detail;
|
||||
assert!(user_detail.is_none())
|
||||
let tester = UserTestBuilder::new().event(GetUserProfile).assert_error().sync_send();
|
||||
assert!(tester.get_user_detail().is_none())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_detail_get() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
|
||||
let user_detail2 = TestBuilder::new()
|
||||
.event(GetUserProfile)
|
||||
.sync_send()
|
||||
.parse::<UserDetail>();
|
||||
let user_detail2 = UserTestBuilder::new().event(GetUserProfile).sync_send().parse::<UserDetail>();
|
||||
|
||||
assert_eq!(user_detail, user_detail2);
|
||||
}
|
||||
@ -30,15 +24,12 @@ fn user_detail_get() {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_update_with_name() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
let new_name = "hello_world".to_owned();
|
||||
let request = UpdateUserRequest::new(&user_detail.id).name(&new_name);
|
||||
let _ = TestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send();
|
||||
let _ = UserTestBuilder::new().event(UpdateUser).request(request).sync_send();
|
||||
|
||||
let user_detail = TestBuilder::new()
|
||||
let user_detail = UserTestBuilder::new()
|
||||
.event(GetUserProfile)
|
||||
.assert_error()
|
||||
.sync_send()
|
||||
@ -50,16 +41,13 @@ fn user_update_with_name() {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_update_with_email() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
let new_email = format!("{}@gmai.com", uuid());
|
||||
let request = UpdateUserRequest::new(&user_detail.id).email(&new_email);
|
||||
|
||||
let _ = TestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send();
|
||||
let _ = UserTestBuilder::new().event(UpdateUser).request(request).sync_send();
|
||||
|
||||
let user_detail = TestBuilder::new()
|
||||
let user_detail = UserTestBuilder::new()
|
||||
.event(GetUserProfile)
|
||||
.assert_error()
|
||||
.sync_send()
|
||||
@ -71,11 +59,11 @@ fn user_update_with_email() {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_update_with_password() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
let new_password = "H123world!".to_owned();
|
||||
let request = UpdateUserRequest::new(&user_detail.id).password(&new_password);
|
||||
|
||||
let _ = TestBuilder::new()
|
||||
let _ = UserTestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
@ -85,16 +73,11 @@ fn user_update_with_password() {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_update_with_invalid_email() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
for email in invalid_email_test_case() {
|
||||
let request = UpdateUserRequest::new(&user_detail.id).email(&email);
|
||||
assert_eq!(
|
||||
TestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
UserTestBuilder::new().event(UpdateUser).request(request).sync_send().error().code,
|
||||
ErrorCode::EmailFormatInvalid
|
||||
);
|
||||
}
|
||||
@ -103,27 +86,19 @@ fn user_update_with_invalid_email() {
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_update_with_invalid_password() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
for password in invalid_password_test_case() {
|
||||
let request = UpdateUserRequest::new(&user_detail.id).password(&password);
|
||||
|
||||
TestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
UserTestBuilder::new().event(UpdateUser).request(request).sync_send().assert_error();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn user_update_with_invalid_name() {
|
||||
let user_detail = TestBuilder::new().sign_up().user_detail;
|
||||
let user_detail = UserTestBuilder::new().sign_up().user_detail;
|
||||
let request = UpdateUserRequest::new(&user_detail.id).name("");
|
||||
|
||||
TestBuilder::new()
|
||||
.event(UpdateUser)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.assert_error();
|
||||
UserTestBuilder::new().event(UpdateUser).request(request).sync_send().assert_error();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::helper::*;
|
||||
|
||||
use flowy_test::builder::UserTestBuilder;
|
||||
use flowy_workspace::entities::{
|
||||
app::{QueryAppRequest, UpdateAppRequest},
|
||||
view::*,
|
||||
@ -7,6 +8,7 @@ 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);
|
||||
dbg!(&app);
|
||||
@ -15,6 +17,8 @@ fn app_create() {
|
||||
#[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);
|
||||
@ -24,6 +28,8 @@ fn app_delete() {
|
||||
|
||||
#[test]
|
||||
fn app_read() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
|
||||
let workspace = create_workspace("Workspace", "");
|
||||
let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
|
||||
let query = QueryAppRequest::new(&app.id);
|
||||
@ -33,6 +39,7 @@ fn app_read() {
|
||||
|
||||
#[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 request_a = CreateViewRequest {
|
||||
@ -63,6 +70,7 @@ 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 query = QueryAppRequest::new(&app_id).set_is_trash(true);
|
||||
let _ = read_app(query);
|
||||
@ -71,6 +79,7 @@ fn app_set_trash_flag() {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn app_set_trash_flag_2() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
let app_id = create_app_with_trash_flag();
|
||||
let query = QueryAppRequest::new(&app_id);
|
||||
let _ = read_app(query);
|
||||
|
@ -1,4 +1,4 @@
|
||||
pub use flowy_test::builder::AnnieTestBuilder;
|
||||
use flowy_test::builder::{UserTestBuilder, WorkspaceTestBuilder};
|
||||
use flowy_workspace::{
|
||||
entities::{app::*, view::*, workspace::*},
|
||||
event::WorkspaceEvent::*,
|
||||
@ -17,7 +17,7 @@ pub fn create_workspace(name: &str, desc: &str) -> Workspace {
|
||||
desc: desc.to_owned(),
|
||||
};
|
||||
|
||||
let workspace = AnnieTestBuilder::new()
|
||||
let workspace = WorkspaceTestBuilder::new()
|
||||
.event(CreateWorkspace)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
@ -26,13 +26,13 @@ pub fn create_workspace(name: &str, desc: &str) -> Workspace {
|
||||
}
|
||||
|
||||
pub fn read_workspaces(request: QueryWorkspaceRequest) -> Option<Workspace> {
|
||||
let mut repeated_workspace = AnnieTestBuilder::new()
|
||||
let mut repeated_workspace = WorkspaceTestBuilder::new()
|
||||
.event(ReadWorkspaces)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.parse::<RepeatedWorkspace>();
|
||||
|
||||
debug_assert_eq!(repeated_workspace.len(), 1);
|
||||
debug_assert_eq!(repeated_workspace.len(), 1, "Default workspace not found");
|
||||
repeated_workspace.drain(..1).collect::<Vec<Workspace>>().pop()
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ pub fn create_app(name: &str, desc: &str, workspace_id: &str) -> App {
|
||||
color_style: Default::default(),
|
||||
};
|
||||
|
||||
let app = AnnieTestBuilder::new()
|
||||
let app = WorkspaceTestBuilder::new()
|
||||
.event(CreateApp)
|
||||
.request(create_app_request)
|
||||
.sync_send()
|
||||
@ -57,19 +57,23 @@ pub fn delete_app(app_id: &str) {
|
||||
app_id: app_id.to_string(),
|
||||
};
|
||||
|
||||
AnnieTestBuilder::new().event(DeleteApp).request(delete_app_request).sync_send();
|
||||
WorkspaceTestBuilder::new().event(DeleteApp).request(delete_app_request).sync_send();
|
||||
}
|
||||
|
||||
pub fn update_app(request: UpdateAppRequest) { AnnieTestBuilder::new().event(UpdateApp).request(request).sync_send(); }
|
||||
pub fn update_app(request: UpdateAppRequest) { WorkspaceTestBuilder::new().event(UpdateApp).request(request).sync_send(); }
|
||||
|
||||
pub fn read_app(request: QueryAppRequest) -> App {
|
||||
let app = AnnieTestBuilder::new().event(ReadApp).request(request).sync_send().parse::<App>();
|
||||
let app = WorkspaceTestBuilder::new()
|
||||
.event(ReadApp)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.parse::<App>();
|
||||
|
||||
app
|
||||
}
|
||||
|
||||
pub fn create_view_with_request(request: CreateViewRequest) -> View {
|
||||
let view = AnnieTestBuilder::new()
|
||||
let view = WorkspaceTestBuilder::new()
|
||||
.event(CreateView)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
@ -78,9 +82,8 @@ pub fn create_view_with_request(request: CreateViewRequest) -> View {
|
||||
view
|
||||
}
|
||||
|
||||
pub fn create_view() -> View {
|
||||
let workspace = create_workspace("Workspace", "");
|
||||
let app = create_app("App A", "AppFlowy Github Project", &workspace.id);
|
||||
pub fn create_view(workspace_id: &str) -> View {
|
||||
let app = create_app("App A", "AppFlowy Github Project", workspace_id);
|
||||
let request = CreateViewRequest {
|
||||
belong_to_id: app.id.clone(),
|
||||
name: "View A".to_string(),
|
||||
@ -92,6 +95,12 @@ pub fn create_view() -> View {
|
||||
create_view_with_request(request)
|
||||
}
|
||||
|
||||
pub fn update_view(request: UpdateViewRequest) { AnnieTestBuilder::new().event(UpdateView).request(request).sync_send(); }
|
||||
pub fn update_view(request: UpdateViewRequest) { WorkspaceTestBuilder::new().event(UpdateView).request(request).sync_send(); }
|
||||
|
||||
pub fn read_view(request: QueryViewRequest) -> View { AnnieTestBuilder::new().event(ReadView).request(request).sync_send().parse::<View>() }
|
||||
pub fn read_view(request: QueryViewRequest) -> View {
|
||||
WorkspaceTestBuilder::new()
|
||||
.event(ReadView)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.parse::<View>()
|
||||
}
|
||||
|
@ -1,12 +1,19 @@
|
||||
use crate::helper::*;
|
||||
|
||||
use flowy_test::builder::UserTestBuilder;
|
||||
use flowy_workspace::entities::view::*;
|
||||
|
||||
#[test]
|
||||
fn view_create() { let _ = create_view(); }
|
||||
fn view_create() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
|
||||
let workspace = create_workspace("Workspace", "");
|
||||
let _ = create_view(&workspace.id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn view_set_trash_flag() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
let view_id = create_view_with_trash_flag();
|
||||
let query = QueryViewRequest::new(&view_id).set_is_trash(true);
|
||||
let _ = read_view(query);
|
||||
@ -15,13 +22,16 @@ fn view_set_trash_flag() {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn view_set_trash_flag2() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
|
||||
let view_id = create_view_with_trash_flag();
|
||||
let query = QueryViewRequest::new(&view_id);
|
||||
let _ = read_view(query);
|
||||
}
|
||||
|
||||
fn create_view_with_trash_flag() -> String {
|
||||
let view = create_view();
|
||||
let workspace = create_workspace("Workspace", "");
|
||||
let view = create_view(&workspace.id);
|
||||
let request = UpdateViewRequest {
|
||||
view_id: view.id.clone(),
|
||||
name: None,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::helper::*;
|
||||
use flowy_test::builder::*;
|
||||
use flowy_workspace::{
|
||||
entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest, RepeatedWorkspace},
|
||||
event::WorkspaceEvent::*,
|
||||
@ -10,11 +11,12 @@ fn workspace_create_success() { let _ = create_workspace("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 request = QueryWorkspaceRequest::new();
|
||||
let workspaces = AnnieTestBuilder::new()
|
||||
|
||||
let workspaces = WorkspaceTestBuilder::new()
|
||||
.event(ReadWorkspaces)
|
||||
.request(request)
|
||||
.request(QueryWorkspaceRequest::new())
|
||||
.sync_send()
|
||||
.parse::<RepeatedWorkspace>();
|
||||
|
||||
@ -42,11 +44,15 @@ fn workspace_create_with_apps() {
|
||||
#[test]
|
||||
fn workspace_create_with_invalid_name() {
|
||||
for name in invalid_workspace_name_test_case() {
|
||||
let builder = AnnieTestBuilder::new();
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
let request = CreateWorkspaceRequest { name, desc: "".to_owned() };
|
||||
|
||||
assert_eq!(
|
||||
builder.event(CreateWorkspace).request(request).sync_send().error().code,
|
||||
WorkspaceTestBuilder::new()
|
||||
.event(CreateWorkspace)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
ErrorCode::WorkspaceNameInvalid
|
||||
)
|
||||
}
|
||||
@ -54,12 +60,16 @@ fn workspace_create_with_invalid_name() {
|
||||
|
||||
#[test]
|
||||
fn workspace_update_with_invalid_name() {
|
||||
let _ = UserTestBuilder::new().sign_up();
|
||||
for name in invalid_workspace_name_test_case() {
|
||||
let builder = AnnieTestBuilder::new();
|
||||
let request = CreateWorkspaceRequest { name, desc: "".to_owned() };
|
||||
|
||||
assert_eq!(
|
||||
builder.event(CreateWorkspace).request(request).sync_send().error().code,
|
||||
WorkspaceTestBuilder::new()
|
||||
.event(CreateWorkspace)
|
||||
.request(request)
|
||||
.sync_send()
|
||||
.error()
|
||||
.code,
|
||||
ErrorCode::WorkspaceNameInvalid
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user