Fix creating address, move to transactional tests
This commit is contained in:
parent
93c66d33bf
commit
cf464beb27
@ -70,8 +70,6 @@ WHERE account_id = $1 AND id = $2
|
|||||||
.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>")]
|
||||||
pub struct DefaultAccountAddress {
|
pub struct DefaultAccountAddress {
|
||||||
@ -139,14 +137,15 @@ WHERE account_id = $1
|
|||||||
.fetch_all(&mut *pool)
|
.fetch_all(&mut *pool)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
log::error!("{}", e);
|
log::error!("{e}");
|
||||||
|
eprintln!("{e}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO account_addresses ( name, email, phone, street, city, country, zip, account_id )
|
INSERT INTO account_addresses ( name, email, phone, street, city, country, zip, account_id, is_default)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||||
RETURNING id, name, email, phone, street, city, country, zip, account_id, is_default
|
RETURNING id, name, email, phone, street, city, country, zip, account_id, is_default
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@ -161,7 +160,11 @@ RETURNING id, name, email, phone, street, city, country, zip, account_id, is_def
|
|||||||
.bind(msg.is_default)
|
.bind(msg.is_default)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| Error::CreateAccountAddress.into())
|
.map_err(|e| {
|
||||||
|
log::error!("{e}");
|
||||||
|
eprintln!("{e}");
|
||||||
|
Error::CreateAccountAddress.into()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
@ -215,7 +218,6 @@ RETURNING id, name, email, phone, street, city, country, zip, account_id, is_def
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use actix::{Actor, Addr};
|
|
||||||
use config::*;
|
use config::*;
|
||||||
use fake::Fake;
|
use fake::Fake;
|
||||||
use model::*;
|
use model::*;
|
||||||
@ -226,20 +228,22 @@ mod test {
|
|||||||
|
|
||||||
impl UpdateConfig for NoOpts {}
|
impl UpdateConfig for NoOpts {}
|
||||||
|
|
||||||
async fn test_create_account(db: Addr<Database>) -> FullAccount {
|
async fn test_create_account(pool: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> FullAccount {
|
||||||
let login: String = fake::faker::internet::en::Username().fake();
|
let login: String = fake::faker::internet::en::Username().fake();
|
||||||
let email: String = fake::faker::internet::en::FreeEmail().fake();
|
let email: String = fake::faker::internet::en::FreeEmail().fake();
|
||||||
let hash: String = fake::faker::internet::en::Password(10..20).fake();
|
let hash: String = fake::faker::internet::en::Password(10..20).fake();
|
||||||
|
|
||||||
db.send(CreateAccount {
|
crate::create_account(
|
||||||
email: Email::new(email),
|
CreateAccount {
|
||||||
login: Login::new(login),
|
email: Email::new(email),
|
||||||
pass_hash: PassHash::new(hash),
|
login: Login::new(login),
|
||||||
role: Role::Admin,
|
pass_hash: PassHash::new(hash),
|
||||||
})
|
role: Role::Admin,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix::test]
|
#[actix::test]
|
||||||
@ -250,10 +254,12 @@ mod test {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
// account
|
// account
|
||||||
let account = test_create_account(db.clone()).await;
|
let account = test_create_account(&mut t).await;
|
||||||
|
|
||||||
// address
|
// address
|
||||||
let mut address: AccountAddress = {
|
let mut address: AccountAddress = {
|
||||||
@ -265,35 +271,36 @@ mod test {
|
|||||||
let country: String = fake::faker::address::en::CountryName().fake();
|
let country: String = fake::faker::address::en::CountryName().fake();
|
||||||
let zip: String = fake::faker::address::en::ZipCode().fake();
|
let zip: String = fake::faker::address::en::ZipCode().fake();
|
||||||
let account_id = Some(account.id);
|
let account_id = Some(account.id);
|
||||||
let is_default: bool = fake::faker::boolean::en::Boolean(6).fake();
|
let is_default: bool = true;
|
||||||
|
|
||||||
let address = db
|
let address = super::create_address(
|
||||||
.send(CreateAccountAddress {
|
CreateAccountAddress {
|
||||||
name: model::Name::new(name.clone()),
|
name: Name::new(name.clone()),
|
||||||
email: model::Email::new(email.clone()),
|
email: Email::new(email.clone()),
|
||||||
phone: model::Phone::new(phone.clone()),
|
phone: Phone::new(phone.clone()),
|
||||||
street: model::Street::new(street.clone()),
|
street: Street::new(street.clone()),
|
||||||
city: model::City::new(city.clone()),
|
city: City::new(city.clone()),
|
||||||
country: model::Country::new(country.clone()),
|
country: Country::new(country.clone()),
|
||||||
zip: model::Zip::new(zip.clone()),
|
zip: Zip::new(zip.clone()),
|
||||||
account_id: account_id.clone(),
|
account_id: account_id.clone(),
|
||||||
is_default: is_default.clone(),
|
is_default,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
address,
|
address,
|
||||||
model::AccountAddress {
|
AccountAddress {
|
||||||
id: address.id,
|
id: address.id,
|
||||||
name: model::Name::new(name.clone()),
|
name: Name::new(name.clone()),
|
||||||
email: model::Email::new(email.clone()),
|
email: Email::new(email.clone()),
|
||||||
phone: model::Phone::new(phone.clone()),
|
phone: Phone::new(phone.clone()),
|
||||||
street: model::Street::new(street.clone()),
|
street: Street::new(street.clone()),
|
||||||
city: model::City::new(city.clone()),
|
city: City::new(city.clone()),
|
||||||
country: model::Country::new(country.clone()),
|
country: Country::new(country.clone()),
|
||||||
zip: model::Zip::new(zip.clone()),
|
zip: Zip::new(zip.clone()),
|
||||||
account_id: account.id,
|
account_id: account.id,
|
||||||
is_default,
|
is_default,
|
||||||
}
|
}
|
||||||
@ -301,19 +308,20 @@ mod test {
|
|||||||
address
|
address
|
||||||
};
|
};
|
||||||
|
|
||||||
let found = db
|
let found = super::find_account_address(
|
||||||
.send(FindAccountAddress {
|
FindAccountAddress {
|
||||||
account_id: account.id,
|
account_id: account.id,
|
||||||
address_id: address.id,
|
address_id: address.id,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(found, address);
|
assert_eq!(found, address);
|
||||||
|
|
||||||
let changed = db
|
let changed = super::update_account_address(
|
||||||
.send(UpdateAccountAddress {
|
UpdateAccountAddress {
|
||||||
id: address.id,
|
id: address.id,
|
||||||
name: address.name.clone(),
|
name: address.name.clone(),
|
||||||
email: address.email.clone(),
|
email: address.email.clone(),
|
||||||
@ -324,23 +332,26 @@ mod test {
|
|||||||
zip: address.zip.clone(),
|
zip: address.zip.clone(),
|
||||||
account_id: address.account_id.clone(),
|
account_id: address.account_id.clone(),
|
||||||
is_default: true,
|
is_default: true,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
address.is_default = true;
|
address.is_default = true;
|
||||||
|
|
||||||
assert_eq!(changed, address);
|
assert_eq!(changed, address);
|
||||||
|
|
||||||
let default_address = db
|
let default_address = super::default_account_address(
|
||||||
.send(DefaultAccountAddress {
|
DefaultAccountAddress {
|
||||||
account_id: account.id,
|
account_id: account.id,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
assert_eq!(default_address, address);
|
assert_eq!(default_address, address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#[cfg(feature = "dummy")]
|
#[cfg(feature = "dummy")]
|
||||||
use fake::Fake;
|
use fake::Fake;
|
||||||
use model::{AccountId, AccountState, Email, FullAccount, Login, PassHash, Role};
|
use model::*;
|
||||||
use sqlx::PgPool;
|
|
||||||
|
|
||||||
use crate::{db_async_handler, Result};
|
use crate::{db_async_handler, Result};
|
||||||
|
|
||||||
@ -24,16 +23,24 @@ pub enum Error {
|
|||||||
#[rtype(result = "Result<Vec<FullAccount>>")]
|
#[rtype(result = "Result<Vec<FullAccount>>")]
|
||||||
pub struct AllAccounts;
|
pub struct AllAccounts;
|
||||||
|
|
||||||
db_async_handler!(AllAccounts, all_accounts, Vec<FullAccount>);
|
db_async_handler!(
|
||||||
|
AllAccounts,
|
||||||
|
all_accounts,
|
||||||
|
Vec<FullAccount>,
|
||||||
|
inner_all_accounts
|
||||||
|
);
|
||||||
|
|
||||||
pub(crate) async fn all_accounts(_msg: AllAccounts, pool: PgPool) -> Result<Vec<FullAccount>> {
|
pub(crate) async fn all_accounts(
|
||||||
|
_msg: AllAccounts,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<Vec<FullAccount>> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, email, login, pass_hash, role, customer_id, state
|
SELECT id, email, login, pass_hash, role, customer_id, state
|
||||||
FROM accounts
|
FROM accounts
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.fetch_all(&pool)
|
.fetch_all(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -51,9 +58,17 @@ pub struct CreateAccount {
|
|||||||
pub role: Role,
|
pub role: Role,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(CreateAccount, create_account, FullAccount);
|
db_async_handler!(
|
||||||
|
CreateAccount,
|
||||||
|
create_account,
|
||||||
|
FullAccount,
|
||||||
|
inner_create_account
|
||||||
|
);
|
||||||
|
|
||||||
pub(crate) async fn create_account(msg: CreateAccount, db: PgPool) -> Result<FullAccount> {
|
pub(crate) async fn create_account(
|
||||||
|
msg: CreateAccount,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<FullAccount> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO accounts (login, email, role, pass_hash)
|
INSERT INTO accounts (login, email, role, pass_hash)
|
||||||
@ -65,7 +80,7 @@ RETURNING id, email, login, pass_hash, role, customer_id, state
|
|||||||
.bind(msg.email)
|
.bind(msg.email)
|
||||||
.bind(msg.role)
|
.bind(msg.role)
|
||||||
.bind(msg.pass_hash)
|
.bind(msg.pass_hash)
|
||||||
.fetch_one(&db)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -85,9 +100,17 @@ pub struct UpdateAccount {
|
|||||||
pub state: AccountState,
|
pub state: AccountState,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(UpdateAccount, update_account, FullAccount);
|
db_async_handler!(
|
||||||
|
UpdateAccount,
|
||||||
|
update_account,
|
||||||
|
FullAccount,
|
||||||
|
inner_update_account
|
||||||
|
);
|
||||||
|
|
||||||
pub(crate) async fn update_account(msg: UpdateAccount, db: PgPool) -> Result<FullAccount> {
|
pub(crate) async fn update_account(
|
||||||
|
msg: UpdateAccount,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<FullAccount> {
|
||||||
match msg.pass_hash {
|
match msg.pass_hash {
|
||||||
Some(hash) => sqlx::query_as(
|
Some(hash) => sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
@ -117,7 +140,7 @@ RETURNING id, email, login, pass_hash, role, customer_id, state
|
|||||||
.bind(msg.role)
|
.bind(msg.role)
|
||||||
.bind(msg.state),
|
.bind(msg.state),
|
||||||
}
|
}
|
||||||
.fetch_one(&db)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -131,9 +154,12 @@ pub struct FindAccount {
|
|||||||
pub account_id: AccountId,
|
pub account_id: AccountId,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(FindAccount, find_account, FullAccount);
|
db_async_handler!(FindAccount, find_account, FullAccount, inner_find_account);
|
||||||
|
|
||||||
pub(crate) async fn find_account(msg: FindAccount, db: PgPool) -> Result<FullAccount> {
|
pub(crate) async fn find_account(
|
||||||
|
msg: FindAccount,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<FullAccount> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, email, login, pass_hash, role, customer_id, state
|
SELECT id, email, login, pass_hash, role, customer_id, state
|
||||||
@ -142,7 +168,7 @@ WHERE id = $1
|
|||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.account_id)
|
.bind(msg.account_id)
|
||||||
.fetch_one(&db)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -157,9 +183,17 @@ pub struct AccountByIdentity {
|
|||||||
pub email: Option<Email>,
|
pub email: Option<Email>,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(AccountByIdentity, account_by_identity, FullAccount);
|
db_async_handler!(
|
||||||
|
AccountByIdentity,
|
||||||
|
account_by_identity,
|
||||||
|
FullAccount,
|
||||||
|
inner_account_by_identity
|
||||||
|
);
|
||||||
|
|
||||||
pub(crate) async fn account_by_identity(msg: AccountByIdentity, db: PgPool) -> Result<FullAccount> {
|
pub(crate) async fn account_by_identity(
|
||||||
|
msg: AccountByIdentity,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<FullAccount> {
|
||||||
match (msg.login, msg.email) {
|
match (msg.login, msg.email) {
|
||||||
(Some(login), None) => sqlx::query_as(
|
(Some(login), None) => sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
@ -188,7 +222,7 @@ WHERE login = $1 AND email = $2
|
|||||||
.bind(email),
|
.bind(email),
|
||||||
_ => return Err(super::Error::Account(Error::NoIdentity)),
|
_ => return Err(super::Error::Account(Error::NoIdentity)),
|
||||||
}
|
}
|
||||||
.fetch_one(&db)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -198,7 +232,6 @@ WHERE login = $1 AND email = $2
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix::Addr;
|
|
||||||
use config::UpdateConfig;
|
use config::UpdateConfig;
|
||||||
use fake::Fake;
|
use fake::Fake;
|
||||||
use model::*;
|
use model::*;
|
||||||
@ -210,7 +243,7 @@ mod tests {
|
|||||||
impl UpdateConfig for NoOpts {}
|
impl UpdateConfig for NoOpts {}
|
||||||
|
|
||||||
async fn test_create_account(
|
async fn test_create_account(
|
||||||
db: Addr<Database>,
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
login: Option<String>,
|
login: Option<String>,
|
||||||
email: Option<String>,
|
email: Option<String>,
|
||||||
hash: Option<String>,
|
hash: Option<String>,
|
||||||
@ -220,15 +253,17 @@ mod tests {
|
|||||||
let email: String = email.unwrap_or_else(|| en::FreeEmail().fake());
|
let email: String = email.unwrap_or_else(|| en::FreeEmail().fake());
|
||||||
let hash: String = hash.unwrap_or_else(|| en::Password(10..20).fake());
|
let hash: String = hash.unwrap_or_else(|| en::Password(10..20).fake());
|
||||||
|
|
||||||
db.send(CreateAccount {
|
super::create_account(
|
||||||
email: Email::new(email),
|
CreateAccount {
|
||||||
login: Login::new(login),
|
email: Email::new(email),
|
||||||
pass_hash: PassHash::new(hash),
|
login: Login::new(login),
|
||||||
role: Role::Admin,
|
pass_hash: PassHash::new(hash),
|
||||||
})
|
role: Role::Admin,
|
||||||
|
},
|
||||||
|
t,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix::test]
|
#[actix::test]
|
||||||
@ -239,22 +274,25 @@ mod tests {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let login: String = fake::faker::internet::en::Username().fake();
|
let login: String = fake::faker::internet::en::Username().fake();
|
||||||
let email: String = fake::faker::internet::en::FreeEmail().fake();
|
let email: String = fake::faker::internet::en::FreeEmail().fake();
|
||||||
let hash: String = fake::faker::internet::en::Password(10..20).fake();
|
let hash: String = fake::faker::internet::en::Password(10..20).fake();
|
||||||
|
|
||||||
let account: FullAccount = db
|
let account: FullAccount = super::create_account(
|
||||||
.send(CreateAccount {
|
CreateAccount {
|
||||||
email: Email::new(&email),
|
email: Email::new(&email),
|
||||||
login: Login::new(&login),
|
login: Login::new(&login),
|
||||||
pass_hash: PassHash::new(&hash),
|
pass_hash: PassHash::new(&hash),
|
||||||
role: Role::Admin,
|
role: Role::Admin,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let expected = FullAccount {
|
let expected = FullAccount {
|
||||||
login: Login::new(login),
|
login: Login::new(login),
|
||||||
@ -266,6 +304,7 @@ mod tests {
|
|||||||
state: AccountState::Active,
|
state: AccountState::Active,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
assert_eq!(account, expected);
|
assert_eq!(account, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,14 +316,17 @@ mod tests {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
test_create_account(db.clone(), None, None, None).await;
|
test_create_account(&mut t, None, None, None).await;
|
||||||
test_create_account(db.clone(), None, None, None).await;
|
test_create_account(&mut t, None, None, None).await;
|
||||||
test_create_account(db.clone(), None, None, None).await;
|
test_create_account(&mut t, None, None, None).await;
|
||||||
|
|
||||||
let v: Vec<FullAccount> = db.send(AllAccounts).await.unwrap().unwrap();
|
let v: Vec<FullAccount> = super::all_accounts(AllAccounts, &mut t).await.unwrap();
|
||||||
assert!(v.len() >= 3);
|
assert!(v.len() >= 3);
|
||||||
|
t.rollback().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix::test]
|
#[actix::test]
|
||||||
@ -295,14 +337,16 @@ mod tests {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let original_login: String = fake::faker::internet::en::Username().fake();
|
let original_login: String = fake::faker::internet::en::Username().fake();
|
||||||
let original_email: String = fake::faker::internet::en::FreeEmail().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_hash: String = fake::faker::internet::en::Password(10..20).fake();
|
||||||
|
|
||||||
let original_account = test_create_account(
|
let original_account = test_create_account(
|
||||||
db.clone(),
|
&mut t,
|
||||||
Some(original_login.clone()),
|
Some(original_login.clone()),
|
||||||
Some(original_email.clone()),
|
Some(original_email.clone()),
|
||||||
Some(original_hash.clone()),
|
Some(original_hash.clone()),
|
||||||
@ -312,18 +356,19 @@ mod tests {
|
|||||||
let updated_login: String = fake::faker::internet::en::Username().fake();
|
let updated_login: String = fake::faker::internet::en::Username().fake();
|
||||||
let updated_email: String = fake::faker::internet::en::FreeEmail().fake();
|
let updated_email: String = fake::faker::internet::en::FreeEmail().fake();
|
||||||
|
|
||||||
let updated_account: FullAccount = db
|
let updated_account: FullAccount = super::update_account(
|
||||||
.send(UpdateAccount {
|
UpdateAccount {
|
||||||
id: original_account.id,
|
id: original_account.id,
|
||||||
email: Email::new(updated_email.clone()),
|
email: Email::new(updated_email.clone()),
|
||||||
login: Login::new(updated_login.clone()),
|
login: Login::new(updated_login.clone()),
|
||||||
pass_hash: None,
|
pass_hash: None,
|
||||||
role: Role::Admin,
|
role: Role::Admin,
|
||||||
state: AccountState::Active,
|
state: AccountState::Active,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let expected = FullAccount {
|
let expected = FullAccount {
|
||||||
id: original_account.id,
|
id: original_account.id,
|
||||||
@ -335,6 +380,7 @@ mod tests {
|
|||||||
state: AccountState::Active,
|
state: AccountState::Active,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
assert_ne!(original_account, expected);
|
assert_ne!(original_account, expected);
|
||||||
assert_eq!(updated_account, expected);
|
assert_eq!(updated_account, expected);
|
||||||
}
|
}
|
||||||
@ -347,14 +393,16 @@ mod tests {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let original_login: String = fake::faker::internet::en::Username().fake();
|
let original_login: String = fake::faker::internet::en::Username().fake();
|
||||||
let original_email: String = fake::faker::internet::en::FreeEmail().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_hash: String = fake::faker::internet::en::Password(10..20).fake();
|
||||||
|
|
||||||
let original_account = test_create_account(
|
let original_account = test_create_account(
|
||||||
db.clone(),
|
&mut t,
|
||||||
Some(original_login.clone()),
|
Some(original_login.clone()),
|
||||||
Some(original_email.clone()),
|
Some(original_email.clone()),
|
||||||
Some(original_hash.clone()),
|
Some(original_hash.clone()),
|
||||||
@ -365,18 +413,19 @@ mod tests {
|
|||||||
let updated_email: String = fake::faker::internet::en::FreeEmail().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_hash: String = fake::faker::internet::en::Password(10..20).fake();
|
||||||
|
|
||||||
let updated_account: FullAccount = db
|
let updated_account: FullAccount = super::update_account(
|
||||||
.send(UpdateAccount {
|
UpdateAccount {
|
||||||
id: original_account.id,
|
id: original_account.id,
|
||||||
email: Email::new(updated_email.clone()),
|
email: Email::new(updated_email.clone()),
|
||||||
login: Login::new(updated_login.clone()),
|
login: Login::new(updated_login.clone()),
|
||||||
pass_hash: Some(PassHash::new(updated_hash.clone())),
|
pass_hash: Some(PassHash::new(updated_hash.clone())),
|
||||||
role: Role::Admin,
|
role: Role::Admin,
|
||||||
state: AccountState::Active,
|
state: AccountState::Active,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let expected = FullAccount {
|
let expected = FullAccount {
|
||||||
id: original_account.id,
|
id: original_account.id,
|
||||||
@ -388,6 +437,7 @@ mod tests {
|
|||||||
state: AccountState::Active,
|
state: AccountState::Active,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
assert_ne!(original_account, expected);
|
assert_ne!(original_account, expected);
|
||||||
assert_eq!(updated_account, expected);
|
assert_eq!(updated_account, expected);
|
||||||
}
|
}
|
||||||
@ -400,18 +450,22 @@ mod tests {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let account = test_create_account(db.clone(), None, None, None).await;
|
let account = test_create_account(&mut t, None, None, None).await;
|
||||||
|
|
||||||
let res: FullAccount = db
|
let res: FullAccount = super::find_account(
|
||||||
.send(FindAccount {
|
FindAccount {
|
||||||
account_id: account.id,
|
account_id: account.id,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
assert_eq!(account, res);
|
assert_eq!(account, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,19 +477,23 @@ mod tests {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let account = test_create_account(db.clone(), None, None, None).await;
|
let account = test_create_account(&mut t, None, None, None).await;
|
||||||
|
|
||||||
let res: FullAccount = db
|
let res: FullAccount = super::account_by_identity(
|
||||||
.send(AccountByIdentity {
|
AccountByIdentity {
|
||||||
email: Some(account.email.clone()),
|
email: Some(account.email.clone()),
|
||||||
login: None,
|
login: None,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
assert_eq!(account, res);
|
assert_eq!(account, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,19 +505,23 @@ mod tests {
|
|||||||
.database_mut()
|
.database_mut()
|
||||||
.set_url("postgres://postgres@localhost/bazzar_test");
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
let db = Database::build(config).await.start();
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
let account = test_create_account(db.clone(), None, None, None).await;
|
let account = test_create_account(&mut t, None, None, None).await;
|
||||||
|
|
||||||
let res: FullAccount = db
|
let res: FullAccount = super::account_by_identity(
|
||||||
.send(AccountByIdentity {
|
AccountByIdentity {
|
||||||
login: Some(account.login.clone()),
|
login: Some(account.login.clone()),
|
||||||
email: None,
|
email: None,
|
||||||
})
|
},
|
||||||
.await
|
&mut t,
|
||||||
.unwrap()
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
assert_eq!(account, res);
|
assert_eq!(account, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,7 @@ RETURNING id,
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
|
eprintln!("{e:?}");
|
||||||
crate::Error::Product(Error::Create)
|
crate::Error::Product(Error::Create)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use actix::Message;
|
use actix::Message;
|
||||||
use model::{ProductId, Quantity, QuantityUnit, Stock, StockId};
|
use model::{ProductId, Quantity, QuantityUnit, Stock, StockId};
|
||||||
use sqlx::PgPool;
|
|
||||||
|
|
||||||
use crate::{MultiLoad, Result};
|
use crate::{MultiLoad, Result};
|
||||||
|
|
||||||
@ -22,16 +21,19 @@ pub enum Error {
|
|||||||
#[rtype(result = "Result<Vec<model::Stock>>")]
|
#[rtype(result = "Result<Vec<model::Stock>>")]
|
||||||
pub struct AllStocks;
|
pub struct AllStocks;
|
||||||
|
|
||||||
crate::db_async_handler!(AllStocks, all_stocks, Vec<Stock>);
|
crate::db_async_handler!(AllStocks, all_stocks, Vec<Stock>, inner_all_stocks);
|
||||||
|
|
||||||
async fn all_stocks(_msg: AllStocks, pool: PgPool) -> Result<Vec<model::Stock>> {
|
async fn all_stocks(
|
||||||
|
_msg: AllStocks,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<Vec<Stock>> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, product_id, quantity, quantity_unit
|
SELECT id, product_id, quantity, quantity_unit
|
||||||
FROM stocks
|
FROM stocks
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.fetch_all(&pool)
|
.fetch_all(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -47,9 +49,12 @@ pub struct CreateStock {
|
|||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::db_async_handler!(CreateStock, create_stock, Stock);
|
crate::db_async_handler!(CreateStock, create_stock, Stock, inner_create_stock);
|
||||||
|
|
||||||
async fn create_stock(msg: CreateStock, pool: PgPool) -> Result<model::Stock> {
|
async fn create_stock(
|
||||||
|
msg: CreateStock,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<Stock> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO stocks (product_id, quantity, quantity_unit)
|
INSERT INTO stocks (product_id, quantity, quantity_unit)
|
||||||
@ -60,10 +65,11 @@ RETURNING id, product_id, quantity, quantity_unit
|
|||||||
.bind(msg.product_id)
|
.bind(msg.product_id)
|
||||||
.bind(msg.quantity)
|
.bind(msg.quantity)
|
||||||
.bind(msg.quantity_unit)
|
.bind(msg.quantity_unit)
|
||||||
.fetch_one(&pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
|
eprintln!("{e:?}");
|
||||||
crate::Error::Stock(Error::Create)
|
crate::Error::Stock(Error::Create)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -77,9 +83,12 @@ pub struct UpdateStock {
|
|||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::db_async_handler!(UpdateStock, update_stock, Stock);
|
crate::db_async_handler!(UpdateStock, update_stock, Stock, inner_update_stock);
|
||||||
|
|
||||||
async fn update_stock(msg: UpdateStock, pool: PgPool) -> Result<model::Stock> {
|
async fn update_stock(
|
||||||
|
msg: UpdateStock,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<Stock> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
UPDATE stocks
|
UPDATE stocks
|
||||||
@ -94,7 +103,7 @@ RETURNING id, product_id, quantity, quantity_unit
|
|||||||
.bind(msg.quantity)
|
.bind(msg.quantity)
|
||||||
.bind(msg.quantity_unit)
|
.bind(msg.quantity_unit)
|
||||||
.bind(msg.id)
|
.bind(msg.id)
|
||||||
.fetch_one(&pool)
|
.fetch_one(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -108,9 +117,17 @@ pub struct DeleteStock {
|
|||||||
pub stock_id: StockId,
|
pub stock_id: StockId,
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::db_async_handler!(DeleteStock, delete_stock, Option<model::Stock>);
|
crate::db_async_handler!(
|
||||||
|
DeleteStock,
|
||||||
|
delete_stock,
|
||||||
|
Option<model::Stock>,
|
||||||
|
inner_delete_stock
|
||||||
|
);
|
||||||
|
|
||||||
async fn delete_stock(msg: DeleteStock, pool: PgPool) -> Result<Option<Stock>> {
|
async fn delete_stock(
|
||||||
|
msg: DeleteStock,
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
) -> Result<Option<Stock>> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM stocks
|
DELETE FROM stocks
|
||||||
@ -119,7 +136,7 @@ RETURNING id, product_id, quantity, quantity_unit
|
|||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.stock_id)
|
.bind(msg.stock_id)
|
||||||
.fetch_optional(&pool)
|
.fetch_optional(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -143,7 +160,7 @@ crate::db_async_handler!(
|
|||||||
async fn product_stock(
|
async fn product_stock(
|
||||||
msg: ProductsStock,
|
msg: ProductsStock,
|
||||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<Vec<model::Stock>> {
|
) -> Result<Vec<Stock>> {
|
||||||
Ok(MultiLoad::new(
|
Ok(MultiLoad::new(
|
||||||
pool,
|
pool,
|
||||||
r#"
|
r#"
|
||||||
@ -160,3 +177,102 @@ async fn product_stock(
|
|||||||
)
|
)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use config::UpdateConfig;
|
||||||
|
use fake::faker::lorem::en as lorem;
|
||||||
|
use fake::Fake;
|
||||||
|
use model::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub struct NoOpts;
|
||||||
|
|
||||||
|
impl UpdateConfig for NoOpts {}
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
async fn test_product(pool: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Product {
|
||||||
|
create_product(
|
||||||
|
CreateProduct {
|
||||||
|
name: ProductName::new(format!("db stocks test product {}", Uuid::new_v4())),
|
||||||
|
short_description: ProductShortDesc::new(lorem::Paragraph(1..2).fake::<String>()),
|
||||||
|
long_description: ProductLongDesc::new(lorem::Paragraph(4..5).fake::<String>()),
|
||||||
|
category: None,
|
||||||
|
price: Price::from_u32(12321),
|
||||||
|
deliver_days_flag: Days(vec![Day::Friday, Day::Sunday]),
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn test_stock(
|
||||||
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
|
product_id: Option<ProductId>,
|
||||||
|
quantity: Option<Quantity>,
|
||||||
|
quantity_unit: Option<QuantityUnit>,
|
||||||
|
) -> Stock {
|
||||||
|
let product_id = match product_id {
|
||||||
|
Some(id) => id,
|
||||||
|
_ => test_product(&mut *pool).await.id,
|
||||||
|
};
|
||||||
|
let quantity = quantity.unwrap_or_else(|| Quantity::from_u32(345));
|
||||||
|
let quantity_unit = quantity_unit.unwrap_or_else(|| QuantityUnit::Piece);
|
||||||
|
|
||||||
|
super::create_stock(
|
||||||
|
CreateStock {
|
||||||
|
product_id,
|
||||||
|
quantity_unit,
|
||||||
|
quantity,
|
||||||
|
},
|
||||||
|
&mut *pool,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix::test]
|
||||||
|
async fn create_stock() {
|
||||||
|
let config = config::default_load(&mut NoOpts);
|
||||||
|
config
|
||||||
|
.lock()
|
||||||
|
.database_mut()
|
||||||
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
test_stock(&mut t, None, None, None).await;
|
||||||
|
t.rollback().await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix::test]
|
||||||
|
async fn products_stock() {
|
||||||
|
let config = config::default_load(&mut NoOpts);
|
||||||
|
config
|
||||||
|
.lock()
|
||||||
|
.database_mut()
|
||||||
|
.set_url("postgres://postgres@localhost/bazzar_test");
|
||||||
|
|
||||||
|
let db = Database::build(config).await;
|
||||||
|
let pool = db.pool();
|
||||||
|
let mut t = pool.begin().await.unwrap();
|
||||||
|
|
||||||
|
let first = test_stock(&mut t, None, None, None).await;
|
||||||
|
let second = test_stock(&mut t, None, None, None).await;
|
||||||
|
|
||||||
|
let stocks: Vec<Stock> = super::product_stock(
|
||||||
|
ProductsStock {
|
||||||
|
product_ids: vec![first.product_id, second.product_id],
|
||||||
|
},
|
||||||
|
&mut t,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
t.rollback().await.unwrap();
|
||||||
|
assert_eq!(stocks, vec![first, second]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -172,7 +172,7 @@ impl Role {
|
|||||||
|
|
||||||
#[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))]
|
||||||
#[derive(Copy, Clone, Debug, Hash, Display, Deserialize, Serialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Hash, Display, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum QuantityUnit {
|
pub enum QuantityUnit {
|
||||||
#[cfg_attr(feature = "db", sqlx(rename = "g"))]
|
#[cfg_attr(feature = "db", sqlx(rename = "g"))]
|
||||||
@ -283,6 +283,12 @@ impl Default for Audience {
|
|||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Price(NonNegative);
|
pub struct Price(NonNegative);
|
||||||
|
|
||||||
|
impl Price {
|
||||||
|
pub fn from_u32(price: u32) -> Self {
|
||||||
|
Self(NonNegative(price as i32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ops::Mul<Quantity> for Price {
|
impl ops::Mul<Quantity> for Price {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@ -294,7 +300,7 @@ impl ops::Mul<Quantity> for Price {
|
|||||||
#[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, Default, Debug, Copy, Clone, Hash, Deref, From)]
|
#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Copy, Clone, Hash, Deref, From)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Quantity(NonNegative);
|
pub struct Quantity(NonNegative);
|
||||||
|
|
||||||
@ -429,7 +435,7 @@ impl<'de> serde::Deserialize<'de> for Email {
|
|||||||
|
|
||||||
#[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, Default, Debug, Copy, Clone, Hash, Deref, Display)]
|
#[derive(Serialize, Default, Debug, PartialEq, Copy, Clone, Hash, Deref, Display)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct NonNegative(i32);
|
pub struct NonNegative(i32);
|
||||||
|
|
||||||
@ -591,7 +597,7 @@ impl TryFrom<i32> for Day {
|
|||||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||||
#[derive(Serialize, Deserialize, Hash, Debug)]
|
#[derive(Serialize, Deserialize, Hash, Debug)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct Days(Vec<Day>);
|
pub struct Days(pub Vec<Day>);
|
||||||
|
|
||||||
impl ops::Deref for Days {
|
impl ops::Deref for Days {
|
||||||
type Target = Vec<Day>;
|
type Target = Vec<Day>;
|
||||||
@ -856,13 +862,13 @@ pub struct Product {
|
|||||||
#[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)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct StockId(pub RecordId);
|
pub struct StockId(pub RecordId);
|
||||||
|
|
||||||
#[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)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Stock {
|
pub struct Stock {
|
||||||
pub id: StockId,
|
pub id: StockId,
|
||||||
pub product_id: ProductId,
|
pub product_id: ProductId,
|
||||||
|
Loading…
Reference in New Issue
Block a user