diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 0ed25e040a..b3805007dc 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -58,4 +58,11 @@ path = "src/lib.rs" [[bin]] name = "backend" -path = "src/main.rs" \ No newline at end of file +path = "src/main.rs" + +[dev-dependencies] +once_cell = "1.7.2" +actix-rt = "2" +tokio = { version = "1", features = ["macros"] } +linkify = "0.5.0" +flowy-user = { path = "../rust-lib/flowy-user" } \ No newline at end of file diff --git a/backend/src/application.rs b/backend/src/application.rs index 91c9a96204..4f434967a7 100644 --- a/backend/src/application.rs +++ b/backend/src/application.rs @@ -34,6 +34,8 @@ impl Application { } pub async fn run_until_stopped(self) -> Result<(), std::io::Error> { self.server.await } + + pub fn port(&self) -> u16 { self.port } } pub fn run(listener: TcpListener, app_ctx: AppContext) -> Result { @@ -64,8 +66,8 @@ fn user_scope() -> Scope { web::scope("/api") // authentication .service(web::resource("/auth") - .route(web::post().to(user_router::login_handler)) - .route(web::delete().to(user_router::logout_handler)) + .route(web::post().to(user_router::sign_in_handler)) + .route(web::delete().to(user_router::sign_out_handler)) .route(web::get().to(user_router::user_profile)) ) // password @@ -74,7 +76,7 @@ fn user_scope() -> Scope { ) // register .service(web::resource("/register") - .route(web::post().to(user_router::register_handler)) + .route(web::post().to(user_router::register_user_handler)) ) } diff --git a/backend/src/entities/user.rs b/backend/src/entities/user.rs index 5abcfc9ef7..c2dcbc39fe 100644 --- a/backend/src/entities/user.rs +++ b/backend/src/entities/user.rs @@ -1,8 +1,12 @@ +// type mapped https://kotiri.com/2018/01/31/postgresql-diesel-rust-types.html + +use chrono::Utc; + #[derive(Debug, Clone, sqlx::FromRow)] pub struct User { pub(crate) id: uuid::Uuid, pub(crate) email: String, pub(crate) name: String, - pub(crate) create_time: i64, + pub(crate) create_time: chrono::DateTime, pub(crate) password: String, } diff --git a/backend/src/routers/user_router.rs b/backend/src/routers/user_router.rs index 563141f273..4ff39dc2a2 100644 --- a/backend/src/routers/user_router.rs +++ b/backend/src/routers/user_router.rs @@ -14,7 +14,7 @@ use flowy_net::errors::ServerError; use sqlx::PgPool; use std::sync::Arc; -pub async fn login_handler( +pub async fn sign_in_handler( payload: Payload, id: Identity, pool: Data, @@ -24,7 +24,7 @@ pub async fn login_handler( Ok(resp.into()) } -pub async fn logout_handler(id: Identity) -> Result { +pub async fn sign_out_handler(id: Identity) -> Result { id.forget(); Ok(HttpResponse::Ok().finish()) } @@ -37,7 +37,7 @@ pub async fn user_profile( unimplemented!() } -pub async fn register_handler( +pub async fn register_user_handler( _request: HttpRequest, payload: Payload, pool: Data, diff --git a/backend/src/user_service/auth_service.rs b/backend/src/user_service/auth_service.rs index 7212c0354a..f3679e7de1 100644 --- a/backend/src/user_service/auth_service.rs +++ b/backend/src/user_service/auth_service.rs @@ -116,7 +116,7 @@ async fn insert_user( params.email, params.name, Utc::now(), - "123".to_string() + password, ) .execute(transaction) .await diff --git a/backend/tests/api/auth.rs b/backend/tests/api/auth.rs new file mode 100644 index 0000000000..004129f293 --- /dev/null +++ b/backend/tests/api/auth.rs @@ -0,0 +1,39 @@ +use crate::helper::spawn_app; +use flowy_user::entities::{SignInParams, SignInResponse, SignUpParams}; + +#[actix_rt::test] +async fn user_register() { + let app = spawn_app().await; + let params = SignUpParams { + email: "annie@appflowy.io".to_string(), + name: "annie".to_string(), + password: "123".to_string(), + }; + + let response = app.register_user(params).await; + log::info!("{:?}", response); +} + +#[actix_rt::test] +async fn user_sign_in() { + let app = spawn_app().await; + let email = "annie@appflowy.io"; + let password = "123"; + + let _ = app + .register_user(SignUpParams { + email: email.to_string(), + name: "annie".to_string(), + password: password.to_string(), + }) + .await; + + let response = app + .sign_in(SignInParams { + email: email.to_string(), + password: password.to_string(), + }) + .await; + + log::info!("{:?}", response); +} diff --git a/backend/tests/api/helper.rs b/backend/tests/api/helper.rs new file mode 100644 index 0000000000..5cea969dd1 --- /dev/null +++ b/backend/tests/api/helper.rs @@ -0,0 +1,77 @@ +use backend::{ + application::{get_connection_pool, Application}, + config::{get_configuration, DatabaseSettings}, +}; +use flowy_net::request::HttpRequestBuilder; +use flowy_user::prelude::*; +use sqlx::{Connection, Executor, PgConnection, PgPool}; +use uuid::Uuid; + +pub struct TestApp { + pub address: String, + pub port: u16, + pub pg_pool: PgPool, +} + +impl TestApp { + pub async fn register_user(&self, params: SignUpParams) -> SignUpResponse { + let url = format!("{}/api/register", self.address); + let resp = user_sign_up(params, &url).await.unwrap(); + resp + } + + pub async fn sign_in(&self, params: SignInParams) -> SignInResponse { + let url = format!("{}/api/auth", self.address); + let resp = user_sign_in(params, &url).await.unwrap(); + resp + } +} + +pub async fn spawn_app() -> TestApp { + let configuration = { + let mut c = get_configuration().expect("Failed to read configuration."); + c.database.database_name = Uuid::new_v4().to_string(); + // Use a random OS port + c.application.port = 0; + c + }; + + let _ = configure_database(&configuration.database).await; + let application = Application::build(configuration.clone()) + .await + .expect("Failed to build application."); + let application_port = application.port(); + + let _ = tokio::spawn(application.run_until_stopped()); + + TestApp { + address: format!("http://localhost:{}", application_port), + port: application_port, + pg_pool: get_connection_pool(&configuration.database) + .await + .expect("Failed to connect to the database"), + } +} + +async fn configure_database(config: &DatabaseSettings) -> PgPool { + // Create database + let mut connection = PgConnection::connect_with(&config.without_db()) + .await + .expect("Failed to connect to Postgres"); + connection + .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) + .await + .expect("Failed to create database."); + + // Migrate database + let connection_pool = PgPool::connect_with(config.with_db()) + .await + .expect("Failed to connect to Postgres."); + + sqlx::migrate!("./migrations") + .run(&connection_pool) + .await + .expect("Failed to migrate the database"); + + connection_pool +} diff --git a/backend/tests/api/main.rs b/backend/tests/api/main.rs new file mode 100644 index 0000000000..24e0c04a4f --- /dev/null +++ b/backend/tests/api/main.rs @@ -0,0 +1,2 @@ +mod auth; +mod helper; diff --git a/rust-lib/flowy-observable/src/dart/stream_sender.rs b/rust-lib/flowy-observable/src/dart/stream_sender.rs index 9d6b974bd0..bbcff5bbf4 100644 --- a/rust-lib/flowy-observable/src/dart/stream_sender.rs +++ b/rust-lib/flowy-observable/src/dart/stream_sender.rs @@ -43,7 +43,7 @@ impl RustStreamSender { pub fn post(_observable_subject: ObservableSubject) -> Result<(), String> { #[cfg(feature = "dart")] match R2F_STREAM_SENDER.read() { - Ok(stream) => stream.inner_post(observable_subject), + Ok(stream) => stream.inner_post(_observable_subject), Err(e) => Err(format!("Get rust to flutter stream lock fail. {:?}", e)), } diff --git a/rust-lib/flowy-user/src/entities/mod.rs b/rust-lib/flowy-user/src/entities/mod.rs index 7792d9de21..002293747b 100644 --- a/rust-lib/flowy-user/src/entities/mod.rs +++ b/rust-lib/flowy-user/src/entities/mod.rs @@ -4,6 +4,6 @@ pub use user_detail::*; pub use user_update::*; mod parser; mod sign_in; -mod sign_up; +pub mod sign_up; mod user_detail; mod user_update; diff --git a/rust-lib/flowy-user/src/entities/sign_in.rs b/rust-lib/flowy-user/src/entities/sign_in.rs index 005dacbe37..67e70ebdd9 100644 --- a/rust-lib/flowy-user/src/entities/sign_in.rs +++ b/rust-lib/flowy-user/src/entities/sign_in.rs @@ -20,7 +20,7 @@ pub struct SignInParams { pub password: String, } -#[derive(Default, ProtoBuf)] +#[derive(Debug, Default, ProtoBuf)] pub struct SignInResponse { #[pb(index = 1)] pub uid: String, diff --git a/rust-lib/flowy-user/src/lib.rs b/rust-lib/flowy-user/src/lib.rs index 579616988d..36d0d51431 100644 --- a/rust-lib/flowy-user/src/lib.rs +++ b/rust-lib/flowy-user/src/lib.rs @@ -1,11 +1,12 @@ +mod event; +mod handlers; +mod sql_tables; + pub mod entities; pub mod errors; -pub mod event; -mod handlers; pub mod module; pub mod protobuf; -mod services; -pub mod sql_tables; +pub mod services; #[macro_use] extern crate flowy_database; @@ -13,7 +14,6 @@ extern crate flowy_database; pub mod prelude { pub use crate::{ entities::*, - handlers::*, services::{user::*, workspace::*}, }; } diff --git a/rust-lib/flowy-user/src/services/user/user_server.rs b/rust-lib/flowy-user/src/services/user/user_server.rs index e8384ce01c..01c3ac005a 100644 --- a/rust-lib/flowy-user/src/services/user/user_server.rs +++ b/rust-lib/flowy-user/src/services/user/user_server.rs @@ -26,18 +26,13 @@ pub(crate) fn construct_server() -> Arc { } pub struct UserServerImpl {} -impl UserServerImpl {} +impl UserServerImpl { + pub fn new() -> Self { Self {} } +} impl UserServer for UserServerImpl { fn sign_up(&self, params: SignUpParams) -> ResultFuture { - ResultFuture::new(async move { - let response = HttpRequestBuilder::post(SIGN_UP_URL.as_ref()) - .protobuf(params)? - .send() - .await? - .response()?; - Ok(response) - }) + ResultFuture::new(async move { user_sign_up(params, SIGN_UP_URL.as_ref()).await }) } fn sign_in(&self, _params: SignInParams) -> ResultFuture { @@ -60,6 +55,24 @@ impl UserServer for UserServerImpl { } } +pub async fn user_sign_up(params: SignUpParams, url: &str) -> Result { + let response = HttpRequestBuilder::post(&url.to_owned()) + .protobuf(params)? + .send() + .await? + .response()?; + Ok(response) +} + +pub async fn user_sign_in(params: SignInParams, url: &str) -> Result { + let response = HttpRequestBuilder::post(&url.to_owned()) + .protobuf(params)? + .send() + .await? + .response()?; + Ok(response) +} + pub struct UserServerMock {} impl UserServer for UserServerMock { diff --git a/rust-lib/flowy-user/tests/server/main.rs b/rust-lib/flowy-user/tests/server/main.rs deleted file mode 100644 index 3e9a615104..0000000000 --- a/rust-lib/flowy-user/tests/server/main.rs +++ /dev/null @@ -1 +0,0 @@ -mod user_test; diff --git a/rust-lib/flowy-user/tests/server/user_test.rs b/rust-lib/flowy-user/tests/server/user_test.rs deleted file mode 100644 index 9c32d2bd5e..0000000000 --- a/rust-lib/flowy-user/tests/server/user_test.rs +++ /dev/null @@ -1,14 +0,0 @@ -use flowy_user::prelude::*; - -#[tokio::test] -async fn user_register_test() { - let server = UserServerImpl {}; - - let params = SignUpParams { - email: "annie@appflowy.io".to_string(), - name: "annie".to_string(), - password: "1233333".to_string(), - }; - let result = server.sign_up(params).await.unwrap(); - println!("{:?}", result); -}