[rust]: fix bugs to pass backend tests

This commit is contained in:
appflowy 2021-11-09 15:32:57 +08:00
parent 823c9f9bd6
commit 2972c40b9b
20 changed files with 84 additions and 116 deletions

View File

@ -1,7 +1,10 @@
use crate::{
entities::{token::Token, user::UserTable},
service::user::{hash_password, verify_password, LoggedUser},
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use anyhow::Context; use anyhow::Context;
use chrono::Utc;
use sqlx::{PgPool, Postgres};
use flowy_net::{ use flowy_net::{
errors::{invalid_params, ErrorCode, ServerError}, errors::{invalid_params, ErrorCode, ServerError},
response::FlowyResponse, response::FlowyResponse,
@ -10,12 +13,7 @@ use flowy_user_infra::{
parser::{UserEmail, UserName, UserPassword}, parser::{UserEmail, UserName, UserPassword},
protobuf::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile}, protobuf::{SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserParams, UserProfile},
}; };
use sqlx::{PgPool, Postgres};
use crate::{
entities::{token::Token, user::UserTable},
service::user::{hash_password, verify_password, LoggedUser},
sqlx_ext::{map_sqlx_error, DBTransaction, SqlBuilder},
};
use super::AUTHORIZED_USERS; use super::AUTHORIZED_USERS;
use crate::service::user::user_default::create_default_workspace; use crate::service::user::user_default::create_default_workspace;

View File

@ -210,7 +210,7 @@ async fn workspace_list_read() {
let _ = server.create_workspace(params).await; let _ = server.create_workspace(params).await;
} }
let read_params = WorkspaceIdentifier::new(); let read_params = WorkspaceIdentifier::new(None);
let workspaces = server.read_workspaces(read_params).await; let workspaces = server.read_workspaces(read_params).await;
assert_eq!(workspaces.len(), 4); assert_eq!(workspaces.len(), 4);
} }

View File

