From a0b972099131a1d2edb9ac242f6b3066ae6baae4 Mon Sep 17 00:00:00 2001 From: eraden Date: Sat, 4 Jun 2022 16:05:18 +0200 Subject: [PATCH] Fix update account, db tests --- .../database_manager/src/account_addresses.rs | 10 +- actors/database_manager/src/accounts.rs | 278 +++++++++++++++++- actors/database_manager/src/tokens.rs | 98 ++++++ shared/model/src/lib.rs | 8 +- web/src/pages.rs | 46 +-- 5 files changed, 405 insertions(+), 35 deletions(-) diff --git a/actors/database_manager/src/account_addresses.rs b/actors/database_manager/src/account_addresses.rs index 1ae111c..4f0c189 100644 --- a/actors/database_manager/src/account_addresses.rs +++ b/actors/database_manager/src/account_addresses.rs @@ -232,9 +232,9 @@ mod test { let hash: String = fake::faker::internet::en::Password(10..20).fake(); db.send(CreateAccount { - email: model::Email::new(email), - login: model::Login::new(login), - pass_hash: model::PassHash::new(hash), + email: Email::new(email), + login: Login::new(login), + pass_hash: PassHash::new(hash), role: Role::Admin, }) .await @@ -250,7 +250,7 @@ mod test { .database_mut() .set_url("postgres://postgres@localhost/bazzar_test"); - let db = crate::Database::build(config).await.start(); + let db = Database::build(config).await.start(); // account let account = test_create_account(db.clone()).await; @@ -286,7 +286,7 @@ mod test { assert_eq!( address, model::AccountAddress { - id: model::AddressId::new(1), + id: address.id, name: model::Name::new(name.clone()), email: model::Email::new(email.clone()), phone: model::Phone::new(phone.clone()), diff --git a/actors/database_manager/src/accounts.rs b/actors/database_manager/src/accounts.rs index b4c0e60..0dde89c 100644 --- a/actors/database_manager/src/accounts.rs +++ b/actors/database_manager/src/accounts.rs @@ -16,6 +16,8 @@ pub enum Error { NotExists, #[error("Failed to load all accounts")] All, + #[error("Can't update account")] + CantUpdate, } #[derive(actix::Message)] @@ -90,11 +92,12 @@ pub(crate) async fn update_account(msg: UpdateAccount, db: PgPool) -> Result sqlx::query_as( r#" UPDATE accounts -SET login = $2 AND email = $3 AND role = $4 AND pass_hash = $5 AND state = $6 +SET login = $2, email = $3, role = $4, pass_hash = $5, state = $6 WHERE id = $1 RETURNING id, email, login, pass_hash, role, customer_id, state "#, ) + .bind(msg.id) .bind(msg.login) .bind(msg.email) .bind(msg.role) @@ -103,11 +106,12 @@ RETURNING id, email, login, pass_hash, role, customer_id, state None => sqlx::query_as( r#" UPDATE accounts -SET login = $2 AND email = $3 AND role = $4 AND pass_hash = $5 +SET login = $2, email = $3, role = $4, state = $5 WHERE id = $1 RETURNING id, email, login, pass_hash, role, customer_id, state "#, ) + .bind(msg.id) .bind(msg.login) .bind(msg.email) .bind(msg.role) @@ -117,7 +121,7 @@ RETURNING id, email, login, pass_hash, role, customer_id, state .await .map_err(|e| { log::error!("{e:?}"); - super::Error::Account(Error::CantCreate) + super::Error::Account(Error::CantUpdate) }) } @@ -191,3 +195,271 @@ WHERE login = $1 AND email = $2 super::Error::Account(Error::CantCreate) }) } + +#[cfg(test)] +mod tests { + use actix::Addr; + use config::UpdateConfig; + use fake::Fake; + use model::*; + + use crate::*; + + pub struct NoOpts; + + impl UpdateConfig for NoOpts {} + + async fn test_create_account( + db: Addr, + login: Option, + email: Option, + hash: Option, + ) -> FullAccount { + use fake::faker::internet::en; + let login: String = login.unwrap_or_else(|| en::Username().fake()); + let email: String = email.unwrap_or_else(|| en::FreeEmail().fake()); + let hash: String = hash.unwrap_or_else(|| en::Password(10..20).fake()); + + db.send(CreateAccount { + email: Email::new(email), + login: Login::new(login), + pass_hash: PassHash::new(hash), + role: Role::Admin, + }) + .await + .unwrap() + .unwrap() + } + + #[actix::test] + async fn create_account() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + let login: String = fake::faker::internet::en::Username().fake(); + let email: String = fake::faker::internet::en::FreeEmail().fake(); + let hash: String = fake::faker::internet::en::Password(10..20).fake(); + + let account: FullAccount = db + .send(CreateAccount { + email: Email::new(&email), + login: Login::new(&login), + pass_hash: PassHash::new(&hash), + role: Role::Admin, + }) + .await + .unwrap() + .unwrap(); + + let expected = FullAccount { + login: Login::new(login), + email: Email::new(email), + pass_hash: PassHash::new(&hash), + role: Role::Admin, + customer_id: account.customer_id, + id: account.id, + state: AccountState::Active, + }; + + assert_eq!(account, expected); + } + + #[actix::test] + async fn all_accounts() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + test_create_account(db.clone(), None, None, None).await; + test_create_account(db.clone(), None, None, None).await; + test_create_account(db.clone(), None, None, None).await; + + let v: Vec = db.send(AllAccounts).await.unwrap().unwrap(); + assert!(v.len() >= 3); + } + + #[actix::test] + async fn update_account_without_pass() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + let original_login: String = fake::faker::internet::en::Username().fake(); + let original_email: String = fake::faker::internet::en::FreeEmail().fake(); + let original_hash: String = fake::faker::internet::en::Password(10..20).fake(); + + let original_account = test_create_account( + db.clone(), + Some(original_login.clone()), + Some(original_email.clone()), + Some(original_hash.clone()), + ) + .await; + + let updated_login: String = fake::faker::internet::en::Username().fake(); + let updated_email: String = fake::faker::internet::en::FreeEmail().fake(); + + let updated_account: FullAccount = db + .send(UpdateAccount { + id: original_account.id, + email: Email::new(updated_email.clone()), + login: Login::new(updated_login.clone()), + pass_hash: None, + role: Role::Admin, + state: AccountState::Active, + }) + .await + .unwrap() + .unwrap(); + + let expected = FullAccount { + id: original_account.id, + email: Email::new(updated_email), + login: Login::new(updated_login), + pass_hash: PassHash::new(original_hash), + role: Role::Admin, + customer_id: original_account.customer_id, + state: AccountState::Active, + }; + + assert_ne!(original_account, expected); + assert_eq!(updated_account, expected); + } + + #[actix::test] + async fn update_account_with_pass() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + let original_login: String = fake::faker::internet::en::Username().fake(); + let original_email: String = fake::faker::internet::en::FreeEmail().fake(); + let original_hash: String = fake::faker::internet::en::Password(10..20).fake(); + + let original_account = test_create_account( + db.clone(), + Some(original_login.clone()), + Some(original_email.clone()), + Some(original_hash.clone()), + ) + .await; + + let updated_login: String = fake::faker::internet::en::Username().fake(); + let updated_email: String = fake::faker::internet::en::FreeEmail().fake(); + let updated_hash: String = fake::faker::internet::en::Password(10..20).fake(); + + let updated_account: FullAccount = db + .send(UpdateAccount { + id: original_account.id, + email: Email::new(updated_email.clone()), + login: Login::new(updated_login.clone()), + pass_hash: Some(PassHash::new(updated_hash.clone())), + role: Role::Admin, + state: AccountState::Active, + }) + .await + .unwrap() + .unwrap(); + + let expected = FullAccount { + id: original_account.id, + email: Email::new(updated_email), + login: Login::new(updated_login), + pass_hash: PassHash::new(updated_hash), + role: Role::Admin, + customer_id: original_account.customer_id, + state: AccountState::Active, + }; + + assert_ne!(original_account, expected); + assert_eq!(updated_account, expected); + } + + #[actix::test] + async fn find() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + let account = test_create_account(db.clone(), None, None, None).await; + + let res: FullAccount = db + .send(FindAccount { + account_id: account.id, + }) + .await + .unwrap() + .unwrap(); + + assert_eq!(account, res); + } + + #[actix::test] + async fn find_identity_email() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + let account = test_create_account(db.clone(), None, None, None).await; + + let res: FullAccount = db + .send(AccountByIdentity { + email: Some(account.email.clone()), + login: None, + }) + .await + .unwrap() + .unwrap(); + + assert_eq!(account, res); + } + + #[actix::test] + async fn find_identity_login() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + let account = test_create_account(db.clone(), None, None, None).await; + + let res: FullAccount = db + .send(AccountByIdentity { + login: Some(account.login.clone()), + email: None, + }) + .await + .unwrap() + .unwrap(); + + assert_eq!(account, res); + } +} diff --git a/actors/database_manager/src/tokens.rs b/actors/database_manager/src/tokens.rs index 37d93aa..053f2d1 100644 --- a/actors/database_manager/src/tokens.rs +++ b/actors/database_manager/src/tokens.rs @@ -12,6 +12,24 @@ pub enum Error { Jti, } +/// Find token by JTI field +/// +/// # Examples +/// +/// ``` +/// use actix::Addr; +/// use database_manager::{Database, TokenByJti}; +/// +/// async fn find(db: Addr) { +/// match db.send(TokenByJti { +/// jti: uuid::Uuid::new_v4() +/// }).await { +/// Ok(Ok(token)) => { println!("{:?}", token); } +/// Ok(Err(db_err)) => { println!("{:?}", db_err); } +/// Err(actor_err) => { println!("{:?}", actor_err); } +/// } +/// } +/// ``` #[derive(Message)] #[rtype(result = "Result")] pub struct TokenByJti { @@ -107,3 +125,83 @@ RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not crate::Error::Token(Error::Create) }) } + +#[cfg(test)] +mod tests { + use actix::Addr; + use fake::*; + use model::*; + use uuid::Uuid; + + use crate::*; + + async fn test_create_account(db: Addr) -> FullAccount { + use fake::faker::internet::en; + let login: String = en::Username().fake(); + let email: String = en::FreeEmail().fake(); + let hash: String = en::Password(10..20).fake(); + + db.send(CreateAccount { + email: Email::new(email), + login: Login::new(login), + pass_hash: PassHash::new(hash), + role: Role::Admin, + }) + .await + .unwrap() + .unwrap() + } + + async fn test_create_token( + db: Addr, + customer_id: Option, + role: Option, + subject: Option, + audience: Option, + expiration_time: Option, + ) -> Token { + let customer_id = customer_id.unwrap_or_else(|| uuid::Uuid::new_v4()); + let role = role.unwrap_or_else(|| Role::Admin); + let subject = match subject { + Some(id) => id, + _ => test_create_account(db.clone()).await.id, + }; + let audience = audience.unwrap_or_else(|| Audience::Web); + let expiration_time = expiration_time + .unwrap_or_else(|| (chrono::Utc::now() + chrono::Duration::days(60)).naive_utc()); + + db.send(CreateExtendedToken { + customer_id, + role, + subject, + audience, + expiration_time, + }) + .await + .unwrap() + .unwrap() + } + + #[actix::test] + async fn create_account() { + let config = config::default_load(&mut NoOpts); + config + .lock() + .database_mut() + .set_url("postgres://postgres@localhost/bazzar_test"); + + let db = Database::build(config).await.start(); + + let account = test_create_account(db.clone()).await; + + db.send(CreateToken { + customer_id: uuid::Uuid::new_v4(), + role: Role::Admin, + subject: account.id, + audience: Default::default(), + }) + .await + .unwrap() + .unwrap(); + } +} diff --git a/shared/model/src/lib.rs b/shared/model/src/lib.rs index af2b869..04eebd0 100644 --- a/shared/model/src/lib.rs +++ b/shared/model/src/lib.rs @@ -146,7 +146,7 @@ pub enum OrderStatus { #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] -#[derive(Copy, Clone, Debug, Hash, Display, Deserialize, Serialize, PartialEq)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Display, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum Role { #[display(fmt = "Adminitrator")] @@ -330,7 +330,7 @@ impl TryFrom for Quantity { #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] -#[derive(Deserialize, Serialize, Debug, Clone, Deref, From, Display)] +#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Deref, From, Display)] #[serde(transparent)] pub struct Login(String); @@ -691,7 +691,7 @@ impl PasswordConfirmation { #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] -#[derive(Serialize, Deserialize, Debug, Deref, From, Display)] +#[derive(Serialize, Deserialize, Debug, PartialEq, Deref, From, Display)] #[serde(transparent)] pub struct PassHash(String); @@ -718,7 +718,7 @@ pub struct AccountId(RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct FullAccount { pub id: AccountId, pub email: Email, diff --git a/web/src/pages.rs b/web/src/pages.rs index abca261..601b5e5 100644 --- a/web/src/pages.rs +++ b/web/src/pages.rs @@ -195,27 +195,27 @@ impl<'a> Urls<'a> { } // Admin - pub fn admin_landing(self) -> Url { - self.base_url() - .add_path_part("admin") - .add_path_part("landing") - } - - pub fn admin_dashboard(self) -> Url { - self.base_url() - .add_path_part("admin") - .add_path_part("dashboard") - } - - pub fn admin_products(self) -> Url { - self.base_url() - .add_path_part("admin") - .add_path_part("products") - } - - pub fn admin_product(self) -> Url { - self.base_url() - .add_path_part("admin") - .add_path_part("product") - } + // pub fn admin_landing(self) -> Url { + // self.base_url() + // .add_path_part("admin") + // .add_path_part("landing") + // } + // + // pub fn admin_dashboard(self) -> Url { + // self.base_url() + // .add_path_part("admin") + // .add_path_part("dashboard") + // } + // + // pub fn admin_products(self) -> Url { + // self.base_url() + // .add_path_part("admin") + // .add_path_part("products") + // } + // + // pub fn admin_product(self) -> Url { + // self.base_url() + // .add_path_part("admin") + // .add_path_part("product") + // } }