Test create address, handle phone number

This commit is contained in:
eraden 2022-05-29 13:36:25 +02:00
parent 7617cb1064
commit 4087b2c3ee
9 changed files with 282 additions and 24 deletions

View File

@ -27,7 +27,7 @@ pub(crate) async fn account_addresses(
) -> Result<Vec<model::AccountAddress>> { ) -> Result<Vec<model::AccountAddress>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, name, email, street, city, country, zip, account_id, is_default SELECT id, name, email, phone, street, city, country, zip, account_id, is_default
FROM account_addresses FROM account_addresses
WHERE account_id = $1 WHERE account_id = $1
"#, "#,
@ -37,7 +37,6 @@ WHERE account_id = $1
.await .await
.map_err(|_| Error::AccountAddresses.into()) .map_err(|_| Error::AccountAddresses.into())
} }
////
#[derive(actix::Message)] #[derive(actix::Message)]
#[rtype(result = "Result<model::AccountAddress>")] #[rtype(result = "Result<model::AccountAddress>")]
@ -59,7 +58,7 @@ pub(crate) async fn find_account_address(
) -> Result<model::AccountAddress> { ) -> Result<model::AccountAddress> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, name, email, street, city, country, zip, account_id, is_default SELECT id, name, email, phone, street, city, country, zip, account_id, is_default
FROM account_addresses FROM account_addresses
WHERE account_id = $1 AND id = $2 WHERE account_id = $1 AND id = $2
"#, "#,
@ -92,7 +91,7 @@ pub(crate) async fn default_account_address(
) -> Result<model::AccountAddress> { ) -> Result<model::AccountAddress> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, name, email, street, city, country, zip, account_id, is_default SELECT id, name, email, phone, street, city, country, zip, account_id, is_default
FROM account_addresses FROM account_addresses
WHERE account_id = $1 AND is_default WHERE account_id = $1 AND is_default
"#, "#,
@ -108,6 +107,7 @@ WHERE account_id = $1 AND is_default
pub struct CreateAccountAddress { pub struct CreateAccountAddress {
pub name: model::Name, pub name: model::Name,
pub email: model::Email, pub email: model::Email,
pub phone: model::Phone,
pub street: model::Street, pub street: model::Street,
pub city: model::City, pub city: model::City,
pub country: model::Country, pub country: model::Country,
@ -127,7 +127,7 @@ pub(crate) async fn create_address(
msg: CreateAccountAddress, msg: CreateAccountAddress,
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<model::AccountAddress> { ) -> Result<model::AccountAddress> {
if msg.is_default { if msg.is_default && msg.account_id.is_some() {
if let Err(e) = sqlx::query( if let Err(e) = sqlx::query(
r#" r#"
UPDATE account_addresses UPDATE account_addresses
@ -142,20 +142,23 @@ WHERE account_id = $1
log::error!("{}", e); log::error!("{}", e);
} }
} }
sqlx::query_as( sqlx::query_as(
r#" r#"
INSERT INTO account_addresses ( name, email, street, city, country, zip, account_id ) INSERT INTO account_addresses ( name, email, phone, street, city, country, zip, account_id )
VALUES ($1, $2, $3, $4, $5, $6, $7) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING id, name, email, street, city, country, zip, account_id, is_default RETURNING id, name, email, phone, street, city, country, zip, account_id, is_default
"#, "#,
) )
.bind(msg.name) .bind(msg.name)
.bind(msg.email) .bind(msg.email)
.bind(msg.phone)
.bind(msg.street) .bind(msg.street)
.bind(msg.city) .bind(msg.city)
.bind(msg.country) .bind(msg.country)
.bind(msg.zip) .bind(msg.zip)
.bind(msg.account_id) .bind(msg.account_id)
.bind(msg.is_default)
.fetch_one(pool) .fetch_one(pool)
.await .await
.map_err(|_| Error::CreateAccountAddress.into()) .map_err(|_| Error::CreateAccountAddress.into())
@ -167,6 +170,7 @@ pub struct UpdateAccountAddress {
pub id: model::AddressId, pub id: model::AddressId,
pub name: model::Name, pub name: model::Name,
pub email: model::Email, pub email: model::Email,
pub phone: model::Phone,
pub street: model::Street, pub street: model::Street,
pub city: model::City, pub city: model::City,
pub country: model::Country, pub country: model::Country,
@ -189,9 +193,9 @@ pub(crate) async fn update_account_address(
sqlx::query_as( sqlx::query_as(
r#" r#"
UPDATE account_addresses UPDATE account_addresses
SET name = $2, email = $3, street = $4, city = $5, country = $6, zip = $7, account_id = $8, is_default = $9 SET name = $2, email = $3, street = $4, city = $5, country = $6, zip = $7, account_id = $8, is_default = $9, phone = $10
WHERE id = $1 WHERE id = $1
RETURNING id, name, email, street, city, country, zip, account_id, is_default RETURNING id, name, email, phone, street, city, country, zip, account_id, is_default
"#, "#,
) )
.bind(msg.id) .bind(msg.id)
@ -203,7 +207,140 @@ RETURNING id, name, email, street, city, country, zip, account_id, is_default
.bind(msg.zip) .bind(msg.zip)
.bind(msg.account_id) .bind(msg.account_id)
.bind(msg.is_default) .bind(msg.is_default)
.bind(msg.phone)
.fetch_one(pool) .fetch_one(pool)
.await .await
.map_err(|_| Error::CreateAccountAddress.into()) .map_err(|_| Error::CreateAccountAddress.into())
} }
#[cfg(test)]
mod test {
use actix::{Actor, Addr};
use config::*;
use fake::Fake;
use model::*;
use crate::*;
pub struct NoOpts;
impl UpdateConfig for NoOpts {}
async fn test_create_account(db: Addr<Database>) -> FullAccount {
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();
db.send(CreateAccount {
email: model::Email::new(email),
login: model::Login::new(login),
pass_hash: model::PassHash::new(hash),
role: Role::Admin,
})
.await
.unwrap()
.unwrap()
}
#[actix::test]
async fn full_check() {
let config = config::default_load(&mut NoOpts);
config
.lock()
.database_mut()
.set_url("postgres://postgres@localhost/bazzar_test");
let db = crate::Database::build(config).await.start();
// account
let account = test_create_account(db.clone()).await;
// address
let mut address: AccountAddress = {
let name: String = fake::faker::name::en::Name().fake();
let email: String = fake::faker::internet::en::FreeEmail().fake();
let phone: String = fake::faker::phone_number::en::PhoneNumber().fake();
let street: String = fake::faker::address::en::StreetName().fake();
let city: String = fake::faker::address::en::CityName().fake();
let country: String = fake::faker::address::en::CountryName().fake();
let zip: String = fake::faker::address::en::ZipCode().fake();
let account_id = Some(account.id);
let is_default: bool = fake::faker::boolean::en::Boolean(6).fake();
let address = db
.send(CreateAccountAddress {
name: model::Name::new(name.clone()),
email: model::Email::new(email.clone()),
phone: model::Phone::new(phone.clone()),
street: model::Street::new(street.clone()),
city: model::City::new(city.clone()),
country: model::Country::new(country.clone()),
zip: model::Zip::new(zip.clone()),
account_id: account_id.clone(),
is_default: is_default.clone(),
})
.await
.unwrap()
.unwrap();
assert_eq!(
address,
model::AccountAddress {
id: model::AddressId::new(1),
name: model::Name::new(name.clone()),
email: model::Email::new(email.clone()),
phone: model::Phone::new(phone.clone()),
street: model::Street::new(street.clone()),
city: model::City::new(city.clone()),
country: model::Country::new(country.clone()),
zip: model::Zip::new(zip.clone()),
account_id: account.id,
is_default,
}
);
address
};
let found = db
.send(FindAccountAddress {
account_id: account.id,
address_id: address.id,
})
.await
.unwrap()
.unwrap();
assert_eq!(found, address);
let changed = db
.send(UpdateAccountAddress {
id: address.id,
name: address.name.clone(),
email: address.email.clone(),
phone: address.phone.clone(),
street: address.street.clone(),
city: address.city.clone(),
country: address.country.clone(),
zip: address.zip.clone(),
account_id: address.account_id.clone(),
is_default: true,
})
.await
.unwrap()
.unwrap();
address.is_default = true;
assert_eq!(changed, address);
let default_address = db
.send(DefaultAccountAddress {
account_id: account.id,
})
.await
.unwrap()
.unwrap();
assert_eq!(default_address, address);
}
}

View File

@ -196,16 +196,16 @@ impl Actor for Database {
/// ///
/// ``` /// ```
/// # use database_manager::photos::Error; /// # use database_manager::photos::Error;
/// async fn load() { /// async fn load(pool: sqlx::PgPool) {
/// # let pool: sqlx::PgPool::connect("").await.unwrap();
/// use database_manager::MultiLoad; /// use database_manager::MultiLoad;
/// let t = pool.begin().await.unwrap(); /// let mut t = pool.begin().await.unwrap();
/// let mut multi = MultiLoad::new( /// let mut multi = MultiLoad::new(
/// &mut t, /// &mut t,
/// "SELECT id, name FROM products WHERE ", /// "SELECT id, name FROM products WHERE ",
/// " id = " /// " id = "
/// ); /// );
/// multi.load(4, vec![1,2,3,4], |_| Error::All.into()); /// let products: Vec<model::Product> = multi.load(4, vec![1, 2, 3, 4].into_iter(), |_| Error::All.into())
/// .await.unwrap();
/// t.commit().await.unwrap(); /// t.commit().await.unwrap();
/// } /// }
/// ``` /// ```

View File

@ -78,6 +78,7 @@ impl OrderManager {
pub struct CreateOrderAddress { pub struct CreateOrderAddress {
pub name: model::Name, pub name: model::Name,
pub email: model::Email, pub email: model::Email,
pub phone: model::Phone,
pub street: model::Street, pub street: model::Street,
pub city: model::City, pub city: model::City,
pub country: model::Country, pub country: model::Country,
@ -130,6 +131,7 @@ pub(crate) async fn create_account_order(
database_manager::CreateAccountAddress { database_manager::CreateAccountAddress {
name: input.name, name: input.name,
email: input.email, email: input.email,
phone: input.phone,
street: input.street, street: input.street,
city: input.city, city: input.city,
country: input.country, country: input.country,

View File

@ -285,6 +285,7 @@ pub(crate) async fn create_order(
api::OrderAddressInput::Address(api::CreateOrderAddress { api::OrderAddressInput::Address(api::CreateOrderAddress {
name, name,
email, email,
phone,
street, street,
city, city,
country, country,
@ -292,6 +293,7 @@ pub(crate) async fn create_order(
}) => order_manager::OrderAddressInput::Address(order_manager::CreateOrderAddress { }) => order_manager::OrderAddressInput::Address(order_manager::CreateOrderAddress {
name, name,
email: email.clone(), email: email.clone(),
phone,
street, street,
city, city,
country, country,

View File

@ -51,7 +51,6 @@ async fn main() {
let db = database_manager::Database::build(config.clone()) let db = database_manager::Database::build(config.clone())
.await .await
.unwrap()
.start(); .start();
let res = tokio::join!( let res = tokio::join!(

6
scripts/test.sh Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env zsh
psql postgres postgres -c "DROP DATABASE bazzar_test"
psql postgres postgres -c "CREATE DATABASE bazzar_test"
sqlx migrate run --database-url='postgres://postgres@localhost/bazzar_test'
cargo test

View File

@ -63,6 +63,7 @@ pub struct AccountAddress {
pub id: AddressId, pub id: AddressId,
pub name: Name, pub name: Name,
pub email: Email, pub email: Email,
pub phone: Phone,
pub street: Street, pub street: Street,
pub city: City, pub city: City,
pub country: Country, pub country: Country,
@ -77,6 +78,7 @@ impl From<crate::AccountAddress> for AccountAddress {
id, id,
name, name,
email, email,
phone,
street, street,
city, city,
country, country,
@ -89,6 +91,7 @@ impl From<crate::AccountAddress> for AccountAddress {
id, id,
name, name,
email, email,
phone,
street, street,
city, city,
country, country,
@ -502,6 +505,7 @@ pub struct SearchRequest {
pub struct CreateOrderAddress { pub struct CreateOrderAddress {
pub name: Name, pub name: Name,
pub email: Email, pub email: Email,
pub phone: Phone,
pub street: Street, pub street: Street,
pub city: City, pub city: City,
pub country: Country, pub country: Country,

View File

@ -1,4 +1,5 @@
use fake::faker::internet::en::{FreeEmail, Password as FakePass, Username}; use fake::faker::internet::en::{FreeEmail, Password as FakePass, Username};
use fake::faker::phone_number::en::PhoneNumber;
use fake::Fake; use fake::Fake;
use crate::*; use crate::*;
@ -15,6 +16,12 @@ impl<T> fake::Dummy<T> for Email {
} }
} }
impl<T> fake::Dummy<T> for Phone {
fn dummy_with_rng<R: Rng + ?Sized>(_config: &T, _rng: &mut R) -> Self {
Self(PhoneNumber().fake())
}
}
impl<T> fake::Dummy<T> for ProductShortDesc { impl<T> fake::Dummy<T> for ProductShortDesc {
fn dummy_with_rng<R: Rng + ?Sized>(_config: &T, _rng: &mut R) -> Self { fn dummy_with_rng<R: Rng + ?Sized>(_config: &T, _rng: &mut R) -> Self {
use fake::faker::lorem::en::Words; use fake::faker::lorem::en::Words;

View File

@ -342,7 +342,9 @@ impl Login {
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Debug, Clone, Default, Deref, DerefMut, From, Display)] #[derive(
Serialize, Debug, Clone, Default, PartialOrd, PartialEq, Deref, DerefMut, From, Display,
)]
#[serde(transparent)] #[serde(transparent)]
pub struct Email(String); pub struct Email(String);
@ -356,6 +358,34 @@ impl Email {
} }
} }
#[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(
Serialize,
Deserialize,
Debug,
Clone,
Default,
PartialOrd,
PartialEq,
Deref,
DerefMut,
From,
Display,
)]
#[serde(transparent)]
pub struct Phone(String);
impl Phone {
pub fn invalid_empty() -> Self {
Self("".into())
}
pub fn new<S: Into<String>>(s: S) -> Self {
Self(s.into())
}
}
impl FromStr for Email { impl FromStr for Email {
type Err = TransformError; type Err = TransformError;
@ -680,7 +710,9 @@ impl PartialEq<PasswordConfirmation> for Password {
#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "dummy", derive(fake::Dummy))]
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref, Display, From)] #[derive(
Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq, Deref, Display, From,
)]
#[serde(transparent)] #[serde(transparent)]
pub struct AccountId(RecordId); pub struct AccountId(RecordId);
@ -1134,12 +1166,32 @@ pub enum ShippingMethod {
#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "dummy", derive(fake::Dummy))]
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, Deref, Display, From)] #[derive(
Serialize, Deserialize, Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Deref, Display, From,
)]
pub struct AddressId(RecordId); pub struct AddressId(RecordId);
impl AddressId {
pub fn new(id: RecordId) -> Self {
Self(id)
}
}
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)] #[derive(
Serialize,
Deserialize,
Debug,
Clone,
Default,
PartialOrd,
PartialEq,
Deref,
DerefMut,
From,
Display,
)]
#[serde(transparent)] #[serde(transparent)]
pub struct Name(String); pub struct Name(String);
@ -1151,7 +1203,19 @@ impl Name {
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)] #[derive(
Serialize,
Deserialize,
Debug,
Clone,
Default,
PartialOrd,
PartialEq,
Deref,
DerefMut,
From,
Display,
)]
#[serde(transparent)] #[serde(transparent)]
pub struct Street(String); pub struct Street(String);
@ -1163,7 +1227,19 @@ impl Street {
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)] #[derive(
Serialize,
Deserialize,
Debug,
Clone,
Default,
PartialOrd,
PartialEq,
Deref,
DerefMut,
From,
Display,
)]
#[serde(transparent)] #[serde(transparent)]
pub struct City(String); pub struct City(String);
@ -1175,7 +1251,19 @@ impl City {
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)] #[derive(
Serialize,
Deserialize,
Debug,
Clone,
Default,
PartialOrd,
PartialEq,
Deref,
DerefMut,
From,
Display,
)]
#[serde(transparent)] #[serde(transparent)]
pub struct Country(String); pub struct Country(String);
@ -1187,7 +1275,19 @@ impl Country {
#[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", derive(sqlx::Type))]
#[cfg_attr(feature = "db", sqlx(transparent))] #[cfg_attr(feature = "db", sqlx(transparent))]
#[derive(Serialize, Deserialize, Debug, Clone, Default, Deref, DerefMut, From, Display)] #[derive(
Serialize,
Deserialize,
Debug,
Clone,
Default,
PartialOrd,
PartialEq,
Deref,
DerefMut,
From,
Display,
)]
#[serde(transparent)] #[serde(transparent)]
pub struct Zip(String); pub struct Zip(String);
@ -1199,11 +1299,12 @@ impl Zip {
#[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "dummy", derive(fake::Dummy))]
#[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))]
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct AccountAddress { pub struct AccountAddress {
pub id: AddressId, pub id: AddressId,
pub name: Name, pub name: Name,
pub email: Email, pub email: Email,
pub phone: Phone,
pub street: Street, pub street: Street,
pub city: City, pub city: City,
pub country: Country, pub country: Country,