add server with flowy_document test

This commit is contained in:
appflowy
2021-09-27 23:23:23 +08:00
parent 11bc536df8
commit 01c9620e03
43 changed files with 515 additions and 340 deletions

View File

@ -95,4 +95,8 @@ once_cell = "1.7.2"
linkify = "0.5.0"
flowy-user = { path = "../rust-lib/flowy-user" }
flowy-workspace = { path = "../rust-lib/flowy-workspace" }
flowy-ws = { path = "../rust-lib/flowy-ws" }
flowy-ws = { path = "../rust-lib/flowy-ws" }
flowy-sdk = { path = "../rust-lib/flowy-sdk" }
flowy-test = { path = "../rust-lib/flowy-test" }
flowy-infra = { path = "../rust-lib/flowy-infra" }
flowy-ot = { path = "../rust-lib/flowy-ot" }

View File

@ -22,7 +22,6 @@ use parking_lot::RwLock;
use protobuf::Message;
use sqlx::PgPool;
use std::{
cmp::min,
convert::TryInto,
sync::{
atomic::{AtomicI64, Ordering::SeqCst},

View File

@ -6,8 +6,7 @@ use crate::service::{
};
use actix_web::web::Data;
use crate::service::ws::WsUser;
use flowy_document::protobuf::{QueryDocParams, Revision, RevisionRange, WsDataType, WsDocumentData};
use flowy_document::protobuf::{QueryDocParams, Revision, WsDataType, WsDocumentData};
use flowy_net::errors::ServerError;
use parking_lot::{RwLock, RwLockUpgradableReadGuard};
use protobuf::Message;

View File

@ -28,7 +28,7 @@ impl Builder {
let env_filter = EnvFilter::new(self.env_filter);
let subscriber = tracing_subscriber::fmt()
.with_target(true)
.with_max_level(tracing::Level::TRACE)
.with_max_level(tracing::Level::DEBUG)
.with_writer(std::io::stderr)
.with_thread_ids(false)
.compact()

View File

@ -8,7 +8,7 @@ use crate::{
};
use flowy_net::errors::ServerError;
use flowy_workspace::{
entities::view::VIEW_DEFAULT_DATA,
entities::view::DOC_DEFAULT_DATA,
protobuf::{App, CreateViewParams, View, ViewType, Workspace},
};
@ -62,7 +62,7 @@ async fn create_view(transaction: &mut DBTransaction<'_>, app: &App) -> Result<V
desc: "View created by AppFlowy".to_string(),
thumbnail: "123.png".to_string(),
view_type: ViewType::Doc,
data: VIEW_DEFAULT_DATA.to_string(),
data: DOC_DEFAULT_DATA.to_string(),
unknown_fields: Default::default(),
cached_size: Default::default(),
};

View File

@ -19,6 +19,7 @@ pub async fn establish_ws_connection(
server: Data<Addr<WsServer>>,
biz_handlers: Data<WsBizHandlers>,
) -> Result<HttpResponse, Error> {
log::debug!("establish_ws_connection");
match LoggedUser::from_token(token.clone()) {
Ok(user) => {
let ws_user = WsUser::new(user.clone());

View File

@ -3,7 +3,7 @@ use crate::{
service::{
user::LoggedUser,
ws::{
entities::{Connect, Disconnect, SessionId, Socket},
entities::{Connect, Disconnect, Socket},
WsBizHandler,
WsBizHandlers,
WsMessageAdaptor,

View File

@ -1,9 +1,9 @@
use crate::helper::{spawn_server, TestServer};
use crate::helper::{spawn_user_server, TestUserServer};
use flowy_user::entities::{SignInParams, SignUpParams, SignUpResponse, UpdateUserParams};
#[actix_rt::test]
async fn user_register() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let response = register_user(&app, "annie@appflowy.io", "HelloWorld123!").await;
log::info!("{:?}", response);
}
@ -11,7 +11,7 @@ async fn user_register() {
#[actix_rt::test]
#[should_panic]
async fn user_sign_in_with_invalid_password() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let email = "annie@appflowy.io";
let password = "123";
let _ = register_user(&app, email, password).await;
@ -20,7 +20,7 @@ async fn user_sign_in_with_invalid_password() {
#[actix_rt::test]
#[should_panic]
async fn user_sign_in_with_invalid_email() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let email = "annie@gmail@";
let password = "HelloWorld123!";
let _ = register_user(&app, email, password).await;
@ -28,7 +28,7 @@ async fn user_sign_in_with_invalid_email() {
#[actix_rt::test]
async fn user_sign_in() {
let app = spawn_server().await;
let app = spawn_user_server().await;
let email = "annie@appflowy.io";
let password = "HelloWorld123!";
let _ = register_user(&app, email, password).await;
@ -42,7 +42,7 @@ async fn user_sign_in() {
#[actix_rt::test]
#[should_panic]
async fn user_sign_out() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
server.sign_out().await;
// user_detail will be empty because use was sign out.
@ -51,13 +51,13 @@ async fn user_sign_out() {
#[actix_rt::test]
async fn user_get_detail() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
log::info!("{:?}", server.get_user_profile().await);
}
#[actix_rt::test]
async fn user_update_password() {
let mut server = spawn_server().await;
let mut server = spawn_user_server().await;
let email = "annie@appflowy.io";
let password = "HelloWorld123!";
let sign_up_resp = register_user(&server, email, password).await;
@ -82,7 +82,7 @@ async fn user_update_password() {
#[actix_rt::test]
async fn user_update_name() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let name = "tom".to_string();
let params = UpdateUserParams::new(&server.user_id()).name(&name);
@ -94,7 +94,7 @@ async fn user_update_name() {
#[actix_rt::test]
async fn user_update_email() {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let email = "123@gmail.com".to_string();
let params = UpdateUserParams::new(server.user_id()).email(&email);
server.update_user_profile(params).await.unwrap();
@ -104,14 +104,14 @@ async fn user_update_email() {
}
#[allow(dead_code)]
async fn sign_up_user(server: &TestServer) -> SignUpResponse {
async fn sign_up_user(server: &TestUserServer) -> SignUpResponse {
let email = "annie@appflowy.io";
let password = "HelloWorld123!";
let response = register_user(server, email, password).await;
response
}
async fn register_user(server: &TestServer, email: &str, password: &str) -> SignUpResponse {
async fn register_user(server: &TestUserServer, email: &str, password: &str) -> SignUpResponse {
let params = SignUpParams {
email: email.to_string(),
name: "annie".to_string(),

View File

@ -2,12 +2,7 @@ use crate::helper::*;
use flowy_workspace::entities::{
app::{DeleteAppParams, QueryAppParams, UpdateAppParams},
view::{DeleteViewParams, QueryViewParams, UpdateViewParams},
workspace::{
CreateWorkspaceParams,
DeleteWorkspaceParams,
QueryWorkspaceParams,
UpdateWorkspaceParams,
},
workspace::{CreateWorkspaceParams, DeleteWorkspaceParams, QueryWorkspaceParams, UpdateWorkspaceParams},
};
#[actix_rt::test]
@ -173,7 +168,7 @@ async fn view_delete() {
#[actix_rt::test]
async fn workspace_list_read() {
let mut server = spawn_server().await;
let mut server = spawn_user_server().await;
let token = server.register_user().await.token;
server.user_token = Some(token);
for i in 0..3 {

View File

@ -0,0 +1,97 @@
use crate::helper::*;
use flowy_document::{
entities::doc::{CreateDocParams, DocDelta, QueryDocParams},
module::FlowyDocument,
services::doc::edit_doc_context::EditDocContext,
};
use flowy_infra::uuid;
use flowy_ot::core::Delta;
use flowy_sdk::{FlowySDK, FlowySDKConfig};
use flowy_test::{prelude::root_dir, FlowyTestSDK};
use flowy_user::{entities::SignUpParams, services::user::UserSession};
use flowy_workspace::prelude::DOC_DEFAULT_DATA;
use std::{str::FromStr, sync::Arc};
pub struct DocumentTest {
server: TestServer,
sdk: FlowyTestSDK,
flowy_document: Arc<FlowyDocument>,
user_session: Arc<UserSession>,
edit_context: Arc<EditDocContext>,
}
#[derive(Clone)]
pub enum DocScript {
SendText(&'static str),
SendBinary(Vec<u8>),
}
async fn create_doc(user_session: Arc<UserSession>, flowy_document: Arc<FlowyDocument>) -> Arc<EditDocContext> {
let conn = user_session.db_pool().unwrap().get().unwrap();
let doc_id = uuid();
let params = CreateDocParams {
id: doc_id.clone(),
data: DOC_DEFAULT_DATA.to_string(),
};
let _ = flowy_document.create(params, &*conn).unwrap();
let edit_context = flowy_document
.open(QueryDocParams { doc_id }, user_session.db_pool().unwrap())
.await
.unwrap();
edit_context
}
async fn init_user(user_session: Arc<UserSession>) {
let params = SignUpParams {
email: format!("{}@gmail.com", uuid()),
name: "nathan".to_string(),
password: "HelloWorld!@12".to_string(),
};
user_session.sign_up(params).await.unwrap();
user_session.init_user().await.unwrap();
}
impl DocumentTest {
pub async fn new() -> Self {
let server = spawn_server().await;
let config = FlowySDKConfig::new(&root_dir(), &server.host, "http", "ws").log_filter("debug");
let sdk = FlowySDK::new(config);
let flowy_document = sdk.flowy_document.clone();
let user_session = sdk.user_session.clone();
init_user(user_session.clone()).await;
let edit_context = create_doc(user_session.clone(), flowy_document.clone()).await;
Self {
server,
sdk,
flowy_document,
user_session,
edit_context,
}
}
pub async fn run_scripts(self, scripts: Vec<DocScript>) {
for script in scripts {
match script {
DocScript::SendText(s) => {
let delta = Delta::from_str(s).unwrap();
let data = delta.to_json();
let doc_delta = DocDelta {
doc_id: self.edit_context.doc_id.clone(),
data,
};
self.flowy_document.apply_doc_delta(doc_delta).await;
},
DocScript::SendBinary(_bytes) => {},
}
}
std::mem::forget(self);
}
}

View File

@ -0,0 +1,12 @@
use crate::document::helper::{DocScript, DocumentTest};
use tokio::time::{interval, Duration};
#[actix_rt::test]
async fn ws_connect() {
let test = DocumentTest::new().await;
test.run_scripts(vec![DocScript::SendText("abc")]).await;
let mut interval = interval(Duration::from_secs(10));
interval.tick().await;
interval.tick().await;
}

View File

@ -12,7 +12,7 @@ use flowy_workspace::prelude::{server::*, *};
use sqlx::{Connection, Executor, PgConnection, PgPool};
use uuid::Uuid;
pub struct TestServer {
pub struct TestUserServer {
pub host: String,
pub port: u16,
pub pg_pool: PgPool,
@ -20,9 +20,9 @@ pub struct TestServer {
pub user_id: Option<String>,
}
impl TestServer {
impl TestUserServer {
pub async fn new() -> Self {
let mut server = spawn_server().await;
let mut server: TestUserServer = spawn_server().await.into();
let response = server.register_user().await;
server.user_token = Some(response.token);
server.user_id = Some(response.user_id);
@ -36,28 +36,16 @@ impl TestServer {
pub async fn sign_out(&self) {
let url = format!("{}/api/auth", self.http_addr());
let _ = user_sign_out_request(self.user_token(), &url)
.await
.unwrap();
let _ = user_sign_out_request(self.user_token(), &url).await.unwrap();
}
pub fn user_token(&self) -> &str {
self.user_token
.as_ref()
.expect("must call register_user first ")
}
pub fn user_token(&self) -> &str { self.user_token.as_ref().expect("must call register_user first ") }
pub fn user_id(&self) -> &str {
self.user_id
.as_ref()
.expect("must call register_user first ")
}
pub fn user_id(&self) -> &str { self.user_id.as_ref().expect("must call register_user first ") }
pub async fn get_user_profile(&self) -> UserProfile {
let url = format!("{}/api/user", self.http_addr());
let user_profile = get_user_profile_request(self.user_token(), &url)
.await
.unwrap();
let user_profile = get_user_profile_request(self.user_token(), &url).await.unwrap();
user_profile
}
@ -68,99 +56,73 @@ impl TestServer {
pub async fn create_workspace(&self, params: CreateWorkspaceParams) -> Workspace {
let url = format!("{}/api/workspace", self.http_addr());
let workspace = create_workspace_request(self.user_token(), params, &url)
.await
.unwrap();
let workspace = create_workspace_request(self.user_token(), params, &url).await.unwrap();
workspace
}
pub async fn read_workspaces(&self, params: QueryWorkspaceParams) -> RepeatedWorkspace {
let url = format!("{}/api/workspace", self.http_addr());
let workspaces = read_workspaces_request(self.user_token(), params, &url)
.await
.unwrap();
let workspaces = read_workspaces_request(self.user_token(), params, &url).await.unwrap();
workspaces
}
pub async fn update_workspace(&self, params: UpdateWorkspaceParams) {
let url = format!("{}/api/workspace", self.http_addr());
update_workspace_request(self.user_token(), params, &url)
.await
.unwrap();
update_workspace_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn delete_workspace(&self, params: DeleteWorkspaceParams) {
let url = format!("{}/api/workspace", self.http_addr());
delete_workspace_request(self.user_token(), params, &url)
.await
.unwrap();
delete_workspace_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn create_app(&self, params: CreateAppParams) -> App {
let url = format!("{}/api/app", self.http_addr());
let app = create_app_request(self.user_token(), params, &url)
.await
.unwrap();
let app = create_app_request(self.user_token(), params, &url).await.unwrap();
app
}
pub async fn read_app(&self, params: QueryAppParams) -> Option<App> {
let url = format!("{}/api/app", self.http_addr());
let app = read_app_request(self.user_token(), params, &url)
.await
.unwrap();
let app = read_app_request(self.user_token(), params, &url).await.unwrap();
app
}
pub async fn update_app(&self, params: UpdateAppParams) {
let url = format!("{}/api/app", self.http_addr());
update_app_request(self.user_token(), params, &url)
.await
.unwrap();
update_app_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn delete_app(&self, params: DeleteAppParams) {
let url = format!("{}/api/app", self.http_addr());
delete_app_request(self.user_token(), params, &url)
.await
.unwrap();
delete_app_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn create_view(&self, params: CreateViewParams) -> View {
let url = format!("{}/api/view", self.http_addr());
let view = create_view_request(self.user_token(), params, &url)
.await
.unwrap();
let view = create_view_request(self.user_token(), params, &url).await.unwrap();
view
}
pub async fn read_view(&self, params: QueryViewParams) -> Option<View> {
let url = format!("{}/api/view", self.http_addr());
let view = read_view_request(self.user_token(), params, &url)
.await
.unwrap();
let view = read_view_request(self.user_token(), params, &url).await.unwrap();
view
}
pub async fn update_view(&self, params: UpdateViewParams) {
let url = format!("{}/api/view", self.http_addr());
update_view_request(self.user_token(), params, &url)
.await
.unwrap();
update_view_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn delete_view(&self, params: DeleteViewParams) {
let url = format!("{}/api/view", self.http_addr());
delete_view_request(self.user_token(), params, &url)
.await
.unwrap();
delete_view_request(self.user_token(), params, &url).await.unwrap();
}
pub async fn read_doc(&self, params: QueryDocParams) -> Option<Doc> {
let url = format!("{}/api/doc", self.http_addr());
let doc = read_doc_request(self.user_token(), params, &url)
.await
.unwrap();
let doc = read_doc_request(self.user_token(), params, &url).await.unwrap();
doc
}
@ -182,14 +144,32 @@ impl TestServer {
pub fn http_addr(&self) -> String { format!("http://{}", self.host) }
pub fn ws_addr(&self) -> String {
format!(
"ws://{}/ws/{}",
self.host,
self.user_token.as_ref().unwrap()
)
pub fn ws_addr(&self) -> String { format!("ws://{}/ws/{}", self.host, self.user_token.as_ref().unwrap()) }
}
impl std::convert::From<TestServer> for TestUserServer {
fn from(server: TestServer) -> Self {
TestUserServer {
host: server.host,
port: server.port,
pg_pool: server.pg_pool,
user_token: None,
user_id: None,
}
}
}
pub async fn spawn_user_server() -> TestUserServer {
let server: TestUserServer = spawn_server().await.into();
server
}
pub struct TestServer {
pub host: String,
pub port: u16,
pub pg_pool: PgPool,
}
pub async fn spawn_server() -> TestServer {
let database_name = format!("{}", Uuid::new_v4().to_string());
let configuration = {
@ -217,8 +197,6 @@ pub async fn spawn_server() -> TestServer {
pg_pool: get_connection_pool(&configuration.database)
.await
.expect("Failed to connect to the database"),
user_token: None,
user_id: None,
}
}
@ -265,7 +243,7 @@ async fn drop_test_database(database_name: String) {
.expect("Failed to drop database.");
}
pub async fn create_test_workspace(server: &TestServer) -> Workspace {
pub async fn create_test_workspace(server: &TestUserServer) -> Workspace {
let params = CreateWorkspaceParams {
name: "My first workspace".to_string(),
desc: "This is my first workspace".to_string(),
@ -275,7 +253,7 @@ pub async fn create_test_workspace(server: &TestServer) -> Workspace {
workspace
}
pub async fn create_test_app(server: &TestServer, workspace_id: &str) -> App {
pub async fn create_test_app(server: &TestUserServer, workspace_id: &str) -> App {
let params = CreateAppParams {
workspace_id: workspace_id.to_owned(),
name: "My first app".to_string(),
@ -287,7 +265,7 @@ pub async fn create_test_app(server: &TestServer, workspace_id: &str) -> App {
app
}
pub async fn create_test_view(application: &TestServer, app_id: &str) -> View {
pub async fn create_test_view(application: &TestUserServer, app_id: &str) -> View {
let name = "My first view".to_string();
let desc = "This is my first view".to_string();
let thumbnail = "http://1.png".to_string();
@ -298,43 +276,37 @@ pub async fn create_test_view(application: &TestServer, app_id: &str) -> View {
}
pub struct WorkspaceTest {
pub server: TestServer,
pub server: TestUserServer,
pub workspace: Workspace,
}
impl WorkspaceTest {
pub async fn new() -> Self {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let workspace = create_test_workspace(&server).await;
Self { server, workspace }
}
pub async fn create_app(&self) -> App {
create_test_app(&self.server, &self.workspace.id).await
}
pub async fn create_app(&self) -> App { create_test_app(&self.server, &self.workspace.id).await }
}
pub struct AppTest {
pub server: TestServer,
pub server: TestUserServer,
pub workspace: Workspace,
pub app: App,
}
impl AppTest {
pub async fn new() -> Self {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let workspace = create_test_workspace(&server).await;
let app = create_test_app(&server, &workspace.id).await;
Self {
server,
workspace,
app,
}
Self { server, workspace, app }
}
}
pub struct ViewTest {
pub server: TestServer,
pub server: TestUserServer,
pub workspace: Workspace,
pub app: App,
pub view: View,
@ -342,7 +314,7 @@ pub struct ViewTest {
impl ViewTest {
pub async fn new() -> Self {
let server = TestServer::new().await;
let server = TestUserServer::new().await;
let workspace = create_test_workspace(&server).await;
let app = create_test_app(&server, &workspace.id).await;
let view = create_test_view(&server, &app.id).await;

View File

@ -1,3 +1,3 @@
mod api;
mod document;
pub mod helper;
mod ws;

View File

@ -1,76 +0,0 @@
use crate::helper::TestServer;
use flowy_ws::{WsController, WsModule, WsSender, WsState};
use parking_lot::RwLock;
use std::sync::Arc;
pub struct WsTest {
server: TestServer,
ws_controller: Arc<RwLock<WsController>>,
}
#[derive(Clone)]
pub enum WsScript {
SendText(&'static str),
SendBinary(Vec<u8>),
Disconnect(&'static str),
}
impl WsTest {
pub async fn new(scripts: Vec<WsScript>) -> Self {
let server = TestServer::new().await;
let ws_controller = Arc::new(RwLock::new(WsController::new()));
ws_controller
.write()
.state_callback(move |state| match state {
WsState::Connected(sender) => {
WsScriptRunner {
scripts: scripts.clone(),
sender: sender.clone(),
source: WsModule::Doc,
}
.run();
},
_ => {},
})
.await;
Self {
server,
ws_controller,
}
}
pub async fn run_scripts(&mut self) {
let addr = self.server.ws_addr();
self.ws_controller
.write()
.connect(addr)
.unwrap()
.await
.unwrap();
}
}
struct WsScriptRunner {
scripts: Vec<WsScript>,
sender: Arc<WsSender>,
source: WsModule,
}
impl WsScriptRunner {
fn run(self) {
for script in self.scripts {
match script {
WsScript::SendText(text) => {
self.sender.send_text(&self.source, text).unwrap();
},
WsScript::SendBinary(bytes) => {
self.sender.send_binary(&self.source, bytes).unwrap();
},
WsScript::Disconnect(reason) => {
self.sender.send_disconnect(reason).unwrap();
},
}
}
}
}

View File

@ -1,13 +0,0 @@
use crate::ws::helper::{WsScript, WsTest};
#[actix_rt::test]
async fn ws_connect() {
let mut ws = WsTest::new(vec![
WsScript::SendText("abc"),
WsScript::SendText("abc"),
WsScript::SendText("abc"),
WsScript::Disconnect("close by user"),
])
.await;
ws.run_scripts().await
}