mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor tests
This commit is contained in:
parent
4450d4410b
commit
7ac55f29db
1
backend/Cargo.lock
generated
1
backend/Cargo.lock
generated
@ -1272,6 +1272,7 @@ dependencies = [
|
|||||||
"flowy-document-infra",
|
"flowy-document-infra",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lib-dispatch",
|
"lib-dispatch",
|
||||||
"lib-infra",
|
"lib-infra",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use backend::services::doc::{crud::update_doc, manager::DocManager};
|
use backend::services::doc::{crud::update_doc, manager::DocManager};
|
||||||
use flowy_document::services::doc::edit::ClientDocEditor as ClientEditDocContext;
|
use flowy_document::services::doc::edit::ClientDocEditor as ClientEditDocContext;
|
||||||
use flowy_test::{workspace::ViewTest, FlowyTest};
|
use flowy_test::{helper::ViewTest, FlowySDKTest};
|
||||||
use flowy_user::services::user::UserSession;
|
use flowy_user::services::user::UserSession;
|
||||||
use futures_util::{stream, stream::StreamExt};
|
use futures_util::{stream, stream::StreamExt};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
@ -18,7 +18,7 @@ use lib_ot::core::Interval;
|
|||||||
|
|
||||||
pub struct DocumentTest {
|
pub struct DocumentTest {
|
||||||
server: TestServer,
|
server: TestServer,
|
||||||
flowy_test: FlowyTest,
|
flowy_test: FlowySDKTest,
|
||||||
}
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum DocScript {
|
pub enum DocScript {
|
||||||
@ -34,7 +34,7 @@ pub enum DocScript {
|
|||||||
impl DocumentTest {
|
impl DocumentTest {
|
||||||
pub async fn new() -> Self {
|
pub async fn new() -> Self {
|
||||||
let server = spawn_server().await;
|
let server = spawn_server().await;
|
||||||
let flowy_test = FlowyTest::setup_with(server.client_server_config.clone());
|
let flowy_test = FlowySDKTest::setup_with(server.client_server_config.clone());
|
||||||
Self { server, flowy_test }
|
Self { server, flowy_test }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ impl DocumentTest {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct ScriptContext {
|
struct ScriptContext {
|
||||||
client_edit_context: Option<Arc<ClientEditDocContext>>,
|
client_edit_context: Option<Arc<ClientEditDocContext>>,
|
||||||
flowy_test: FlowyTest,
|
client_sdk: FlowySDKTest,
|
||||||
client_user_session: Arc<UserSession>,
|
client_user_session: Arc<UserSession>,
|
||||||
server_doc_manager: Arc<DocManager>,
|
server_doc_manager: Arc<DocManager>,
|
||||||
server_pg_pool: Data<PgPool>,
|
server_pg_pool: Data<PgPool>,
|
||||||
@ -58,13 +58,13 @@ struct ScriptContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptContext {
|
impl ScriptContext {
|
||||||
async fn new(flowy_test: FlowyTest, server: TestServer) -> Self {
|
async fn new(client_sdk: FlowySDKTest, server: TestServer) -> Self {
|
||||||
let user_session = flowy_test.sdk.user_session.clone();
|
let user_session = client_sdk.user_session.clone();
|
||||||
let doc_id = create_doc(&flowy_test).await;
|
let doc_id = create_doc(&client_sdk).await;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
client_edit_context: None,
|
client_edit_context: None,
|
||||||
flowy_test,
|
client_sdk,
|
||||||
client_user_session: user_session,
|
client_user_session: user_session,
|
||||||
server_doc_manager: server.app_ctx.doc_biz.manager.clone(),
|
server_doc_manager: server.app_ctx.doc_biz.manager.clone(),
|
||||||
server_pg_pool: Data::new(server.pg_pool.clone()),
|
server_pg_pool: Data::new(server.pg_pool.clone()),
|
||||||
@ -73,7 +73,7 @@ impl ScriptContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn open_doc(&mut self) {
|
async fn open_doc(&mut self) {
|
||||||
let flowy_document = self.flowy_test.sdk.flowy_document.clone();
|
let flowy_document = self.client_sdk.flowy_document.clone();
|
||||||
let doc_id = self.doc_id.clone();
|
let doc_id = self.doc_id.clone();
|
||||||
|
|
||||||
let edit_context = flowy_document.open(DocIdentifier { doc_id }).await.unwrap();
|
let edit_context = flowy_document.open(DocIdentifier { doc_id }).await.unwrap();
|
||||||
@ -161,7 +161,7 @@ fn assert_eq(expect: &str, receive: &str) {
|
|||||||
assert_eq!(target_delta, expected_delta);
|
assert_eq!(target_delta, expected_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_doc(flowy_test: &FlowyTest) -> String {
|
async fn create_doc(flowy_test: &FlowySDKTest) -> String {
|
||||||
let view_test = ViewTest::new(flowy_test).await;
|
let view_test = ViewTest::new(flowy_test).await;
|
||||||
view_test.view.id
|
view_test.view.id
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use flowy_core::entities::{
|
|||||||
trash::{TrashIdentifier, TrashType},
|
trash::{TrashIdentifier, TrashType},
|
||||||
view::*,
|
view::*,
|
||||||
};
|
};
|
||||||
use flowy_test::workspace::*;
|
use flowy_test::helper::*;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
|
@ -3,12 +3,12 @@ use flowy_core::entities::{
|
|||||||
trash::{TrashIdentifier, TrashType},
|
trash::{TrashIdentifier, TrashType},
|
||||||
view::*,
|
view::*,
|
||||||
};
|
};
|
||||||
use flowy_test::{workspace::*, FlowyTest};
|
use flowy_test::{helper::*, FlowySDKTest};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
async fn view_delete() {
|
async fn view_delete() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let _ = test.init_user().await;
|
let _ = test.init_user().await;
|
||||||
|
|
||||||
let test = ViewTest::new(&test).await;
|
let test = ViewTest::new(&test).await;
|
||||||
@ -21,7 +21,7 @@ async fn view_delete() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn view_delete_then_putback() {
|
async fn view_delete_then_putback() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let _ = test.init_user().await;
|
let _ = test.init_user().await;
|
||||||
|
|
||||||
let test = ViewTest::new(&test).await;
|
let test = ViewTest::new(&test).await;
|
||||||
@ -44,7 +44,7 @@ async fn view_delete_then_putback() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn view_delete_all() {
|
async fn view_delete_all() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let _ = test.init_user().await;
|
let _ = test.init_user().await;
|
||||||
|
|
||||||
let test = ViewTest::new(&test).await;
|
let test = ViewTest::new(&test).await;
|
||||||
@ -66,7 +66,7 @@ async fn view_delete_all() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn view_delete_all_permanent() {
|
async fn view_delete_all_permanent() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let _ = test.init_user().await;
|
let _ = test.init_user().await;
|
||||||
|
|
||||||
let test = ViewTest::new(&test).await;
|
let test = ViewTest::new(&test).await;
|
||||||
@ -85,7 +85,7 @@ async fn view_delete_all_permanent() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn view_open_doc() {
|
async fn view_open_doc() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let _ = test.init_user().await;
|
let _ = test.init_user().await;
|
||||||
|
|
||||||
let test = ViewTest::new(&test).await;
|
let test = ViewTest::new(&test).await;
|
||||||
|
@ -3,7 +3,7 @@ use flowy_core::{
|
|||||||
event::WorkspaceEvent::*,
|
event::WorkspaceEvent::*,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use flowy_test::{builder::*, workspace::*, FlowyTest};
|
use flowy_test::{event_builder::*, helper::*, FlowySDKTest};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn workspace_read_all() {
|
async fn workspace_read_all() {
|
||||||
@ -42,13 +42,13 @@ async fn workspace_create_with_apps() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn workspace_create_with_invalid_name() {
|
async fn workspace_create_with_invalid_name() {
|
||||||
for (name, code) in invalid_workspace_name_test_case() {
|
for (name, code) in invalid_workspace_name_test_case() {
|
||||||
let sdk = FlowyTest::setup().sdk;
|
let sdk = FlowySDKTest::setup();
|
||||||
let request = CreateWorkspaceRequest {
|
let request = CreateWorkspaceRequest {
|
||||||
name,
|
name,
|
||||||
desc: "".to_owned(),
|
desc: "".to_owned(),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
FlowyWorkspaceTest::new(sdk)
|
CoreModuleEventBuilder::new(sdk)
|
||||||
.event(CreateWorkspace)
|
.event(CreateWorkspace)
|
||||||
.request(request)
|
.request(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -62,14 +62,14 @@ 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 = FlowySDKTest::setup();
|
||||||
for (name, code) 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(),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
.event(CreateWorkspace)
|
.event(CreateWorkspace)
|
||||||
.request(request)
|
.request(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
|
@ -39,6 +39,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
serde_json = {version = "1.0"}
|
serde_json = {version = "1.0"}
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
futures-core = { version = "0.3", default-features = false }
|
futures-core = { version = "0.3", default-features = false }
|
||||||
|
futures-util = "0.3.15"
|
||||||
byteorder = {version = "1.3.4"}
|
byteorder = {version = "1.3.4"}
|
||||||
async-stream = "0.3.2"
|
async-stream = "0.3.2"
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
|
@ -46,7 +46,7 @@ impl DocError {
|
|||||||
static_doc_error!(ws, ErrorCode::WsConnectError);
|
static_doc_error!(ws, ErrorCode::WsConnectError);
|
||||||
static_doc_error!(internal, ErrorCode::InternalError);
|
static_doc_error!(internal, ErrorCode::InternalError);
|
||||||
static_doc_error!(unauthorized, ErrorCode::UserUnauthorized);
|
static_doc_error!(unauthorized, ErrorCode::UserUnauthorized);
|
||||||
static_doc_error!(record_not_found, ErrorCode::DocNotfound);
|
static_doc_error!(doc_not_found, ErrorCode::DocNotfound);
|
||||||
static_doc_error!(duplicate_rev, ErrorCode::DuplicateRevision);
|
static_doc_error!(duplicate_rev, ErrorCode::DuplicateRevision);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ impl std::default::Default for ErrorCode {
|
|||||||
impl std::convert::From<flowy_database::Error> for DocError {
|
impl std::convert::From<flowy_database::Error> for DocError {
|
||||||
fn from(error: flowy_database::Error) -> Self {
|
fn from(error: flowy_database::Error) -> Self {
|
||||||
match error {
|
match error {
|
||||||
flowy_database::Error::NotFound => DocError::record_not_found().context(error),
|
flowy_database::Error::NotFound => DocError::doc_not_found().context(error),
|
||||||
_ => DocError::internal().context(error),
|
_ => DocError::internal().context(error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::DocError,
|
errors::DocError,
|
||||||
services::{
|
services::{
|
||||||
doc::{doc_controller::DocController, edit::ClientDocEditor},
|
doc::{controller::DocController, edit::ClientDocEditor},
|
||||||
server::construct_doc_server,
|
server::construct_doc_server,
|
||||||
ws::WsDocumentManager,
|
ws::WsDocumentManager,
|
||||||
},
|
},
|
||||||
|
@ -46,4 +46,4 @@ impl DocCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn doc_not_found() -> DocError { DocError::record_not_found().context("Doc is close or you should call open first") }
|
fn doc_not_found() -> DocError { DocError::doc_not_found().context("Doc is close or you should call open first") }
|
||||||
|
@ -122,7 +122,7 @@ struct RevisionServerImpl {
|
|||||||
|
|
||||||
impl RevisionServer for RevisionServerImpl {
|
impl RevisionServer for RevisionServerImpl {
|
||||||
#[tracing::instrument(level = "debug", skip(self))]
|
#[tracing::instrument(level = "debug", skip(self))]
|
||||||
fn fetch_document_from_remote(&self, doc_id: &str) -> ResultFuture<Doc, DocError> {
|
fn fetch_document(&self, doc_id: &str) -> ResultFuture<Doc, DocError> {
|
||||||
let params = DocIdentifier {
|
let params = DocIdentifier {
|
||||||
doc_id: doc_id.to_string(),
|
doc_id: doc_id.to_string(),
|
||||||
};
|
};
|
||||||
@ -131,7 +131,7 @@ impl RevisionServer for RevisionServerImpl {
|
|||||||
|
|
||||||
ResultFuture::new(async move {
|
ResultFuture::new(async move {
|
||||||
match server.read_doc(&token, params).await? {
|
match server.read_doc(&token, params).await? {
|
||||||
None => Err(DocError::record_not_found().context("Remote doesn't have this document")),
|
None => Err(DocError::doc_not_found().context("Remote doesn't have this document")),
|
||||||
Some(doc) => Ok(doc),
|
Some(doc) => Ok(doc),
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -209,7 +209,7 @@ impl ClientDocEditor {
|
|||||||
async fn handle_push_rev(&self, bytes: Bytes) -> DocResult<()> {
|
async fn handle_push_rev(&self, bytes: Bytes) -> DocResult<()> {
|
||||||
// Transform the revision
|
// Transform the revision
|
||||||
let (ret, rx) = oneshot::channel::<DocumentResult<TransformDeltas>>();
|
let (ret, rx) = oneshot::channel::<DocumentResult<TransformDeltas>>();
|
||||||
let _ = self.edit_tx.send(EditCommand::RemoteRevision { bytes, ret });
|
let _ = self.edit_tx.send(EditCommand::ProcessRemoteRevision { bytes, ret });
|
||||||
let TransformDeltas {
|
let TransformDeltas {
|
||||||
client_prime,
|
client_prime,
|
||||||
server_prime,
|
server_prime,
|
||||||
@ -268,12 +268,12 @@ impl ClientDocEditor {
|
|||||||
let revision = self.rev_manager.mk_revisions(range).await?;
|
let revision = self.rev_manager.mk_revisions(range).await?;
|
||||||
let _ = self.ws.send(revision.into());
|
let _ = self.ws.send(revision.into());
|
||||||
},
|
},
|
||||||
WsDataType::NewDocUser => {},
|
|
||||||
WsDataType::Acked => {
|
WsDataType::Acked => {
|
||||||
let rev_id = RevId::try_from(bytes)?;
|
let rev_id = RevId::try_from(bytes)?;
|
||||||
let _ = self.rev_manager.ack_revision(rev_id).await?;
|
let _ = self.rev_manager.ack_revision(rev_id).await?;
|
||||||
},
|
},
|
||||||
WsDataType::Conflict => {},
|
WsDataType::Conflict => {},
|
||||||
|
WsDataType::NewDocUser => {},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ impl EditCommandQueue {
|
|||||||
let result = self.composed_delta(delta).await;
|
let result = self.composed_delta(delta).await;
|
||||||
let _ = ret.send(result);
|
let _ = ret.send(result);
|
||||||
},
|
},
|
||||||
EditCommand::RemoteRevision { bytes, ret } => {
|
EditCommand::ProcessRemoteRevision { bytes, ret } => {
|
||||||
let revision = Revision::try_from(bytes)?;
|
let revision = Revision::try_from(bytes)?;
|
||||||
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
|
let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
|
||||||
let rev_id: RevId = revision.rev_id.into();
|
let rev_id: RevId = revision.rev_id.into();
|
||||||
@ -131,7 +131,7 @@ pub(crate) enum EditCommand {
|
|||||||
delta: RichTextDelta,
|
delta: RichTextDelta,
|
||||||
ret: Ret<()>,
|
ret: Ret<()>,
|
||||||
},
|
},
|
||||||
RemoteRevision {
|
ProcessRemoteRevision {
|
||||||
bytes: Bytes,
|
bytes: Bytes,
|
||||||
ret: Ret<TransformDeltas>,
|
ret: Ret<TransformDeltas>,
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
pub mod edit;
|
pub mod edit;
|
||||||
pub mod revision;
|
pub mod revision;
|
||||||
|
|
||||||
pub(crate) mod doc_controller;
|
pub(crate) mod controller;
|
||||||
|
@ -22,7 +22,7 @@ use lib_ot::{
|
|||||||
};
|
};
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::RwLock,
|
sync::{mpsc, RwLock},
|
||||||
task::{spawn_blocking, JoinHandle},
|
task::{spawn_blocking, JoinHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,13 +107,15 @@ impl RevisionCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_document(&self) -> DocResult<Doc> {
|
pub async fn load_document(&self) -> DocResult<Doc> {
|
||||||
let result = fetch_from_local(&self.doc_id, self.dish_cache.clone()).await;
|
// Loading the document from disk and it will be sync with server.
|
||||||
|
let result = load_from_disk(&self.doc_id, self.memory_cache.clone(), self.dish_cache.clone()).await;
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
let doc = self.server.fetch_document_from_remote(&self.doc_id).await?;
|
// The document doesn't exist in local. Try load from server
|
||||||
|
let doc = self.server.fetch_document(&self.doc_id).await?;
|
||||||
let delta_data = doc.data.as_bytes();
|
let delta_data = doc.data.as_bytes();
|
||||||
let revision = Revision::new(
|
let revision = Revision::new(
|
||||||
doc.base_rev_id,
|
doc.base_rev_id,
|
||||||
@ -154,21 +156,30 @@ impl RevisionIterator for RevisionCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_from_local(doc_id: &str, disk_cache: Arc<DocRevisionDeskCache>) -> DocResult<Doc> {
|
async fn load_from_disk(
|
||||||
|
doc_id: &str,
|
||||||
|
memory_cache: Arc<RevisionMemoryCache>,
|
||||||
|
disk_cache: Arc<DocRevisionDeskCache>,
|
||||||
|
) -> DocResult<Doc> {
|
||||||
let doc_id = doc_id.to_owned();
|
let doc_id = doc_id.to_owned();
|
||||||
spawn_blocking(move || {
|
let (tx, mut rx) = mpsc::channel(2);
|
||||||
|
let doc = spawn_blocking(move || {
|
||||||
let revisions = disk_cache.read_revisions(&doc_id)?;
|
let revisions = disk_cache.read_revisions(&doc_id)?;
|
||||||
if revisions.is_empty() {
|
if revisions.is_empty() {
|
||||||
return Err(DocError::record_not_found().context("Local doesn't have this document"));
|
return Err(DocError::doc_not_found().context("Local doesn't have this document"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let base_rev_id: RevId = revisions.last().unwrap().base_rev_id.into();
|
let (base_rev_id, rev_id) = revisions.last().unwrap().pair_rev_id();
|
||||||
let rev_id: RevId = revisions.last().unwrap().rev_id.into();
|
|
||||||
let mut delta = RichTextDelta::new();
|
let mut delta = RichTextDelta::new();
|
||||||
for (_, revision) in revisions.into_iter().enumerate() {
|
for (_, revision) in revisions.into_iter().enumerate() {
|
||||||
match RichTextDelta::from_bytes(revision.delta_data) {
|
// Opti: revision's clone may cause memory issues
|
||||||
|
match RichTextDelta::from_bytes(revision.clone().delta_data) {
|
||||||
Ok(local_delta) => {
|
Ok(local_delta) => {
|
||||||
delta = delta.compose(&local_delta)?;
|
delta = delta.compose(&local_delta)?;
|
||||||
|
match tx.blocking_send(revision) {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => log::error!("Load document from disk error: {}", e),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Deserialize delta from revision failed: {}", e);
|
log::error!("Deserialize delta from revision failed: {}", e);
|
||||||
@ -176,51 +187,36 @@ async fn fetch_from_local(doc_id: &str, disk_cache: Arc<DocRevisionDeskCache>) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
correct_delta_if_need(&mut delta);
|
||||||
validate_delta(&doc_id, disk_cache, &delta);
|
|
||||||
|
|
||||||
match delta.ops.last() {
|
|
||||||
None => {},
|
|
||||||
Some(op) => {
|
|
||||||
let data = op.get_data();
|
|
||||||
if !data.ends_with('\n') {
|
|
||||||
delta.ops.push(Operation::Insert("\n".into()))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Result::<Doc, DocError>::Ok(Doc {
|
Result::<Doc, DocError>::Ok(Doc {
|
||||||
id: doc_id,
|
id: doc_id,
|
||||||
data: delta.to_json(),
|
data: delta.to_json(),
|
||||||
rev_id: rev_id.into(),
|
rev_id,
|
||||||
base_rev_id: base_rev_id.into(),
|
base_rev_id,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(internal_error)?
|
.map_err(internal_error)?;
|
||||||
|
|
||||||
|
while let Some(revision) = rx.recv().await {
|
||||||
|
match memory_cache.add_revision(revision).await {
|
||||||
|
Ok(_) => {},
|
||||||
|
Err(e) => log::error!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
fn correct_delta_if_need(delta: &mut RichTextDelta) {
|
||||||
fn validate_delta(doc_id: &str, disk_cache: Arc<DocRevisionDeskCache>, delta: &RichTextDelta) {
|
|
||||||
if delta.ops.last().is_none() {
|
if delta.ops.last().is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = delta.ops.last().as_ref().unwrap().get_data();
|
let data = delta.ops.last().as_ref().unwrap().get_data();
|
||||||
if !data.ends_with('\n') {
|
if !data.ends_with('\n') {
|
||||||
log::error!("The op must end with newline");
|
log::error!("The op must end with newline. Correcting it by inserting newline op");
|
||||||
let result = || {
|
delta.ops.push(Operation::Insert("\n".into()));
|
||||||
let revisions = disk_cache.read_revisions(&doc_id)?;
|
|
||||||
for revision in revisions {
|
|
||||||
let delta = RichTextDelta::from_bytes(revision.delta_data)?;
|
|
||||||
log::error!("Invalid revision: {}:{}", revision.rev_id, delta.to_json());
|
|
||||||
}
|
|
||||||
Ok::<(), DocError>(())
|
|
||||||
};
|
|
||||||
match result() {
|
|
||||||
Ok(_) => {},
|
|
||||||
Err(e) => log::error!("{}", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ use std::sync::Arc;
|
|||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub trait RevisionServer: Send + Sync {
|
pub trait RevisionServer: Send + Sync {
|
||||||
fn fetch_document_from_remote(&self, doc_id: &str) -> ResultFuture<Doc, DocError>;
|
fn fetch_document(&self, doc_id: &str) -> ResultFuture<Doc, DocError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RevisionManager {
|
pub struct RevisionManager {
|
||||||
@ -41,7 +41,7 @@ impl RevisionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn load_document(&mut self) -> DocResult<RichTextDelta> {
|
pub async fn load_document(&mut self) -> DocResult<RichTextDelta> {
|
||||||
let doc = self.cache.fetch_document().await?;
|
let doc = self.cache.load_document().await?;
|
||||||
self.update_rev_id_counter_value(doc.rev_id);
|
self.update_rev_id_counter_value(doc.rev_id);
|
||||||
Ok(doc.delta()?)
|
Ok(doc.delta()?)
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ impl RevisionUploadStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn send_next_revision(&self) -> DocResult<()> {
|
async fn send_next_revision(&self) -> DocResult<()> {
|
||||||
|
log::debug!("😁Tick");
|
||||||
match self.revisions.next().await? {
|
match self.revisions.next().await? {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
Some(record) => {
|
Some(record) => {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![allow(clippy::module_inception)]
|
#![allow(clippy::module_inception)]
|
||||||
mod attribute_test;
|
mod attribute_test;
|
||||||
mod op_test;
|
mod op_test;
|
||||||
|
mod revision_test;
|
||||||
mod serde_test;
|
mod serde_test;
|
||||||
mod undo_redo_test;
|
mod undo_redo_test;
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
use flowy_test::editor::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn create_doc() {
|
||||||
|
let test = EditorTest::new().await;
|
||||||
|
let _editor = test.create_doc().await;
|
||||||
|
println!("123");
|
||||||
|
}
|
22
frontend/rust-lib/flowy-test/src/editor.rs
Normal file
22
frontend/rust-lib/flowy-test/src/editor.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use crate::{helper::ViewTest, FlowySDKTest};
|
||||||
|
use flowy_document::services::doc::edit::ClientDocEditor;
|
||||||
|
use flowy_document_infra::entities::doc::DocIdentifier;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct EditorTest {
|
||||||
|
pub sdk: FlowySDKTest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EditorTest {
|
||||||
|
pub async fn new() -> Self {
|
||||||
|
let sdk = FlowySDKTest::setup();
|
||||||
|
let _ = sdk.init_user().await;
|
||||||
|
Self { sdk }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_doc(&self) -> Arc<ClientDocEditor> {
|
||||||
|
let test = ViewTest::new(&self.sdk).await;
|
||||||
|
let doc_identifier: DocIdentifier = test.view.id.clone().into();
|
||||||
|
self.sdk.flowy_document.open(doc_identifier).await.unwrap()
|
||||||
|
}
|
||||||
|
}
|
@ -5,37 +5,36 @@ use std::{
|
|||||||
hash::Hash,
|
hash::Hash,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::FlowyTestSDK;
|
use crate::FlowySDKTest;
|
||||||
use lib_dispatch::prelude::*;
|
|
||||||
|
|
||||||
use flowy_core::errors::WorkspaceError;
|
use flowy_core::errors::WorkspaceError;
|
||||||
use flowy_sdk::*;
|
|
||||||
use flowy_user::errors::UserError;
|
use flowy_user::errors::UserError;
|
||||||
|
use lib_dispatch::prelude::*;
|
||||||
use std::{convert::TryFrom, marker::PhantomData, sync::Arc};
|
use std::{convert::TryFrom, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
pub type FlowyWorkspaceTest = Builder<WorkspaceError>;
|
pub type CoreModuleEventBuilder = EventBuilder<WorkspaceError>;
|
||||||
impl FlowyWorkspaceTest {
|
impl CoreModuleEventBuilder {
|
||||||
pub fn new(sdk: FlowyTestSDK) -> Self { Builder::test(TestContext::new(sdk)) }
|
pub fn new(sdk: FlowySDKTest) -> Self { EventBuilder::test(TestContext::new(sdk)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UserTest = Builder<UserError>;
|
pub type UserModuleEventBuilder = EventBuilder<UserError>;
|
||||||
impl UserTest {
|
impl UserModuleEventBuilder {
|
||||||
pub fn new(sdk: FlowyTestSDK) -> Self { Builder::test(TestContext::new(sdk)) }
|
pub fn new(sdk: FlowySDKTest) -> Self { EventBuilder::test(TestContext::new(sdk)) }
|
||||||
pub fn user_profile(&self) -> &Option<UserProfile> { &self.user_profile }
|
pub fn user_profile(&self) -> &Option<UserProfile> { &self.user_profile }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Builder<E> {
|
pub struct EventBuilder<E> {
|
||||||
context: TestContext,
|
context: TestContext,
|
||||||
user_profile: Option<UserProfile>,
|
user_profile: Option<UserProfile>,
|
||||||
err_phantom: PhantomData<E>,
|
err_phantom: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> Builder<E>
|
impl<E> EventBuilder<E>
|
||||||
where
|
where
|
||||||
E: FromBytes + Debug,
|
E: FromBytes + Debug,
|
||||||
{
|
{
|
||||||
pub(crate) fn test(context: TestContext) -> Self {
|
fn test(context: TestContext) -> Self {
|
||||||
Self {
|
Self {
|
||||||
context,
|
context,
|
||||||
user_profile: None,
|
user_profile: None,
|
||||||
@ -111,8 +110,6 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sdk(&self) -> FlowySDK { self.context.sdk.clone() }
|
|
||||||
|
|
||||||
fn dispatch(&self) -> Arc<EventDispatcher> { self.context.sdk.dispatcher() }
|
fn dispatch(&self) -> Arc<EventDispatcher> { self.context.sdk.dispatcher() }
|
||||||
|
|
||||||
fn get_response(&self) -> EventResponse {
|
fn get_response(&self) -> EventResponse {
|
||||||
@ -128,13 +125,13 @@ where
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TestContext {
|
pub struct TestContext {
|
||||||
sdk: FlowyTestSDK,
|
pub sdk: FlowySDKTest,
|
||||||
request: Option<ModuleRequest>,
|
request: Option<ModuleRequest>,
|
||||||
response: Option<EventResponse>,
|
response: Option<EventResponse>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestContext {
|
impl TestContext {
|
||||||
pub fn new(sdk: FlowyTestSDK) -> Self {
|
pub fn new(sdk: FlowySDKTest) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sdk,
|
sdk,
|
||||||
request: None,
|
request: None,
|
@ -1,19 +1,282 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use lib_dispatch::prelude::{EventDispatcher, ModuleRequest, ToBytes};
|
|
||||||
use lib_infra::{kv::KV, uuid};
|
|
||||||
|
|
||||||
use flowy_core::{
|
use flowy_core::{
|
||||||
entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest, Workspace},
|
entities::{
|
||||||
errors::WorkspaceError,
|
app::*,
|
||||||
event::WorkspaceEvent::{CreateWorkspace, OpenWorkspace},
|
trash::{RepeatedTrash, TrashIdentifier},
|
||||||
|
view::*,
|
||||||
|
workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest, Workspace, *},
|
||||||
|
},
|
||||||
|
errors::{ErrorCode, WorkspaceError},
|
||||||
|
event::WorkspaceEvent::{CreateWorkspace, OpenWorkspace, *},
|
||||||
};
|
};
|
||||||
|
use flowy_document_infra::entities::doc::Doc;
|
||||||
use flowy_user::{
|
use flowy_user::{
|
||||||
entities::{SignInRequest, SignUpRequest, UserProfile},
|
entities::{SignInRequest, SignUpRequest, UserProfile},
|
||||||
errors::UserError,
|
errors::UserError,
|
||||||
event::UserEvent::{SignIn, SignOut, SignUp},
|
event::UserEvent::{SignIn, SignOut, SignUp},
|
||||||
};
|
};
|
||||||
|
use lib_dispatch::prelude::{EventDispatcher, ModuleRequest, ToBytes};
|
||||||
|
use lib_infra::{kv::KV, uuid};
|
||||||
use std::{fs, path::PathBuf, sync::Arc};
|
use std::{fs, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
|
pub struct WorkspaceTest {
|
||||||
|
pub sdk: FlowySDKTest,
|
||||||
|
pub workspace: Workspace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkspaceTest {
|
||||||
|
pub async fn new() -> Self {
|
||||||
|
let sdk = FlowySDKTest::setup();
|
||||||
|
let _ = sdk.init_user().await;
|
||||||
|
let workspace = create_workspace(&sdk, "Workspace", "").await;
|
||||||
|
open_workspace(&sdk, &workspace.id).await;
|
||||||
|
|
||||||
|
Self { sdk, workspace }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppTest {
|
||||||
|
pub sdk: FlowySDKTest,
|
||||||
|
pub workspace: Workspace,
|
||||||
|
pub app: App,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppTest {
|
||||||
|
pub async fn new() -> Self {
|
||||||
|
let sdk = FlowySDKTest::setup();
|
||||||
|
let _ = sdk.init_user().await;
|
||||||
|
let workspace = create_workspace(&sdk, "Workspace", "").await;
|
||||||
|
open_workspace(&sdk, &workspace.id).await;
|
||||||
|
let app = create_app(&sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
||||||
|
Self { sdk, workspace, app }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn move_app_to_trash(&self) {
|
||||||
|
let request = UpdateAppRequest {
|
||||||
|
app_id: self.app.id.clone(),
|
||||||
|
name: None,
|
||||||
|
desc: None,
|
||||||
|
color_style: None,
|
||||||
|
is_trash: Some(true),
|
||||||
|
};
|
||||||
|
update_app(&self.sdk, request).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ViewTest {
|
||||||
|
pub sdk: FlowySDKTest,
|
||||||
|
pub workspace: Workspace,
|
||||||
|
pub app: App,
|
||||||
|
pub view: View,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewTest {
|
||||||
|
pub async fn new(sdk: &FlowySDKTest) -> Self {
|
||||||
|
let workspace = create_workspace(&sdk, "Workspace", "").await;
|
||||||
|
open_workspace(&sdk, &workspace.id).await;
|
||||||
|
let app = create_app(&sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
||||||
|
let view = create_view(&sdk, &app.id).await;
|
||||||
|
Self {
|
||||||
|
sdk: sdk.clone(),
|
||||||
|
workspace,
|
||||||
|
app,
|
||||||
|
view,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_views(&self, view_ids: Vec<String>) {
|
||||||
|
let request = QueryViewRequest { view_ids };
|
||||||
|
delete_view(&self.sdk, request).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_views_permanent(&self, view_ids: Vec<String>) {
|
||||||
|
let request = QueryViewRequest { view_ids };
|
||||||
|
delete_view(&self.sdk, request).await;
|
||||||
|
|
||||||
|
CoreModuleEventBuilder::new(self.sdk.clone())
|
||||||
|
.event(DeleteAll)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {
|
||||||
|
vec![
|
||||||
|
("".to_owned(), ErrorCode::WorkspaceNameInvalid),
|
||||||
|
("1234".repeat(100), ErrorCode::WorkspaceNameTooLong),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_workspace(sdk: &FlowySDKTest, name: &str, desc: &str) -> Workspace {
|
||||||
|
let request = CreateWorkspaceRequest {
|
||||||
|
name: name.to_owned(),
|
||||||
|
desc: desc.to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let workspace = CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(CreateWorkspace)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<Workspace>();
|
||||||
|
workspace
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn open_workspace(sdk: &FlowySDKTest, workspace_id: &str) {
|
||||||
|
let request = QueryWorkspaceRequest {
|
||||||
|
workspace_id: Some(workspace_id.to_owned()),
|
||||||
|
};
|
||||||
|
let _ = CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(OpenWorkspace)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_workspace(sdk: &FlowySDKTest, request: QueryWorkspaceRequest) -> Vec<Workspace> {
|
||||||
|
let repeated_workspace = CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(ReadWorkspaces)
|
||||||
|
.request(request.clone())
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<RepeatedWorkspace>();
|
||||||
|
|
||||||
|
let workspaces;
|
||||||
|
if let Some(workspace_id) = &request.workspace_id {
|
||||||
|
workspaces = repeated_workspace
|
||||||
|
.into_inner()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|workspace| &workspace.id == workspace_id)
|
||||||
|
.collect::<Vec<Workspace>>();
|
||||||
|
debug_assert_eq!(workspaces.len(), 1);
|
||||||
|
} else {
|
||||||
|
workspaces = repeated_workspace.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaces
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_app(sdk: &FlowySDKTest, name: &str, desc: &str, workspace_id: &str) -> App {
|
||||||
|
let create_app_request = CreateAppRequest {
|
||||||
|
workspace_id: workspace_id.to_owned(),
|
||||||
|
name: name.to_string(),
|
||||||
|
desc: desc.to_string(),
|
||||||
|
color_style: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let app = CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(CreateApp)
|
||||||
|
.request(create_app_request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<App>();
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_app(sdk: &FlowySDKTest, app_id: &str) {
|
||||||
|
let delete_app_request = AppIdentifier {
|
||||||
|
app_id: app_id.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(DeleteApp)
|
||||||
|
.request(delete_app_request)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_app(sdk: &FlowySDKTest, request: UpdateAppRequest) {
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(UpdateApp)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_app(sdk: &FlowySDKTest, request: QueryAppRequest) -> App {
|
||||||
|
let app = CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(ReadApp)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<App>();
|
||||||
|
|
||||||
|
app
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_view_with_request(sdk: &FlowySDKTest, request: CreateViewRequest) -> View {
|
||||||
|
let view = CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(CreateView)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<View>();
|
||||||
|
view
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_view(sdk: &FlowySDKTest, app_id: &str) -> View {
|
||||||
|
let request = CreateViewRequest {
|
||||||
|
belong_to_id: app_id.to_string(),
|
||||||
|
name: "View A".to_string(),
|
||||||
|
desc: "".to_string(),
|
||||||
|
thumbnail: Some("http://1.png".to_string()),
|
||||||
|
view_type: ViewType::Doc,
|
||||||
|
};
|
||||||
|
|
||||||
|
create_view_with_request(sdk, request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_view(sdk: &FlowySDKTest, request: UpdateViewRequest) {
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(UpdateView)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_view(sdk: &FlowySDKTest, request: QueryViewRequest) -> View {
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(ReadView)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<View>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_view(sdk: &FlowySDKTest, request: QueryViewRequest) {
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(DeleteView)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read_trash(sdk: &FlowySDKTest) -> RepeatedTrash {
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(ReadTrash)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<RepeatedTrash>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn putback_trash(sdk: &FlowySDKTest, id: TrashIdentifier) {
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(PutbackTrash)
|
||||||
|
.request(id)
|
||||||
|
.async_send()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn open_view(sdk: &FlowySDKTest, request: QueryViewRequest) -> Doc {
|
||||||
|
CoreModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(OpenView)
|
||||||
|
.request(request)
|
||||||
|
.async_send()
|
||||||
|
.await
|
||||||
|
.parse::<Doc>()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root_dir() -> String {
|
pub fn root_dir() -> String {
|
||||||
// https://doc.rust-lang.org/cargo/reference/environment-variables.html
|
// https://doc.rust-lang.org/cargo/reference/environment-variables.html
|
||||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| "./".to_owned());
|
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| "./".to_owned());
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
pub mod builder;
|
pub mod editor;
|
||||||
mod helper;
|
pub mod event_builder;
|
||||||
pub mod workspace;
|
pub mod helper;
|
||||||
|
|
||||||
use crate::helper::*;
|
use crate::helper::*;
|
||||||
use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration};
|
use backend_service::configuration::{get_client_server_configuration, ClientServerConfiguration};
|
||||||
@ -9,40 +9,40 @@ use flowy_user::entities::UserProfile;
|
|||||||
use lib_infra::uuid;
|
use lib_infra::uuid;
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{builder::*, helper::*, *};
|
pub use crate::{event_builder::*, helper::*, *};
|
||||||
pub use lib_dispatch::prelude::*;
|
pub use lib_dispatch::prelude::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FlowyTestSDK = FlowySDK;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FlowyTest {
|
pub struct FlowySDKTest(pub FlowySDK);
|
||||||
pub sdk: FlowyTestSDK,
|
|
||||||
|
impl std::ops::Deref for FlowySDKTest {
|
||||||
|
type Target = FlowySDK;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowyTest {
|
impl FlowySDKTest {
|
||||||
pub fn setup() -> Self {
|
pub fn setup() -> Self {
|
||||||
let server_config = get_client_server_configuration().unwrap();
|
let server_config = get_client_server_configuration().unwrap();
|
||||||
let test = Self::setup_with(server_config);
|
let sdk = Self::setup_with(server_config);
|
||||||
std::mem::forget(test.sdk.dispatcher());
|
std::mem::forget(sdk.dispatcher());
|
||||||
test
|
sdk
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn sign_up(&self) -> SignUpContext {
|
|
||||||
let context = async_sign_up(self.sdk.dispatcher()).await;
|
|
||||||
context
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn init_user(&self) -> UserProfile {
|
|
||||||
let context = async_sign_up(self.sdk.dispatcher()).await;
|
|
||||||
context.user_profile
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_with(server_config: ClientServerConfiguration) -> Self {
|
pub fn setup_with(server_config: ClientServerConfiguration) -> Self {
|
||||||
let config = FlowySDKConfig::new(&root_dir(), server_config, &uuid()).log_filter("debug");
|
let config = FlowySDKConfig::new(&root_dir(), server_config, &uuid()).log_filter("debug");
|
||||||
let sdk = FlowySDK::new(config);
|
let sdk = FlowySDK::new(config);
|
||||||
Self { sdk }
|
Self(sdk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sdk(&self) -> FlowyTestSDK { self.sdk.clone() }
|
pub async fn sign_up(&self) -> SignUpContext {
|
||||||
|
let context = async_sign_up(self.0.dispatcher()).await;
|
||||||
|
context
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init_user(&self) -> UserProfile {
|
||||||
|
let context = async_sign_up(self.0.dispatcher()).await;
|
||||||
|
context.user_profile
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,276 +0,0 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
use flowy_core::{
|
|
||||||
entities::{
|
|
||||||
app::*,
|
|
||||||
trash::{RepeatedTrash, TrashIdentifier},
|
|
||||||
view::*,
|
|
||||||
workspace::*,
|
|
||||||
},
|
|
||||||
errors::ErrorCode,
|
|
||||||
event::WorkspaceEvent::*,
|
|
||||||
};
|
|
||||||
use flowy_document_infra::entities::doc::Doc;
|
|
||||||
|
|
||||||
pub struct WorkspaceTest {
|
|
||||||
pub sdk: FlowyTestSDK,
|
|
||||||
pub workspace: Workspace,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorkspaceTest {
|
|
||||||
pub async fn new() -> Self {
|
|
||||||
let test = FlowyTest::setup();
|
|
||||||
let _ = test.init_user().await;
|
|
||||||
let workspace = create_workspace(&test.sdk, "Workspace", "").await;
|
|
||||||
open_workspace(&test.sdk, &workspace.id).await;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
sdk: test.sdk,
|
|
||||||
workspace,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppTest {
|
|
||||||
pub sdk: FlowyTestSDK,
|
|
||||||
pub workspace: Workspace,
|
|
||||||
pub app: App,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppTest {
|
|
||||||
pub async fn new() -> Self {
|
|
||||||
let test = FlowyTest::setup();
|
|
||||||
let _ = test.init_user().await;
|
|
||||||
let workspace = create_workspace(&test.sdk, "Workspace", "").await;
|
|
||||||
open_workspace(&test.sdk, &workspace.id).await;
|
|
||||||
let app = create_app(&test.sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
|
||||||
Self {
|
|
||||||
sdk: test.sdk,
|
|
||||||
workspace,
|
|
||||||
app,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn move_app_to_trash(&self) {
|
|
||||||
let request = UpdateAppRequest {
|
|
||||||
app_id: self.app.id.clone(),
|
|
||||||
name: None,
|
|
||||||
desc: None,
|
|
||||||
color_style: None,
|
|
||||||
is_trash: Some(true),
|
|
||||||
};
|
|
||||||
update_app(&self.sdk, request).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ViewTest {
|
|
||||||
pub sdk: FlowyTestSDK,
|
|
||||||
pub workspace: Workspace,
|
|
||||||
pub app: App,
|
|
||||||
pub view: View,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ViewTest {
|
|
||||||
pub async fn new(test: &FlowyTest) -> Self {
|
|
||||||
let workspace = create_workspace(&test.sdk, "Workspace", "").await;
|
|
||||||
open_workspace(&test.sdk, &workspace.id).await;
|
|
||||||
let app = create_app(&test.sdk, "App", "AppFlowy GitHub Project", &workspace.id).await;
|
|
||||||
let view = create_view(&test.sdk, &app.id).await;
|
|
||||||
Self {
|
|
||||||
sdk: test.sdk.clone(),
|
|
||||||
workspace,
|
|
||||||
app,
|
|
||||||
view,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_views(&self, view_ids: Vec<String>) {
|
|
||||||
let request = QueryViewRequest { view_ids };
|
|
||||||
delete_view(&self.sdk, request).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_views_permanent(&self, view_ids: Vec<String>) {
|
|
||||||
let request = QueryViewRequest { view_ids };
|
|
||||||
delete_view(&self.sdk, request).await;
|
|
||||||
|
|
||||||
FlowyWorkspaceTest::new(self.sdk.clone())
|
|
||||||
.event(DeleteAll)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn invalid_workspace_name_test_case() -> Vec<(String, ErrorCode)> {
|
|
||||||
vec![
|
|
||||||
("".to_owned(), ErrorCode::WorkspaceNameInvalid),
|
|
||||||
("1234".repeat(100), ErrorCode::WorkspaceNameTooLong),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_workspace(sdk: &FlowyTestSDK, name: &str, desc: &str) -> Workspace {
|
|
||||||
let request = CreateWorkspaceRequest {
|
|
||||||
name: name.to_owned(),
|
|
||||||
desc: desc.to_owned(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let workspace = FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(CreateWorkspace)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<Workspace>();
|
|
||||||
workspace
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn open_workspace(sdk: &FlowyTestSDK, workspace_id: &str) {
|
|
||||||
let request = QueryWorkspaceRequest {
|
|
||||||
workspace_id: Some(workspace_id.to_owned()),
|
|
||||||
};
|
|
||||||
let _ = FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(OpenWorkspace)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn read_workspace(sdk: &FlowyTestSDK, request: QueryWorkspaceRequest) -> Vec<Workspace> {
|
|
||||||
let repeated_workspace = FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(ReadWorkspaces)
|
|
||||||
.request(request.clone())
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<RepeatedWorkspace>();
|
|
||||||
|
|
||||||
let workspaces;
|
|
||||||
if let Some(workspace_id) = &request.workspace_id {
|
|
||||||
workspaces = repeated_workspace
|
|
||||||
.into_inner()
|
|
||||||
.into_iter()
|
|
||||||
.filter(|workspace| &workspace.id == workspace_id)
|
|
||||||
.collect::<Vec<Workspace>>();
|
|
||||||
debug_assert_eq!(workspaces.len(), 1);
|
|
||||||
} else {
|
|
||||||
workspaces = repeated_workspace.items;
|
|
||||||
}
|
|
||||||
|
|
||||||
workspaces
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_app(sdk: &FlowyTestSDK, name: &str, desc: &str, workspace_id: &str) -> App {
|
|
||||||
let create_app_request = CreateAppRequest {
|
|
||||||
workspace_id: workspace_id.to_owned(),
|
|
||||||
name: name.to_string(),
|
|
||||||
desc: desc.to_string(),
|
|
||||||
color_style: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let app = FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(CreateApp)
|
|
||||||
.request(create_app_request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<App>();
|
|
||||||
app
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_app(sdk: &FlowyTestSDK, app_id: &str) {
|
|
||||||
let delete_app_request = AppIdentifier {
|
|
||||||
app_id: app_id.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(DeleteApp)
|
|
||||||
.request(delete_app_request)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_app(sdk: &FlowyTestSDK, request: UpdateAppRequest) {
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(UpdateApp)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn read_app(sdk: &FlowyTestSDK, request: QueryAppRequest) -> App {
|
|
||||||
let app = FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(ReadApp)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<App>();
|
|
||||||
|
|
||||||
app
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_view_with_request(sdk: &FlowyTestSDK, request: CreateViewRequest) -> View {
|
|
||||||
let view = FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(CreateView)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<View>();
|
|
||||||
view
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_view(sdk: &FlowyTestSDK, app_id: &str) -> View {
|
|
||||||
let request = CreateViewRequest {
|
|
||||||
belong_to_id: app_id.to_string(),
|
|
||||||
name: "View A".to_string(),
|
|
||||||
desc: "".to_string(),
|
|
||||||
thumbnail: Some("http://1.png".to_string()),
|
|
||||||
view_type: ViewType::Doc,
|
|
||||||
};
|
|
||||||
|
|
||||||
create_view_with_request(sdk, request).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_view(sdk: &FlowyTestSDK, request: UpdateViewRequest) {
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(UpdateView)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn read_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> View {
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(ReadView)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<View>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_view(sdk: &FlowyTestSDK, request: QueryViewRequest) {
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(DeleteView)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn read_trash(sdk: &FlowyTestSDK) -> RepeatedTrash {
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(ReadTrash)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<RepeatedTrash>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn putback_trash(sdk: &FlowyTestSDK, id: TrashIdentifier) {
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(PutbackTrash)
|
|
||||||
.request(id)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn open_view(sdk: &FlowyTestSDK, request: QueryViewRequest) -> Doc {
|
|
||||||
FlowyWorkspaceTest::new(sdk.clone())
|
|
||||||
.event(OpenView)
|
|
||||||
.request(request)
|
|
||||||
.async_send()
|
|
||||||
.await
|
|
||||||
.parse::<Doc>()
|
|
||||||
}
|
|
@ -1,11 +1,11 @@
|
|||||||
use crate::helper::*;
|
use crate::helper::*;
|
||||||
use flowy_test::{builder::UserTest, FlowyTest};
|
use flowy_test::{event_builder::UserModuleEventBuilder, FlowySDKTest};
|
||||||
use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
|
use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_up_with_invalid_email() {
|
async fn sign_up_with_invalid_email() {
|
||||||
for email in invalid_email_test_case() {
|
for email in invalid_email_test_case() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
let request = SignUpRequest {
|
let request = SignUpRequest {
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
name: valid_name(),
|
name: valid_name(),
|
||||||
@ -13,7 +13,7 @@ async fn sign_up_with_invalid_email() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UserTest::new(test.sdk)
|
UserModuleEventBuilder::new(sdk)
|
||||||
.event(SignUp)
|
.event(SignUp)
|
||||||
.request(request)
|
.request(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -27,14 +27,14 @@ async fn sign_up_with_invalid_email() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_up_with_invalid_password() {
|
async fn sign_up_with_invalid_password() {
|
||||||
for password in invalid_password_test_case() {
|
for password in invalid_password_test_case() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
let request = SignUpRequest {
|
let request = SignUpRequest {
|
||||||
email: random_email(),
|
email: random_email(),
|
||||||
name: valid_name(),
|
name: valid_name(),
|
||||||
password,
|
password,
|
||||||
};
|
};
|
||||||
|
|
||||||
UserTest::new(test.sdk)
|
UserModuleEventBuilder::new(sdk)
|
||||||
.event(SignUp)
|
.event(SignUp)
|
||||||
.request(request)
|
.request(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -45,8 +45,8 @@ async fn sign_up_with_invalid_password() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_in_success() {
|
async fn sign_in_success() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let _ = UserTest::new(test.sdk()).event(SignOut).sync_send();
|
let _ = UserModuleEventBuilder::new(test.clone()).event(SignOut).sync_send();
|
||||||
let sign_up_context = test.sign_up().await;
|
let sign_up_context = test.sign_up().await;
|
||||||
|
|
||||||
let request = SignInRequest {
|
let request = SignInRequest {
|
||||||
@ -55,7 +55,7 @@ async fn sign_in_success() {
|
|||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = UserTest::new(test.sdk())
|
let response = UserModuleEventBuilder::new(test.clone())
|
||||||
.event(SignIn)
|
.event(SignIn)
|
||||||
.request(request)
|
.request(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -67,7 +67,7 @@ async fn sign_in_success() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_in_with_invalid_email() {
|
async fn sign_in_with_invalid_email() {
|
||||||
for email in invalid_email_test_case() {
|
for email in invalid_email_test_case() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
let request = SignInRequest {
|
let request = SignInRequest {
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
password: login_password(),
|
password: login_password(),
|
||||||
@ -75,7 +75,7 @@ async fn sign_in_with_invalid_email() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UserTest::new(test.sdk)
|
UserModuleEventBuilder::new(sdk)
|
||||||
.event(SignIn)
|
.event(SignIn)
|
||||||
.request(request)
|
.request(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -90,7 +90,7 @@ async fn sign_in_with_invalid_email() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sign_in_with_invalid_password() {
|
async fn sign_in_with_invalid_password() {
|
||||||
for password in invalid_password_test_case() {
|
for password in invalid_password_test_case() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
|
|
||||||
let request = SignInRequest {
|
let request = SignInRequest {
|
||||||
email: random_email(),
|
email: random_email(),
|
||||||
@ -98,7 +98,7 @@ async fn sign_in_with_invalid_password() {
|
|||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
UserTest::new(test.sdk)
|
UserModuleEventBuilder::new(sdk)
|
||||||
.event(SignIn)
|
.event(SignIn)
|
||||||
.request(request)
|
.request(request)
|
||||||
.async_send()
|
.async_send()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pub use flowy_test::{
|
pub use flowy_test::{
|
||||||
builder::*,
|
event_builder::*,
|
||||||
prelude::{login_password, random_email},
|
prelude::{login_password, random_email},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use crate::helper::*;
|
use crate::helper::*;
|
||||||
use flowy_test::{builder::UserTest, FlowyTest};
|
use flowy_test::{event_builder::UserModuleEventBuilder, FlowySDKTest};
|
||||||
use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
|
use flowy_user::{errors::ErrorCode, event::UserEvent::*, prelude::*};
|
||||||
use lib_infra::uuid;
|
use lib_infra::uuid;
|
||||||
use serial_test::*;
|
use serial_test::*;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn user_profile_get_failed() {
|
async fn user_profile_get_failed() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
let result = UserTest::new(test.sdk)
|
let result = UserModuleEventBuilder::new(sdk)
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.assert_error()
|
.assert_error()
|
||||||
.async_send()
|
.async_send()
|
||||||
@ -18,9 +18,9 @@ async fn user_profile_get_failed() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn user_profile_get() {
|
async fn user_profile_get() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let user_profile = test.init_user().await;
|
let user_profile = test.init_user().await;
|
||||||
let user = UserTest::new(test.sdk.clone())
|
let user = UserModuleEventBuilder::new(test.clone())
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
.parse::<UserProfile>();
|
.parse::<UserProfile>();
|
||||||
@ -30,13 +30,16 @@ async fn user_profile_get() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn user_update_with_name() {
|
async fn user_update_with_name() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
let user = test.init_user().await;
|
let user = sdk.init_user().await;
|
||||||
let new_name = "hello_world".to_owned();
|
let new_name = "hello_world".to_owned();
|
||||||
let request = UpdateUserRequest::new(&user.id).name(&new_name);
|
let request = UpdateUserRequest::new(&user.id).name(&new_name);
|
||||||
let _ = UserTest::new(test.sdk()).event(UpdateUser).request(request).sync_send();
|
let _ = UserModuleEventBuilder::new(sdk.clone())
|
||||||
|
.event(UpdateUser)
|
||||||
|
.request(request)
|
||||||
|
.sync_send();
|
||||||
|
|
||||||
let user_profile = UserTest::new(test.sdk())
|
let user_profile = UserModuleEventBuilder::new(sdk.clone())
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.assert_error()
|
.assert_error()
|
||||||
.sync_send()
|
.sync_send()
|
||||||
@ -48,12 +51,15 @@ async fn user_update_with_name() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn user_update_with_email() {
|
async fn user_update_with_email() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
let user = test.init_user().await;
|
let user = sdk.init_user().await;
|
||||||
let new_email = format!("{}@gmail.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 _ = UserModuleEventBuilder::new(sdk.clone())
|
||||||
let user_profile = UserTest::new(test.sdk())
|
.event(UpdateUser)
|
||||||
|
.request(request)
|
||||||
|
.sync_send();
|
||||||
|
let user_profile = UserModuleEventBuilder::new(sdk.clone())
|
||||||
.event(GetUserProfile)
|
.event(GetUserProfile)
|
||||||
.assert_error()
|
.assert_error()
|
||||||
.sync_send()
|
.sync_send()
|
||||||
@ -65,12 +71,12 @@ async fn user_update_with_email() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn user_update_with_password() {
|
async fn user_update_with_password() {
|
||||||
let test = FlowyTest::setup();
|
let sdk = FlowySDKTest::setup();
|
||||||
let user = test.init_user().await;
|
let user = sdk.init_user().await;
|
||||||
let new_password = "H123world!".to_owned();
|
let new_password = "H123world!".to_owned();
|
||||||
let request = UpdateUserRequest::new(&user.id).password(&new_password);
|
let request = UpdateUserRequest::new(&user.id).password(&new_password);
|
||||||
|
|
||||||
let _ = UserTest::new(test.sdk())
|
let _ = UserModuleEventBuilder::new(sdk.clone())
|
||||||
.event(UpdateUser)
|
.event(UpdateUser)
|
||||||
.request(request)
|
.request(request)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
@ -80,12 +86,12 @@ async fn user_update_with_password() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn user_update_with_invalid_email() {
|
async fn user_update_with_invalid_email() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let user = test.init_user().await;
|
let user = test.init_user().await;
|
||||||
for email in invalid_email_test_case() {
|
for email in invalid_email_test_case() {
|
||||||
let request = UpdateUserRequest::new(&user.id).email(&email);
|
let request = UpdateUserRequest::new(&user.id).email(&email);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
UserTest::new(test.sdk())
|
UserModuleEventBuilder::new(test.clone())
|
||||||
.event(UpdateUser)
|
.event(UpdateUser)
|
||||||
.request(request)
|
.request(request)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
@ -99,12 +105,12 @@ async fn user_update_with_invalid_email() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn user_update_with_invalid_password() {
|
async fn user_update_with_invalid_password() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let user = test.init_user().await;
|
let user = test.init_user().await;
|
||||||
for password in invalid_password_test_case() {
|
for password in invalid_password_test_case() {
|
||||||
let request = UpdateUserRequest::new(&user.id).password(&password);
|
let request = UpdateUserRequest::new(&user.id).password(&password);
|
||||||
|
|
||||||
UserTest::new(test.sdk())
|
UserModuleEventBuilder::new(test.clone())
|
||||||
.event(UpdateUser)
|
.event(UpdateUser)
|
||||||
.request(request)
|
.request(request)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
@ -115,10 +121,10 @@ async fn user_update_with_invalid_password() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn user_update_with_invalid_name() {
|
async fn user_update_with_invalid_name() {
|
||||||
let test = FlowyTest::setup();
|
let test = FlowySDKTest::setup();
|
||||||
let user = test.init_user().await;
|
let user = test.init_user().await;
|
||||||
let request = UpdateUserRequest::new(&user.id).name("");
|
let request = UpdateUserRequest::new(&user.id).name("");
|
||||||
UserTest::new(test.sdk())
|
UserModuleEventBuilder::new(test.clone())
|
||||||
.event(UpdateUser)
|
.event(UpdateUser)
|
||||||
.request(request)
|
.request(request)
|
||||||
.sync_send()
|
.sync_send()
|
||||||
|
@ -6,8 +6,11 @@ use std::convert::{TryFrom, TryInto};
|
|||||||
|
|
||||||
#[derive(Debug, Clone, ProtoBuf_Enum, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, ProtoBuf_Enum, Eq, PartialEq, Hash)]
|
||||||
pub enum WsDataType {
|
pub enum WsDataType {
|
||||||
|
// The frontend receives the Acked means the backend has accepted the revision
|
||||||
Acked = 0,
|
Acked = 0,
|
||||||
|
// The frontend receives the PushRev event means the backend is pushing the new revision to frontend
|
||||||
PushRev = 1,
|
PushRev = 1,
|
||||||
|
// The fronted receives the PullRev event means the backend try to pull the revision from frontend
|
||||||
PullRev = 2, // data should be Revision
|
PullRev = 2, // data should be Revision
|
||||||
Conflict = 3,
|
Conflict = 3,
|
||||||
NewDocUser = 4,
|
NewDocUser = 4,
|
||||||
|
@ -36,6 +36,7 @@ impl OTError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static_ot_error!(duplicate_revision, OTErrorCode::DuplicatedRevision);
|
static_ot_error!(duplicate_revision, OTErrorCode::DuplicatedRevision);
|
||||||
|
static_ot_error!(revision_id_conflict, OTErrorCode::RevisionIDConflict);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for OTError {
|
impl fmt::Display for OTError {
|
||||||
@ -66,6 +67,7 @@ pub enum OTErrorCode {
|
|||||||
RedoFail,
|
RedoFail,
|
||||||
SerdeError,
|
SerdeError,
|
||||||
DuplicatedRevision,
|
DuplicatedRevision,
|
||||||
|
RevisionIDConflict,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ErrorBuilder {
|
pub struct ErrorBuilder {
|
||||||
|
@ -33,8 +33,12 @@ impl RevisionMemoryCache {
|
|||||||
pub fn new() -> Self { RevisionMemoryCache::default() }
|
pub fn new() -> Self { RevisionMemoryCache::default() }
|
||||||
|
|
||||||
pub async fn add_revision(&self, revision: Revision) -> Result<(), OTError> {
|
pub async fn add_revision(&self, revision: Revision) -> Result<(), OTError> {
|
||||||
if self.revs_map.contains_key(&revision.rev_id) {
|
// The last revision's rev_id must be greater than the new one.
|
||||||
return Ok(());
|
if let Some(rev_id) = self.pending_revs.read().await.back() {
|
||||||
|
if *rev_id >= revision.rev_id {
|
||||||
|
return Err(OTError::revision_id_conflict()
|
||||||
|
.context(format!("The new revision's id must be greater than {}", rev_id)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pending_revs.write().await.push_back(revision.rev_id);
|
self.pending_revs.write().await.push_back(revision.rev_id);
|
||||||
@ -121,21 +125,3 @@ impl RevisionRecord {
|
|||||||
|
|
||||||
pub fn ack(&mut self) { self.state = RevState::Acked; }
|
pub fn ack(&mut self) { self.state = RevState::Acked; }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PendingRevId {
|
|
||||||
pub rev_id: i64,
|
|
||||||
pub sender: RevIdSender,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PendingRevId {
|
|
||||||
pub fn new(rev_id: i64, sender: RevIdSender) -> Self { Self { rev_id, sender } }
|
|
||||||
|
|
||||||
pub fn finish(&self, rev_id: i64) -> bool {
|
|
||||||
if self.rev_id > rev_id {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
let _ = self.sender.send(self.rev_id);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -25,6 +25,8 @@ pub struct Revision {
|
|||||||
|
|
||||||
impl Revision {
|
impl Revision {
|
||||||
pub fn is_empty(&self) -> bool { self.base_rev_id == self.rev_id }
|
pub fn is_empty(&self) -> bool { self.base_rev_id == self.rev_id }
|
||||||
|
|
||||||
|
pub fn pair_rev_id(&self) -> (i64, i64) { (self.base_rev_id, self.rev_id) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Revision {
|
impl std::fmt::Debug for Revision {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user