@ -4,11 +4,7 @@ use diesel::{Connection, SqliteConnection};
use flowy_derive::ProtoBuf; use flowy_derive::ProtoBuf;
use flowy_sqlite::{DBConnection, Database, PoolConfig}; use flowy_sqlite::{DBConnection, Database, PoolConfig};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::{ use std::{collections::HashMap, path::Path, sync::RwLock};
collections::HashMap,
path::Path,
sync::{PoisonError, RwLock, RwLockWriteGuard},
};
const DB_NAME: &str = "kv.db"; const DB_NAME: &str = "kv.db";
lazy_static! { lazy_static! {

View File

@ -102,37 +102,6 @@ mod tests {
say("hello world"); say("hello world");
} }
#[test]
fn test_log2() {
let env_filter = EnvFilter::new("Debug");
let file_appender = tracing_appender::rolling::daily(".", "flowy_log_test");
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
let subscriber = tracing_subscriber::fmt()
.with_target(false)
.with_max_level(tracing::Level::TRACE)
.with_writer(std::io::stderr)
.with_thread_ids(true)
.with_writer(non_blocking)
.json()
.compact()
.finish()
.with(env_filter);
let formatting_layer = FlowyFormattingLayer::new(std::io::stdout);
let _ = set_global_default(subscriber.with(JsonStorageLayer).with(formatting_layer))
.map_err(|e| format!("{:?}", e))
.unwrap();
let _ = LogTracer::builder()
.with_max_level(LevelFilter::Trace)
.init()
.map_err(|e| format!("{:?}", e))
.unwrap();
tracing::info!("😁");
}
#[tracing::instrument(name = "say")] #[tracing::instrument(name = "say")]
fn say(s: &str) { fn say(s: &str) {
tracing::info!("{}", s); tracing::info!("{}", s);

View File

@ -110,8 +110,8 @@ async fn _listen_user_status(
Ok(status) => { Ok(status) => {
let result = || async { let result = || async {
match status { match status {
UserStatus::Login { .. } => { UserStatus::Login { token } => {
let _ = workspace_controller.user_did_login()?; let _ = workspace_controller.user_did_sign_in(&token)?;
}, },
UserStatus::Logout { .. } => { UserStatus::Logout { .. } => {
workspace_controller.user_did_logout(); workspace_controller.user_did_logout();
@ -119,8 +119,8 @@ async fn _listen_user_status(
UserStatus::Expired { .. } => { UserStatus::Expired { .. } => {
workspace_controller.user_session_expired(); workspace_controller.user_session_expired();
}, },
UserStatus::SignUp { .. } => { UserStatus::SignUp { profile } => {
let _ = workspace_controller.user_did_sign_up().await?; let _ = workspace_controller.user_did_sign_up(&profile.token).await?;
}, },
} }
Ok::<(), WorkspaceError>(()) Ok::<(), WorkspaceError>(())

View File

@ -40,8 +40,6 @@ impl FlowyTest {
pub fn setup_with(server_config: ServerConfig) -> Self { pub fn setup_with(server_config: ServerConfig) -> Self {
let config = FlowySDKConfig::new(&root_dir(), server_config).log_filter("debug"); let config = FlowySDKConfig::new(&root_dir(), server_config).log_filter("debug");
let sdk = FlowySDK::new(config); let sdk = FlowySDK::new(config);
let _ = sdk.workspace.init().unwrap();
Self { sdk } Self { sdk }
} }

View File

@ -7,6 +7,7 @@ use flowy_workspace::{
view::*, view::*,
workspace::*, workspace::*,
}, },
errors::ErrorCode,
event::WorkspaceEvent::*, event::WorkspaceEvent::*,
}; };
@ -98,11 +99,11 @@ impl ViewTest {
} }
} }
pub fn invalid_workspace_name_test_case() -> Vec<String> { pub fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {
vec!["", "1234".repeat(100).as_str()] vec![
.iter() ("".to_owned(), ErrorCode::WorkspaceNameInvalid),
.map(|s| s.to_string()) ("1234".repeat(100), ErrorCode::WorkspaceNameTooLong),
.collect::<Vec<_>>() ]
} }
pub async fn create_workspace(sdk: &FlowyTestSDK, name: &str, desc: &str) -> Workspace { pub async fn create_workspace(sdk: &FlowyTestSDK, name: &str, desc: &str) -> Workspace {
@ -131,7 +132,7 @@ async fn open_workspace(sdk: &FlowyTestSDK, workspace_id: &str) {
.await; .await;
} }
pub async fn read_workspace(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest) -> Option<Workspace> { pub async fn read_workspace(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest) -> Vec<Workspace> {
let mut repeated_workspace = FlowyWorkspaceTest::new(sdk.clone()) let mut repeated_workspace = FlowyWorkspaceTest::new(sdk.clone())
.event(ReadWorkspaces) .event(ReadWorkspaces)
.request(request.clone()) .request(request.clone())
@ -139,7 +140,7 @@ pub async fn read_workspace(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest)
.await .await
.parse::<RepeatedWorkspace>(); .parse::<RepeatedWorkspace>();
let mut workspaces; let workspaces;
if let Some(workspace_id) = &request.workspace_id { if let Some(workspace_id) = &request.workspace_id {
workspaces = repeated_workspace workspaces = repeated_workspace
.into_inner() .into_inner()
@ -151,7 +152,7 @@ pub async fn read_workspace(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest)
workspaces = repeated_workspace.items; workspaces = repeated_workspace.items;
} }
workspaces.drain(..1).collect::<Vec<Workspace>>().pop() workspaces
} }
pub async fn create_app(sdk: &FlowyTestSDK, name: &str, desc: &str, workspace_id: &str) -> App { pub async fn create_app(sdk: &FlowyTestSDK, name: &str, desc: &str, workspace_id: &str) -> App {

View File

@ -75,7 +75,6 @@ impl UserSession {
} }
pub fn init(&self) { pub fn init(&self) {
log::debug!("😁😁😁 user did login");
match self.get_session() { match self.get_session() {
Ok(session) => { Ok(session) => {
let _ = self.status_notifier.send(UserStatus::Login { token: session.token }); let _ = self.status_notifier.send(UserStatus::Login { token: session.token });

View File

@ -50,7 +50,7 @@ async fn user_update_with_name() {
async fn user_update_with_email() { async fn user_update_with_email() {
let test = FlowyTest::setup(); let test = FlowyTest::setup();
let user = test.init_user().await; let user = test.init_user().await;
let new_email = format!("{}@gmai.com", uuid()); let new_email = format!("{}@gmail.com", uuid());
let request = UpdateUserRequest::new(&user.id).email(&new_email); let request = UpdateUserRequest::new(&user.id).email(&new_email);
let _ = UserTest::new(test.sdk()).event(UpdateUser).request(request).sync_send(); let _ = UserTest::new(test.sdk()).event(UpdateUser).request(request).sync_send();
let user_profile = UserTest::new(test.sdk()) let user_profile = UserTest::new(test.sdk())

View File

@ -10,12 +10,7 @@ pub struct QueryWorkspaceRequest {
} }
impl QueryWorkspaceRequest { impl QueryWorkspaceRequest {
pub fn new() -> Self { Self { workspace_id: None } } pub fn new(workspace_id: Option<String>) -> Self { Self { workspace_id } }
pub fn workspace_id(mut self, workspace_id: &str) -> Self {
self.workspace_id = Some(workspace_id.to_owned());
self
}
} }
// Read all workspaces if the workspace_id is None // Read all workspaces if the workspace_id is None

View File

@ -15,6 +15,7 @@ pub mod handlers;
mod notify; mod notify;
pub mod protobuf; pub mod protobuf;
mod sql_tables; mod sql_tables;
mod util;
pub mod prelude { pub mod prelude {
pub use flowy_workspace_infra::entities::{app::*, trash::*, view::*, workspace::*}; pub use flowy_workspace_infra::entities::{app::*, trash::*, view::*, workspace::*};

View File

@ -12,7 +12,7 @@ use crate::{
errors::*, errors::*,
module::{WorkspaceDatabase, WorkspaceUser}, module::{WorkspaceDatabase, WorkspaceUser},
notify::*, notify::*,
services::{helper::spawn, server::Server, TrashCan, TrashEvent}, services::{server::Server, TrashCan, TrashEvent},
sql_tables::app::{AppTable, AppTableChangeset, AppTableSql}, sql_tables::app::{AppTable, AppTableChangeset, AppTableSql},
}; };
@ -122,7 +122,7 @@ impl AppController {
fn update_app_on_server(&self, params: UpdateAppParams) -> Result<(), WorkspaceError> { fn update_app_on_server(&self, params: UpdateAppParams) -> Result<(), WorkspaceError> {
let token = self.user.token()?; let token = self.user.token()?;
let server = self.server.clone(); let server = self.server.clone();
spawn(async move { tokio::spawn(async move {
match server.update_app(&token, params).await { match server.update_app(&token, params).await {
Ok(_) => {}, Ok(_) => {},
Err(e) => { Err(e) => {
@ -139,7 +139,7 @@ impl AppController {
let token = self.user.token()?; let token = self.user.token()?;
let server = self.server.clone(); let server = self.server.clone();
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
spawn(async move { tokio::spawn(async move {
// Opti: retry? // Opti: retry?
match server.read_app(&token, params).await { match server.read_app(&token, params).await {
Ok(Some(app)) => match pool.get() { Ok(Some(app)) => match pool.get() {

View File

@ -1,9 +0,0 @@
use tokio::task::JoinHandle;
pub fn spawn<F>(f: F) -> JoinHandle<F::Output>
where
F: std::future::Future + Send + 'static,
F::Output: Send + 'static,
{
tokio::spawn(f)
}

View File

@ -5,9 +5,7 @@ pub use workspace_controller::*;
mod app_controller; mod app_controller;
mod database; mod database;
mod helper; pub(crate) mod server;
pub mod server;
mod trash_can; mod trash_can;
mod util;
mod view_controller; mod view_controller;
mod workspace_controller; mod workspace_controller;

View File

@ -10,7 +10,7 @@ use crate::{
errors::{WorkspaceError, WorkspaceResult}, errors::{WorkspaceError, WorkspaceResult},
module::{WorkspaceDatabase, WorkspaceUser}, module::{WorkspaceDatabase, WorkspaceUser},
notify::{send_anonymous_dart_notification, WorkspaceNotification}, notify::{send_anonymous_dart_notification, WorkspaceNotification},
services::{helper::spawn, server::Server}, services::server::Server,
sql_tables::trash::TrashTableSql, sql_tables::trash::TrashTableSql,
}; };
@ -232,7 +232,7 @@ impl TrashCan {
let server = self.server.clone(); let server = self.server.clone();
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
spawn(async move { tokio::spawn(async move {
match server.read_trash(&token).await { match server.read_trash(&token).await {
Ok(repeated_trash) => { Ok(repeated_trash) => {
tracing::debug!("Remote trash count: {}", repeated_trash.items.len()); tracing::debug!("Remote trash count: {}", repeated_trash.items.len());

View File

@ -16,7 +16,7 @@ use crate::{
errors::{internal_error, WorkspaceError, WorkspaceResult}, errors::{internal_error, WorkspaceError, WorkspaceResult},
module::{WorkspaceDatabase, WorkspaceUser}, module::{WorkspaceDatabase, WorkspaceUser},
notify::{send_dart_notification, WorkspaceNotification}, notify::{send_dart_notification, WorkspaceNotification},
services::{helper::spawn, server::Server, TrashCan, TrashEvent}, services::{server::Server, TrashCan, TrashEvent},
sql_tables::view::{ViewTable, ViewTableChangeset, ViewTableSql}, sql_tables::view::{ViewTable, ViewTableChangeset, ViewTableSql},
}; };
@ -187,7 +187,7 @@ impl ViewController {
fn update_view_on_server(&self, params: UpdateViewParams) -> Result<(), WorkspaceError> { fn update_view_on_server(&self, params: UpdateViewParams) -> Result<(), WorkspaceError> {
let token = self.user.token()?; let token = self.user.token()?;
let server = self.server.clone(); let server = self.server.clone();
spawn(async move { tokio::spawn(async move {
match server.update_view(&token, params).await { match server.update_view(&token, params).await {
Ok(_) => {}, Ok(_) => {},
Err(e) => { Err(e) => {
@ -205,7 +205,7 @@ impl ViewController {
let server = self.server.clone(); let server = self.server.clone();
let pool = self.database.db_pool()?; let pool = self.database.db_pool()?;
// Opti: retry? // Opti: retry?
spawn(async move { tokio::spawn(async move {
match server.read_view(&token, params).await { match server.read_view(&token, params).await {
Ok(Some(view)) => match pool.get() { Ok(Some(view)) => match pool.get() {
Ok(conn) => { Ok(conn) => {

View File

@ -2,7 +2,7 @@ use crate::{
errors::*, errors::*,
module::{WorkspaceDatabase, WorkspaceUser}, module::{WorkspaceDatabase, WorkspaceUser},
notify::*, notify::*,
services::{helper::spawn, read_local_workspace_apps, server::Server, AppController, TrashCan, ViewController}, services::{read_local_workspace_apps, server::Server, AppController, TrashCan, ViewController},
sql_tables::workspace::{WorkspaceTable, WorkspaceTableChangeset, WorkspaceTableSql}, sql_tables::workspace::{WorkspaceTable, WorkspaceTableChangeset, WorkspaceTableSql},
}; };
use chrono::Utc; use chrono::Utc;
@ -12,12 +12,19 @@ use flowy_workspace_infra::{
entities::{app::RepeatedApp, workspace::*}, entities::{app::RepeatedApp, workspace::*},
user_default, user_default,
}; };
use std::sync::{ use lazy_static::lazy_static;
atomic::{AtomicBool, Ordering}, use parking_lot::RwLock;
Arc, use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
}; };
static INIT_WORKSPACE: AtomicBool = AtomicBool::new(false); lazy_static! {
static ref INIT_WORKSPACE: RwLock<HashMap<String, bool>> = RwLock::new(HashMap::new());
}
pub struct WorkspaceController { pub struct WorkspaceController {
pub user: Arc<dyn WorkspaceUser>, pub user: Arc<dyn WorkspaceUser>,
@ -50,13 +57,14 @@ impl WorkspaceController {
} }
} }
fn init(&self) -> Result<(), WorkspaceError> { fn init(&self, token: &str) -> Result<(), WorkspaceError> {
if INIT_WORKSPACE.load(Ordering::SeqCst) { if let Some(is_init) = INIT_WORKSPACE.read().get(token) {
return Ok(()); if *is_init {
return Ok(());
}
} }
INIT_WORKSPACE.write().insert(token.to_owned(), true);
log::debug!("workspace initialize");
INIT_WORKSPACE.store(true, Ordering::SeqCst);
let _ = self.server.init(); let _ = self.server.init();
let _ = self.trash_can.init()?; let _ = self.trash_can.init()?;
let _ = self.view_controller.init()?; let _ = self.view_controller.init()?;
@ -65,9 +73,11 @@ impl WorkspaceController {
Ok(()) Ok(())
} }
pub fn user_did_login(&self) -> WorkspaceResult<()> { pub fn user_did_sign_in(&self, token: &str) -> WorkspaceResult<()> {
// TODO: (nathan) do something here // TODO: (nathan) do something here
let _ = self.init()?;
log::debug!("workspace initialize after sign in");
let _ = self.init(token)?;
Ok(()) Ok(())
} }
@ -79,7 +89,7 @@ impl WorkspaceController {
// TODO: (nathan) do something here // TODO: (nathan) do something here
} }
pub async fn user_did_sign_up(&self) -> WorkspaceResult<()> { pub async fn user_did_sign_up(&self, _token: &str) -> WorkspaceResult<()> {
log::debug!("Create user default workspace"); log::debug!("Create user default workspace");
let time = Utc::now(); let time = Utc::now();
let mut workspace = user_default::create_default_workspace(time); let mut workspace = user_default::create_default_workspace(time);
@ -103,7 +113,9 @@ impl WorkspaceController {
send_dart_notification(&token, WorkspaceNotification::UserCreateWorkspace) send_dart_notification(&token, WorkspaceNotification::UserCreateWorkspace)
.payload(repeated_workspace) .payload(repeated_workspace)
.send(); .send();
let _ = self.init()?;
log::debug!("workspace initialize after sign up");
let _ = self.init(&token)?;
Ok(()) Ok(())
} }
@ -290,7 +302,7 @@ impl WorkspaceController {
#[tracing::instrument(level = "debug", skip(self), err)] #[tracing::instrument(level = "debug", skip(self), err)]
fn update_workspace_on_server(&self, params: UpdateWorkspaceParams) -> Result<(), WorkspaceError> { fn update_workspace_on_server(&self, params: UpdateWorkspaceParams) -> Result<(), WorkspaceError> {
let (token, server) = self.token_with_server()?; let (token, server) = self.token_with_server()?;
spawn(async move { tokio::spawn(async move {
match server.update_workspace(&token, params).await { match server.update_workspace(&token, params).await {
Ok(_) => {}, Ok(_) => {},
Err(e) => { Err(e) => {
@ -308,7 +320,7 @@ impl WorkspaceController {
workspace_id: workspace_id.to_string(), workspace_id: workspace_id.to_string(),
}; };
let (token, server) = self.token_with_server()?; let (token, server) = self.token_with_server()?;
spawn(async move { tokio::spawn(async move {
match server.delete_workspace(&token, params).await { match server.delete_workspace(&token, params).await {
Ok(_) => {}, Ok(_) => {},
Err(e) => { Err(e) => {
@ -327,7 +339,7 @@ impl WorkspaceController {
let app_ctrl = self.app_controller.clone(); let app_ctrl = self.app_controller.clone();
let view_ctrl = self.view_controller.clone(); let view_ctrl = self.view_controller.clone();
let conn = self.database.db_connection()?; let conn = self.database.db_connection()?;
spawn(async move { tokio::spawn(async move {
// Opti: handle the error and retry? // Opti: handle the error and retry?
let workspaces = server.read_workspace(&token, params).await?; let workspaces = server.read_workspace(&token, params).await?;
let _ = (&*conn).immediate_transaction::<_, WorkspaceError, _>(|| { let _ = (&*conn).immediate_transaction::<_, WorkspaceError, _>(|| {

View File

@ -8,30 +8,40 @@ use flowy_workspace::{
#[tokio::test] #[tokio::test]
async fn workspace_read_all() { async fn workspace_read_all() {
let test = WorkspaceTest::new().await; let test = WorkspaceTest::new().await;
let workspace = read_workspace(&test.sdk, QueryWorkspaceRequest::new()).await.unwrap(); let workspace = read_workspace(&test.sdk, QueryWorkspaceRequest::new(None)).await;
assert_eq!(test.workspace, workspace); assert_eq!(workspace.len(), 2);
} }
#[tokio::test] #[tokio::test]
async fn workspace_read() { async fn workspace_read() {
let test = WorkspaceTest::new().await; let test = WorkspaceTest::new().await;
let request = QueryWorkspaceRequest::new().workspace_id(&test.workspace.id); let request = QueryWorkspaceRequest::new(Some(test.workspace.id.clone()));
let workspace = read_workspace(&test.sdk, request).await.unwrap(); let workspace_from_db = read_workspace(&test.sdk, request)
assert_eq!(test.workspace, workspace); .await
.drain(..1)
.collect::<Vec<Workspace>>()
.pop()
.unwrap();
assert_eq!(test.workspace, workspace_from_db);
} }
#[tokio::test] #[tokio::test]
async fn workspace_create_with_apps() { async fn workspace_create_with_apps() {
let test = WorkspaceTest::new().await; let test = WorkspaceTest::new().await;
let app = create_app(&test.sdk, "App A", "AppFlowy Github Project", &test.workspace.id).await; let app = create_app(&test.sdk, "App A", "AppFlowy Github Project", &test.workspace.id).await;
let request = QueryWorkspaceRequest::new().workspace_id(&test.workspace.id); let request = QueryWorkspaceRequest::new(Some(test.workspace.id.clone()));
let workspace_from_db = read_workspace(&test.sdk, request).await.unwrap(); let workspace_from_db = read_workspace(&test.sdk, request)
.await
.drain(..1)
.collect::<Vec<Workspace>>()
.pop()
.unwrap();
assert_eq!(&app, workspace_from_db.apps.first_or_crash()); assert_eq!(&app, workspace_from_db.apps.first_or_crash());
} }
#[tokio::test] #[tokio::test]
async fn workspace_create_with_invalid_name() { async fn workspace_create_with_invalid_name() {
for name in invalid_workspace_name_test_case() { for (name, code) in invalid_workspace_name_test_case() {
let sdk = FlowyTest::setup().sdk; let sdk = FlowyTest::setup().sdk;
let request = CreateWorkspaceRequest { let request = CreateWorkspaceRequest {
name, name,
@ -45,7 +55,7 @@ async fn workspace_create_with_invalid_name() {
.await .await
.error() .error()
.code, .code,
ErrorCode::WorkspaceNameInvalid.value() code.value()
) )
} }
} }
@ -53,7 +63,7 @@ async fn workspace_create_with_invalid_name() {
#[tokio::test] #[tokio::test]
async fn workspace_update_with_invalid_name() { async fn workspace_update_with_invalid_name() {
let sdk = FlowyTest::setup().sdk; let sdk = FlowyTest::setup().sdk;
for name in invalid_workspace_name_test_case() { for (name, code) in invalid_workspace_name_test_case() {
let request = CreateWorkspaceRequest { let request = CreateWorkspaceRequest {
name, name,
desc: "".to_owned(), desc: "".to_owned(),
@ -66,7 +76,7 @@ async fn workspace_update_with_invalid_name() {
.await .await
.error() .error()
.code, .code,
ErrorCode::WorkspaceNameInvalid.value() code.value()
) )
} }
} }