Start testing orders

This commit is contained in:
Adrian Woźniak 2022-06-06 16:07:49 +02:00
parent 7c272d310f
commit e0975cc94b
No known key found for this signature in database
GPG Key ID: 0012845A89C7352B
8 changed files with 357 additions and 67 deletions

View File

@ -128,3 +128,17 @@ RETURNING id, name, email, street, city, country, zip
.await
.map_err(|_| Error::CreateOrderAddress.into())
}
#[cfg(test)]
mod tests {
use config::UpdateConfig;
use fake::Fake;
use model::*;
use uuid::Uuid;
pub struct NoOpts;
impl UpdateConfig for NoOpts {}
use crate::*;
}

View File

@ -109,7 +109,7 @@ WHERE id = $1
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<OrderItem>>")]
pub struct OrderItems {
pub order_id: model::OrderId,
pub order_id: OrderId,
}
db_async_handler!(OrderItems, order_items, Vec<OrderItem>);

View File

@ -1,5 +1,4 @@
use model::*;
use sqlx::PgPool;
use super::Result;
use crate::{
@ -22,11 +21,14 @@ pub enum Error {
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<Order>>")]
pub struct AllAccountOrders;
pub struct AllOrders;
db_async_handler!(AllAccountOrders, all_orders, Vec<Order>);
db_async_handler!(AllOrders, all_orders, Vec<Order>, inner_all_orders);
pub(crate) async fn all_orders(_msg: AllAccountOrders, pool: PgPool) -> Result<Vec<Order>> {
pub(crate) async fn all_orders(
_msg: AllOrders,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Vec<Order>> {
sqlx::query_as(
r#"
SELECT id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
@ -34,7 +36,7 @@ FROM orders
ORDER BY id DESC
"#,
)
.fetch_all(&pool)
.fetch_all(t)
.await
.map_err(|e| {
log::error!("{e:?}");
@ -54,39 +56,38 @@ pub mod create_order {
#[derive(actix::Message)]
#[rtype(result = "Result<Order>")]
pub struct CreateAccountOrder {
pub struct CreateOrder {
pub buyer_id: AccountId,
pub items: Vec<create_order::OrderItem>,
pub shopping_cart_id: ShoppingCartId,
pub shopping_cart_id: Option<ShoppingCartId>,
pub checkout_notes: Option<String>,
pub delivery_address_id: OrderAddressId,
}
db_async_handler!(
CreateAccountOrder,
create_account_order,
Order,
inner_create_account_order
);
db_async_handler!(CreateOrder, create_order, Order, inner_create_order);
pub(crate) async fn create_account_order(
msg: CreateAccountOrder,
pub(crate) async fn create_order(
msg: CreateOrder,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Order> {
let order: Order = match sqlx::query_as(
r#"
INSERT INTO orders (buyer_id, status)
VALUES ($1, $2, $3)
INSERT INTO orders (buyer_id, status, checkout_notes, address_id)
VALUES ($1, $2, $3, $4)
RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
"#,
)
.bind(msg.buyer_id)
.bind(OrderStatus::Confirmed)
.bind(msg.checkout_notes.as_deref())
.bind(msg.delivery_address_id)
.fetch_one(&mut *t)
.await
{
Ok(order) => order,
Err(e) => {
log::error!("{e:?}");
dbg!(e);
return Err(super::Error::AccountOrder(Error::CantCreate));
}
};
@ -102,42 +103,49 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes,
)
.await
{
dbg!(e);
log::error!("{e:?}");
return Err(super::Error::AccountOrder(Error::CantCreate));
}
}
if let Err(e) = shopping_cart_set_state(
ShoppingCartSetState {
id: msg.shopping_cart_id,
state: ShoppingCartState::Closed,
checkout_notes: msg.checkout_notes,
},
t,
)
.await
{
log::error!("{e:?}");
if let Some(shopping_cart_id) = msg.shopping_cart_id {
if let Err(e) = shopping_cart_set_state(
ShoppingCartSetState {
id: shopping_cart_id,
state: ShoppingCartState::Closed,
checkout_notes: msg.checkout_notes,
},
t,
)
.await
{
dbg!(e);
log::error!("{e:?}");
return Err(super::Error::AccountOrder(Error::CantCreate));
};
return Err(super::Error::AccountOrder(Error::CantCreate));
};
}
Ok(order)
}
#[derive(actix::Message)]
#[rtype(result = "Result<Order>")]
pub struct UpdateAccountOrder {
pub struct UpdateOrder {
pub id: OrderId,
pub buyer_id: AccountId,
pub status: OrderStatus,
pub order_id: Option<ExtOrderId>,
}
db_async_handler!(UpdateAccountOrder, update_account_order, Order);
db_async_handler!(UpdateOrder, update_order, Order, inner_update_order);
pub(crate) async fn update_account_order(msg: UpdateAccountOrder, db: PgPool) -> Result<Order> {
pub(crate) async fn update_order(
msg: UpdateOrder,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Order> {
sqlx::query_as(
r#"
UPDATE orders
@ -150,7 +158,7 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes,
.bind(msg.buyer_id)
.bind(msg.status)
.bind(msg.order_id)
.fetch_one(&db)
.fetch_one(t)
.await
.map_err(|e| {
log::error!("{e:?}");
@ -160,16 +168,21 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes,
#[derive(actix::Message)]
#[rtype(result = "Result<Order>")]
pub struct UpdateAccountOrderByExt {
pub struct UpdateOrderByExt {
pub order_ext_id: String,
pub status: OrderStatus,
}
db_async_handler!(UpdateAccountOrderByExt, update_account_order_by_ext, Order);
db_async_handler!(
UpdateOrderByExt,
update_order_by_ext,
Order,
inner_update_order_by_ext
);
pub(crate) async fn update_account_order_by_ext(
msg: UpdateAccountOrderByExt,
db: PgPool,
pub(crate) async fn update_order_by_ext(
msg: UpdateOrderByExt,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Order> {
sqlx::query_as(
r#"
@ -181,7 +194,7 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes,
)
.bind(msg.order_ext_id)
.bind(msg.status)
.fetch_one(&db)
.fetch_one(t)
.await
.map_err(|e| {
log::error!("{e:?}");
@ -191,13 +204,21 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes,
#[derive(actix::Message)]
#[rtype(result = "Result<Order>")]
pub struct FindAccountOrder {
pub struct FindOrder {
pub id: OrderId,
}
db_async_handler!(FindAccountOrder, find_account_order, Order);
db_async_handler!(
FindOrder,
find_account_order,
Order,
inner_find_account_order
);
pub(crate) async fn find_account_order(msg: FindAccountOrder, db: PgPool) -> Result<Order> {
pub(crate) async fn find_account_order(
msg: FindOrder,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Order> {
sqlx::query_as(
r#"
SELECT id, buyer_id, status, order_ext_id, service_order_id, checkout_notes, address_id
@ -206,7 +227,7 @@ WHERE id = $1
"#,
)
.bind(msg.id)
.fetch_one(&db)
.fetch_one(t)
.await
.map_err(|e| {
log::error!("{e:?}");
@ -221,9 +242,17 @@ pub struct SetOrderServiceId {
pub service_order_id: String,
}
db_async_handler!(SetOrderServiceId, set_order_service_id, Order);
db_async_handler!(
SetOrderServiceId,
set_order_service_id,
Order,
inner_set_order_service_id
);
pub(crate) async fn set_order_service_id(msg: SetOrderServiceId, db: PgPool) -> Result<Order> {
pub(crate) async fn set_order_service_id(
msg: SetOrderServiceId,
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> Result<Order> {
sqlx::query_as(
r#"
UPDATE orders
@ -234,10 +263,252 @@ RETURNING id, buyer_id, status, order_ext_id, service_order_id, checkout_notes,
)
.bind(msg.id)
.bind(msg.service_order_id)
.fetch_one(&db)
.fetch_one(t)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::AccountOrder(Error::NotExists)
})
}
#[cfg(test)]
mod tests {
use config::UpdateConfig;
use fake::Fake;
use model::*;
use uuid::Uuid;
pub struct NoOpts;
impl UpdateConfig for NoOpts {}
use crate::*;
async fn test_order_address(
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
) -> model::OrderAddress {
create_order_address(
CreateOrderAddress {
name: Default::default(),
email: Default::default(),
street: Default::default(),
city: Default::default(),
country: Default::default(),
zip: Default::default(),
},
t,
)
.await
.unwrap()
}
async fn test_product(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Product {
create_product(
CreateProduct {
name: ProductName::new(format!("{}", Uuid::new_v4())),
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
category: None,
price: Price::from_u32(4687),
deliver_days_flag: Days(vec![Day::Friday, Day::Sunday]),
},
t,
)
.await
.unwrap()
}
async fn test_shopping_cart(
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
buyer_id: Option<AccountId>,
state: ShoppingCartState,
) -> ShoppingCart {
let buyer_id = match buyer_id {
Some(id) => id,
_ => test_account(&mut *t).await.id,
};
sqlx::query(
r#"
UPDATE shopping_carts
SET state = 'closed'
WHERE buyer_id = $1
"#,
)
.bind(buyer_id)
.execute(&mut *t)
.await
.unwrap();
let cart = create_shopping_cart(
CreateShoppingCart {
buyer_id,
payment_method: PaymentMethod::PaymentOnTheSpot,
},
&mut *t,
)
.await
.unwrap();
update_shopping_cart(
UpdateShoppingCart {
id: cart.id,
buyer_id: cart.buyer_id,
payment_method: cart.payment_method,
state,
checkout_notes: None,
},
&mut *t,
)
.await
.unwrap()
}
async fn test_account(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> 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();
create_account(
CreateAccount {
email: Email::new(email),
login: Login::new(login),
pass_hash: PassHash::new(hash),
role: Role::Admin,
},
t,
)
.await
.unwrap()
}
async fn test_empty_order_without_cart(
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
buyer_id: Option<AccountId>,
address_id: OrderAddressId,
) -> Order {
let buyer_id = match buyer_id {
Some(id) => id,
_ => test_account(t).await.id,
};
super::create_order(
CreateOrder {
buyer_id,
items: vec![],
shopping_cart_id: None,
checkout_notes: None,
delivery_address_id: address_id,
},
t,
)
.await
.unwrap()
}
#[actix::test]
async fn empty_order_without_cart() {
testx::db_t!(t);
let address_id = test_order_address(&mut t).await.id;
test_empty_order_without_cart(&mut t, None, address_id).await;
testx::db_rollback!(t);
}
#[actix::test]
async fn empty_order_with_cart() {
testx::db_t!(t);
let buyer_id = test_account(&mut t).await.id;
let address_id = test_order_address(&mut t).await.id;
let cart_id = test_shopping_cart(&mut t, Some(buyer_id), ShoppingCartState::Active)
.await
.id;
super::create_order(
CreateOrder {
buyer_id,
items: vec![],
shopping_cart_id: Some(cart_id),
checkout_notes: None,
delivery_address_id: address_id,
},
&mut t,
)
.await
.unwrap();
testx::db_rollback!(t);
}
#[actix::test]
async fn non_empty_order_with_cart() {
testx::db_t!(t);
let buyer_id = test_account(&mut t).await.id;
let address_id = test_order_address(&mut t).await.id;
let cart_id = test_shopping_cart(&mut t, Some(buyer_id), ShoppingCartState::Active)
.await
.id;
super::create_order(
CreateOrder {
buyer_id,
items: vec![
create_order::OrderItem {
product_id: test_product(&mut t).await.id,
quantity: Default::default(),
quantity_unit: QuantityUnit::Gram,
},
create_order::OrderItem {
product_id: test_product(&mut t).await.id,
quantity: Default::default(),
quantity_unit: QuantityUnit::Gram,
},
],
shopping_cart_id: Some(cart_id),
checkout_notes: None,
delivery_address_id: address_id,
},
&mut t,
)
.await
.unwrap();
testx::db_rollback!(t);
}
#[actix::test]
async fn non_empty_order_without_cart() {
testx::db_t!(t);
let buyer_id = test_account(&mut t).await.id;
let address_id = test_order_address(&mut t).await.id;
super::create_order(
CreateOrder {
buyer_id,
items: vec![
create_order::OrderItem {
product_id: test_product(&mut t).await.id,
quantity: Default::default(),
quantity_unit: QuantityUnit::Gram,
},
create_order::OrderItem {
product_id: test_product(&mut t).await.id,
quantity: Default::default(),
quantity_unit: QuantityUnit::Gram,
},
],
shopping_cart_id: None,
checkout_notes: None,
delivery_address_id: address_id,
},
&mut t,
)
.await
.unwrap();
testx::db_rollback!(t);
}
}

View File

@ -163,7 +163,7 @@ pub(crate) async fn create_account_order(
}
};
query_db!(
let address: model::OrderAddress = query_db!(
db,
database_manager::CreateOrderAddress {
name: address.name,
@ -178,8 +178,8 @@ pub(crate) async fn create_account_order(
let order = query_db!(
db,
database_manager::CreateAccountOrder {
shopping_cart_id: cart.id,
database_manager::CreateOrder {
shopping_cart_id: Some(cart.id),
buyer_id: msg.account_id,
items: items
.into_iter()
@ -189,7 +189,8 @@ pub(crate) async fn create_account_order(
quantity_unit: item.quantity_unit,
})
.collect(),
checkout_notes: cart.checkout_notes
checkout_notes: cart.checkout_notes,
delivery_address_id: address.id,
},
Error::CreateAccountOrder,
Error::DatabaseInternal

View File

@ -215,9 +215,22 @@ pub(crate) async fn request_payment(
Error::UnavailableShoppingCart
);
let address: model::OrderAddress = query_db!(
db,
database_manager::CreateOrderAddress {
name: address.name,
email: address.email,
street: address.street,
city: address.city,
country: address.country,
zip: address.zip,
},
Error::InvalidOrderAddress
);
let db_order: model::Order = query_db!(
db,
database_manager::CreateAccountOrder {
database_manager::CreateOrder {
buyer_id: msg.buyer_id,
items: cart_products
.iter()
@ -236,7 +249,7 @@ pub(crate) async fn request_payment(
}
})
.collect(),
shopping_cart_id: cart.id,
shopping_cart_id: Some(cart.id),
checkout_notes: cart.checkout_notes,
},
Error::CreateOrder
@ -345,7 +358,7 @@ pub(crate) async fn update_payment(
pay_u::PaymentStatus::Canceled => OrderStatus::Cancelled,
};
let _ = db
.send(database_manager::UpdateAccountOrderByExt {
.send(database_manager::UpdateOrderByExt {
status,
order_ext_id,
})

View File

@ -17,7 +17,7 @@ async fn orders(
) -> routes::Result<Json<Orders>> {
credentials.require_admin(tm.into_inner()).await?;
let orders: Vec<model::Order> = admin_send_db!(&db, database_manager::AllAccountOrders);
let orders: Vec<model::Order> = admin_send_db!(&db, database_manager::AllOrders);
let items: Vec<model::OrderItem> = admin_send_db!(db, database_manager::AllOrderItems);
Ok(Json((orders, items).into()))

View File

@ -117,7 +117,6 @@ impl From<(Vec<crate::Order>, Vec<crate::OrderItem>)> for Orders {
id,
buyer_id,
status,
order_id,
order_ext_id: _,
service_order_id: _,
checkout_notes,
@ -127,7 +126,6 @@ impl From<(Vec<crate::Order>, Vec<crate::OrderItem>)> for Orders {
id,
buyer_id,
status,
order_id,
items: items.drain_filter(|item| item.order_id == id).collect(),
checkout_notes,
address_id,
@ -146,7 +144,6 @@ impl From<(crate::Order, Vec<crate::OrderItem>)> for Order {
id,
buyer_id,
status,
order_id,
order_ext_id: _,
service_order_id: _,
checkout_notes,
@ -159,7 +156,6 @@ impl From<(crate::Order, Vec<crate::OrderItem>)> for Order {
id,
buyer_id,
status,
order_id,
items: items.drain_filter(|item| item.order_id == id).collect(),
checkout_notes,
address_id,
@ -173,7 +169,6 @@ pub struct Order {
pub id: crate::OrderId,
pub buyer_id: crate::AccountId,
pub status: crate::OrderStatus,
pub order_id: Option<crate::ExtOrderId>,
pub items: Vec<crate::OrderItem>,
pub checkout_notes: Option<String>,
pub address_id: OrderAddressId,

View File

@ -908,7 +908,6 @@ pub struct Order {
pub id: OrderId,
pub buyer_id: AccountId,
pub status: OrderStatus,
pub order_id: Option<ExtOrderId>,
pub order_ext_id: uuid::Uuid,
pub service_order_id: Option<String>,
pub checkout_notes: Option<String>,
@ -918,22 +917,20 @@ pub struct Order {
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
#[cfg_attr(feature = "db", derive(sqlx::FromRow))]
#[derive(Serialize, Deserialize)]
pub struct PublicAccountOrder {
pub struct PublicOrder {
pub id: OrderId,
pub buyer_id: AccountId,
pub status: OrderStatus,
pub order_id: Option<ExtOrderId>,
pub checkout_notes: String,
pub address_id: OrderAddressId,
}
impl From<Order> for PublicAccountOrder {
impl From<Order> for PublicOrder {
fn from(
Order {
id,
buyer_id,
status,
order_id,
order_ext_id: _,
service_order_id: _,
checkout_notes,
@ -944,7 +941,6 @@ impl From<Order> for PublicAccountOrder {
id,
buyer_id,
status,
order_id,
checkout_notes: checkout_notes.unwrap_or_default(),
address_id,
}