diff --git a/tests/common/bakery_chain/bakery_chain_erd.drawio b/tests/common/bakery_chain/bakery_chain_erd.drawio index 0e782312..1f2b430f 100644 --- a/tests/common/bakery_chain/bakery_chain_erd.drawio +++ b/tests/common/bakery_chain/bakery_chain_erd.drawio @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/tests/common/bakery_chain/bakery_chain_erd.jpg b/tests/common/bakery_chain/bakery_chain_erd.jpg index 4d20a32c..6decffa5 100644 Binary files a/tests/common/bakery_chain/bakery_chain_erd.jpg and b/tests/common/bakery_chain/bakery_chain_erd.jpg differ diff --git a/tests/sequential_op_tests.rs b/tests/sequential_op_tests.rs new file mode 100644 index 00000000..1630e4c6 --- /dev/null +++ b/tests/sequential_op_tests.rs @@ -0,0 +1,268 @@ +use chrono::offset::Utc; +use rust_decimal::prelude::*; +use rust_decimal_macros::dec; +use sea_orm::{entity::*, query::*, DatabaseConnection, FromQueryResult}; +use uuid::Uuid; + +pub mod common; +pub use common::{bakery_chain::*, setup::*, TestContext}; + +#[async_std::test] +pub async fn test_multiple_operations() { + let ctx = TestContext::new("mysql://root:@localhost", "multiple_sequential_operations").await; + + init_setup(&ctx.db).await; + let baker_least_sales = find_baker_least_sales(&ctx.db).await.unwrap(); + assert_eq!(baker_least_sales.name, "Baker 2"); + + let new_cake = create_cake(&ctx.db, baker_least_sales).await.unwrap(); + create_order(&ctx.db, new_cake).await; + + let baker_least_sales = find_baker_least_sales(&ctx.db).await.unwrap(); + assert_eq!(baker_least_sales.name, "Baker 1"); +} + +async fn init_setup(db: &DatabaseConnection) { + let bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + } + .save(db) + .await + .expect("could not insert bakery"); + + let baker_1 = baker::ActiveModel { + name: Set("Baker 1".to_owned()), + contact_details: Set(serde_json::json!({})), + bakery_id: Set(Some(bakery.id.clone().unwrap())), + ..Default::default() + } + .save(db) + .await + .expect("could not insert baker"); + + let _baker_2 = baker::ActiveModel { + name: Set("Baker 2".to_owned()), + contact_details: Set(serde_json::json!({})), + bakery_id: Set(Some(bakery.id.clone().unwrap())), + ..Default::default() + } + .save(db) + .await + .expect("could not insert baker"); + + let mud_cake = cake::ActiveModel { + name: Set("Mud Cake".to_owned()), + price: Set(dec!(10.25)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(Some(bakery.id.clone().unwrap())), + ..Default::default() + }; + + let cake_insert_res: InsertResult = Cake::insert(mud_cake) + .exec(db) + .await + .expect("could not insert cake"); + + let cake_baker = cakes_bakers::ActiveModel { + cake_id: Set(cake_insert_res.last_insert_id as i32), + baker_id: Set(baker_1.id.clone().unwrap()), + ..Default::default() + }; + + let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + .exec(db) + .await + .expect("could not insert cake_baker"); + + let customer_kate = customer::ActiveModel { + name: Set("Kate".to_owned()), + ..Default::default() + } + .save(db) + .await + .expect("could not insert customer"); + + let kate_order_1 = order::ActiveModel { + bakery_id: Set(Some(bakery.id.clone().unwrap())), + customer_id: Set(Some(customer_kate.id.clone().unwrap())), + total: Set(dec!(99.95)), + placed_at: Set(Utc::now().naive_utc()), + + ..Default::default() + } + .save(db) + .await + .expect("could not insert order"); + + let _lineitem = lineitem::ActiveModel { + cake_id: Set(Some(cake_insert_res.last_insert_id as i32)), + price: Set(dec!(10.00)), + quantity: Set(12), + order_id: Set(Some(kate_order_1.id.clone().unwrap())), + ..Default::default() + } + .save(db) + .await + .expect("could not insert order"); + + let _lineitem2 = lineitem::ActiveModel { + cake_id: Set(Some(cake_insert_res.last_insert_id as i32)), + price: Set(dec!(50.00)), + quantity: Set(2), + order_id: Set(Some(kate_order_1.id.clone().unwrap())), + ..Default::default() + } + .save(db) + .await + .expect("could not insert order"); +} + +async fn find_baker_least_sales(db: &DatabaseConnection) -> Option { + #[derive(Debug, FromQueryResult)] + struct SelectResult { + id: i32, + cakes_sold_opt: Option, + } + + #[derive(Debug)] + struct LeastSalesBakerResult { + id: i32, + cakes_sold: Decimal, + } + + let rel: RelationDef = cakes_bakers::Entity::belongs_to(baker::Entity) + .from(cakes_bakers::Column::BakerId) + .to(baker::Column::Id) + .into(); + + let rel2: RelationDef = cakes_bakers::Entity::belongs_to(cake::Entity) + .from(cakes_bakers::Column::CakeId) + .to(cake::Column::Id) + .into(); + + let rel3: RelationDef = cake::Entity::has_many(lineitem::Entity) + .from(cake::Column::Id) + .to(lineitem::Column::CakeId) + .into(); + + let select = cakes_bakers::Entity::find() + .join(JoinType::RightJoin, rel) + .join(JoinType::LeftJoin, rel2) + .join(JoinType::LeftJoin, rel3) + .select_only() + .column(baker::Column::Id) + .column_as(lineitem::Column::Quantity.sum(), "cakes_sold_opt") + .group_by(baker::Column::Id); + + let mut results: Vec = select + .into_model::() + .all(&db) + .await + .unwrap() + .into_iter() + .map(|b| LeastSalesBakerResult { + id: b.id.clone(), + cakes_sold: b.cakes_sold_opt.unwrap_or(dec!(0)), + }) + .collect(); + + results.sort_by(|a, b| b.cakes_sold.cmp(&a.cakes_sold)); + + Baker::find_by_id(results.last().unwrap().id) + .one(db) + .await + .unwrap() +} + +async fn create_cake(db: &DatabaseConnection, baker: baker::Model) -> Option { + let new_cake = cake::ActiveModel { + name: Set("New Cake".to_owned()), + price: Set(dec!(8.00)), + gluten_free: Set(false), + serial: Set(Uuid::new_v4()), + bakery_id: Set(Some(baker.bakery_id.clone().unwrap())), + ..Default::default() + }; + + let cake_insert_res: InsertResult = Cake::insert(new_cake) + .exec(db) + .await + .expect("could not insert cake"); + + let cake_baker = cakes_bakers::ActiveModel { + cake_id: Set(cake_insert_res.last_insert_id as i32), + baker_id: Set(baker.id), + ..Default::default() + }; + + let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + .exec(db) + .await + .expect("could not insert cake_baker"); + + Cake::find_by_id(cake_insert_res.last_insert_id) + .one(db) + .await + .unwrap() +} + +async fn create_order(db: &DatabaseConnection, cake: cake::Model) { + let another_customer = customer::ActiveModel { + name: Set("John".to_owned()), + ..Default::default() + } + .save(db) + .await + .expect("could not insert customer"); + + let order = order::ActiveModel { + bakery_id: Set(Some(cake.bakery_id.unwrap())), + customer_id: Set(Some(another_customer.id.clone().unwrap())), + total: Set(dec!(200.00)), + placed_at: Set(Utc::now().naive_utc()), + + ..Default::default() + } + .save(db) + .await + .expect("could not insert order"); + + let _lineitem = lineitem::ActiveModel { + cake_id: Set(Some(cake.id)), + price: Set(dec!(10.00)), + quantity: Set(300), + order_id: Set(Some(order.id.clone().unwrap())), + ..Default::default() + } + .save(db) + .await + .expect("could not insert order"); +} + +pub async fn test_delete_bakery(db: &DatabaseConnection) { + let initial_bakeries = Bakery::find().all(db).await.unwrap().len(); + + let bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + } + .save(db) + .await + .expect("could not insert bakery"); + + assert_eq!( + Bakery::find().all(db).await.unwrap().len(), + initial_bakeries + 1 + ); + + let _result = bakery.delete(db).await.expect("failed to delete bakery"); + + assert_eq!( + Bakery::find().all(db).await.unwrap().len(), + initial_bakeries + ); +}