From 5a050fde0e39e096fa8d2e71518ced5856902337 Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Tue, 24 Sep 2024 12:31:39 +0530 Subject: [PATCH] feat: ordering: customization ID is provided by caller & customization view tests --- ...cfab30cc8b01fed9b73b9cc0813750230314.json} | 5 +- .../adapters/output/db/customization_view.rs | 154 +++++++++++++++++- .../adapters/output/db/product_id_exists.rs | 4 +- .../services/add_customization_service.rs | 41 ++--- .../domain/add_customization_command.rs | 56 +++---- 5 files changed, 187 insertions(+), 73 deletions(-) rename .sqlx/{query-d9c625876e7d398cb48c6278e69b2eb6ad8515e68d5520013634415109309e6e.json => query-a5a58d14ddbfa78cca3729392faecfab30cc8b01fed9b73b9cc0813750230314.json} (64%) diff --git a/.sqlx/query-d9c625876e7d398cb48c6278e69b2eb6ad8515e68d5520013634415109309e6e.json b/.sqlx/query-a5a58d14ddbfa78cca3729392faecfab30cc8b01fed9b73b9cc0813750230314.json similarity index 64% rename from .sqlx/query-d9c625876e7d398cb48c6278e69b2eb6ad8515e68d5520013634415109309e6e.json rename to .sqlx/query-a5a58d14ddbfa78cca3729392faecfab30cc8b01fed9b73b9cc0813750230314.json index 1cff257..6fe3c5f 100644 --- a/.sqlx/query-d9c625876e7d398cb48c6278e69b2eb6ad8515e68d5520013634415109309e6e.json +++ b/.sqlx/query-a5a58d14ddbfa78cca3729392faecfab30cc8b01fed9b73b9cc0813750230314.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE\n cqrs_ordering_product_customizations_query\n SET\n version = $1,\n name = $2,\n customization_id = $3,\n product_id = $4,\n deleted = $5;", + "query": "UPDATE\n cqrs_ordering_product_customizations_query\n SET\n version = $1,\n name = $2,\n product_id = $3,\n deleted = $4;", "describe": { "columns": [], "parameters": { @@ -8,11 +8,10 @@ "Int8", "Text", "Uuid", - "Uuid", "Bool" ] }, "nullable": [] }, - "hash": "d9c625876e7d398cb48c6278e69b2eb6ad8515e68d5520013634415109309e6e" + "hash": "a5a58d14ddbfa78cca3729392faecfab30cc8b01fed9b73b9cc0813750230314" } diff --git a/src/ordering/adapters/output/db/customization_view.rs b/src/ordering/adapters/output/db/customization_view.rs index d43a4d3..87a5c36 100644 --- a/src/ordering/adapters/output/db/customization_view.rs +++ b/src/ordering/adapters/output/db/customization_view.rs @@ -17,11 +17,6 @@ use crate::utils::parse_aggregate_id::parse_aggregate_id; pub const NEW_CUSTOMIZATION_NON_UUID: &str = "ordering_new_customization_non_uuid-asdfa"; -//#[derive(Debug, Default, Serialize, Deserialize)] -//struct Customizations { -// customizations: Vec, -//} - #[derive(Debug, Default, Serialize, Deserialize)] struct CustomizationView { name: String, @@ -187,12 +182,10 @@ impl ViewRepository for OrderingDBPostgresAdap SET version = $1, name = $2, - customization_id = $3, - product_id = $4, - deleted = $5;", + product_id = $3, + deleted = $4;", version, view.name, - view.customization_id, view.product_id, view.deleted, ) @@ -225,3 +218,146 @@ impl Query for OrderingDBPostgresAdapter { self.update_view(view, view_context).await.unwrap(); } } + + +#[cfg(test)] +mod tests { + use super::*; + + use postgres_es::PostgresCqrs; + + use crate::{ + db::migrate::*, + ordering::{ + adapters::output::db::product_id_exists::tests::create_dummy_product_record, + application::services::{ + add_customization_service::*, update_customization_service::*, + MockOrderingServicesInterface, + }, + domain::{ + add_customization_command::*, + commands::OrderingCommand, + product_aggregate::Product, + update_customization_command::{tests::get_update_customization_command, *}, + }, + }, + tests::bdd::*, + utils::{random_string::GenerateRandomStringInterface, uuid::tests::UUID}, + }; + use std::sync::Arc; + + #[actix_rt::test] + async fn pg_query_ordering_customization_view() { + let settings = crate::settings::tests::get_settings().await; + //let settings = crate::settings::Settings::new().unwrap(); + settings.create_db().await; + + let db = crate::db::sqlx_postgres::Postgres::init(&settings.database.url).await; + db.migrate().await; + let db = OrderingDBPostgresAdapter::new(db.pool.clone()); + let product = Product::default(); + create_dummy_product_record(&product, &db).await; + + // let simple_query = super::store_view::SimpleLoggingQuery {}; + + let queries: Vec>> = vec![Box::new(db.clone())]; + + let mut mock_services = MockOrderingServicesInterface::new(); + + let db2 = db.clone(); + mock_services + .expect_add_customization() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .returning(move || { + Arc::new( + AddCustomizationServiceBuilder::default() + .db_product_id_exists(Arc::new(db2.clone())) + .db_customization_id_exists(Arc::new(db2.clone())) + .db_customization_name_exists_for_product(Arc::new(db2.clone())) + .build() + .unwrap(), + ) + }); + + let db2 = Arc::new(db.clone()); + mock_services + .expect_update_customization() + .times(IS_CALLED_ONLY_ONCE.unwrap()) + .returning(move || { + Arc::new( + UpdateCustomizationServiceBuilder::default() + .db_product_id_exists(db2.clone()) + .db_customization_name_exists_for_product(db2.clone()) + .db_customization_id_exists(db2.clone()) + .build() + .unwrap(), + ) + }); + + let (cqrs, custmoization_query): ( + Arc>, + Arc>, + ) = ( + Arc::new(postgres_es::postgres_cqrs( + db.pool.clone(), + queries, + Arc::new(mock_services), + )), + Arc::new(db.clone()), + ); + + let rand = crate::utils::random_string::GenerateRandomString {}; + + let cmd = AddCustomizationCommandBuilder::default() + .name(rand.get_random(10)) + .product_id(product.product_id().clone()) + .customization_id(UUID.clone()) + .build() + .unwrap(); + + cqrs.execute( + &cmd.customization_id().to_string(), + OrderingCommand::AddCustomization(cmd.clone()), + ) + .await + .unwrap(); + let customization = custmoization_query + .load(&(*cmd.customization_id()).to_string()) + .await + .unwrap() + .unwrap(); + let customization: Customization = customization.into(); + assert_eq!(customization.name(), cmd.name()); + assert_eq!(customization.customization_id(), cmd.customization_id()); + assert_eq!(customization.product_id(), cmd.product_id()); + assert!(!customization.deleted()); + + let update_customization_command = UnvalidatedUpdateCustomizationCommandBuilder::default() + .name(rand.get_random(10)) + .old_customization(customization.clone()) + .adding_by(UUID.clone()) + .build() + .unwrap() + .validate() + .unwrap(); + cqrs.execute( + &cmd.customization_id().to_string(), + OrderingCommand::UpdateCustomization(update_customization_command.clone()), + ) + .await + .unwrap(); + let c = custmoization_query + .load(&(*cmd.customization_id()).to_string()) + .await + .unwrap() + .unwrap(); + let c: Customization = c.into(); + assert_eq!(c.name(), update_customization_command.name()); + assert_eq!( + update_customization_command.old_customization(), + &customization + ); + assert!(!c.deleted()); + settings.drop_db().await; + } +} diff --git a/src/ordering/adapters/output/db/product_id_exists.rs b/src/ordering/adapters/output/db/product_id_exists.rs index 7904a9a..40d1f6b 100644 --- a/src/ordering/adapters/output/db/product_id_exists.rs +++ b/src/ordering/adapters/output/db/product_id_exists.rs @@ -95,8 +95,8 @@ pub mod tests { );", 1, p.name(), - p.description().as_ref().unwrap(), - p.image().as_ref().unwrap(), + p.description().as_ref().map(|s| s.as_str()), + p.image().as_ref().map(|s| s.as_str()), p.product_id(), p.category_id(), p.price().major().clone() as i32, diff --git a/src/ordering/application/services/add_customization_service.rs b/src/ordering/application/services/add_customization_service.rs index 7e5146c..0b52d68 100644 --- a/src/ordering/application/services/add_customization_service.rs +++ b/src/ordering/application/services/add_customization_service.rs @@ -11,20 +11,16 @@ use mockall::*; use super::errors::*; use crate::ordering::{ application::port::output::db::{ - customization_id_exists::{self, *}, + customization_id_exists::*, customization_name_exists_for_product::*, - product_id_exists::{self, *}, - product_name_exists_for_category::*, + product_id_exists::*, }, domain::{ - add_customization_command::AddCustomizationCommand, - customization_added_event::{self, *}, + add_customization_command::*, + customization_added_event::*, customization_aggregate::*, - product_added_event::{self, ProductAddedEvent, ProductAddedEventBuilder}, - product_aggregate::*, }, }; -use crate::utils::uuid::*; #[automock] #[async_trait::async_trait] @@ -42,7 +38,6 @@ pub struct AddCustomizationService { db_product_id_exists: ProductIDExistsDBPortObj, db_customization_id_exists: CustomizationIDExistsDBPortObj, db_customization_name_exists_for_product: CustomizationNameExistsForProductDBPortObj, - get_uuid: GetUUIDInterfaceObj, } #[async_trait::async_trait] @@ -59,25 +54,19 @@ impl AddCustomizationUseCase for AddCustomizationService { return Err(OrderingError::ProductIDNotFound); } - let mut customization_id = self.get_uuid.get_uuid(); - loop { if self .db_customization_id_exists - .customization_id_exists(&customization_id) + .customization_id_exists(cmd.customization_id()) .await? { - customization_id = self.get_uuid.get_uuid(); - continue; - } else { - break; + return Err(OrderingError::DuplicateCustomizationID); } - } let customization = CustomizationBuilder::default() .name(cmd.name().into()) .deleted(false) .product_id(*cmd.product_id()) - .customization_id(customization_id) + .customization_id(*cmd.customization_id()) .build() .unwrap(); @@ -100,12 +89,11 @@ impl AddCustomizationUseCase for AddCustomizationService { pub mod tests { use super::*; - use customization_added_event::tests::get_customization_added_event_from_cmd; - use uuid::Uuid; - - use crate::ordering::domain::add_customization_command::tests::get_command; - use crate::utils::uuid::tests::UUID; - use crate::{tests::bdd::*, utils::uuid::tests::mock_get_uuid}; + use crate::ordering::domain::{ + add_customization_command::tests::*, + customization_added_event::tests::*, + }; + use crate::tests::bdd::*; pub fn mock_add_customization_service( times: Option, @@ -138,13 +126,12 @@ pub mod tests { .db_customization_name_exists_for_product( mock_customization_name_exists_for_product_db_port_false(IS_CALLED_ONLY_ONCE), ) - .get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE)) .build() .unwrap(); let res = s.add_customization(cmd.clone()).await.unwrap(); assert_eq!(res.customization().name(), cmd.name()); - // assert_eq!(customization_added_events.len(), cmd.customizations().len()); + assert_eq!(res.customization().customization_id(), cmd.customization_id()); } #[actix_rt::test] @@ -159,7 +146,6 @@ pub mod tests { .db_customization_name_exists_for_product( mock_customization_name_exists_for_product_db_port_true(IS_CALLED_ONLY_ONCE), ) - .get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE)) .build() .unwrap(); @@ -179,7 +165,6 @@ pub mod tests { .db_customization_name_exists_for_product( mock_customization_name_exists_for_product_db_port_false(IS_NEVER_CALLED), ) - .get_uuid(mock_get_uuid(IS_NEVER_CALLED)) .build() .unwrap(); diff --git a/src/ordering/domain/add_customization_command.rs b/src/ordering/domain/add_customization_command.rs index c79a64e..a912070 100644 --- a/src/ordering/domain/add_customization_command.rs +++ b/src/ordering/domain/add_customization_command.rs @@ -16,28 +16,26 @@ pub enum AddCustomizationCommandError { #[derive( Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder, )] -pub struct UnvalidatedAddCustomizationCommand { - name: String, - product_id: Uuid, -} - -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters)] +#[builder(build_fn(validate = "Self::validate"))] pub struct AddCustomizationCommand { + #[builder(setter(custom))] name: String, product_id: Uuid, + customization_id: Uuid, } -impl UnvalidatedAddCustomizationCommand { - pub fn validate(self) -> Result { - let name = self.name.trim().to_owned(); - if name.is_empty() { - return Err(AddCustomizationCommandError::NameIsEmpty); - } +impl AddCustomizationCommandBuilder { + pub fn name(&mut self, name: String) -> &mut Self { + self.name = Some(name.trim().to_owned()); + self + } - Ok(AddCustomizationCommand { - name, - product_id: self.product_id, - }) + fn validate(&self) -> Result<(), String> { + let name = self.name.as_ref().unwrap().trim().to_owned(); + if name.is_empty() { + return Err(AddCustomizationCommandError::NameIsEmpty.to_string()); + } + Ok(()) } } @@ -48,13 +46,12 @@ pub mod tests { use crate::utils::uuid::tests::UUID; pub fn get_command() -> AddCustomizationCommand { - UnvalidatedAddCustomizationCommandBuilder::default() + AddCustomizationCommandBuilder::default() .name("foo".into()) .product_id(UUID.clone()) + .customization_id(UUID.clone()) .build() .unwrap() - .validate() - .unwrap() } #[test] @@ -62,12 +59,11 @@ pub mod tests { let name = "foo"; let product_id = UUID; - let cmd = UnvalidatedAddCustomizationCommandBuilder::default() + let cmd = AddCustomizationCommandBuilder::default() .name(name.into()) .product_id(product_id.clone()) + .customization_id(UUID.clone()) .build() - .unwrap() - .validate() .unwrap(); assert_eq!(cmd.name(), name); @@ -78,14 +74,12 @@ pub mod tests { fn test_cmd_name_is_empty() { let product_id = UUID; - assert_eq!( - UnvalidatedAddCustomizationCommandBuilder::default() - .name("".into()) - .product_id(product_id.clone()) - .build() - .unwrap() - .validate(), - Err(AddCustomizationCommandError::NameIsEmpty) - ); + assert!(AddCustomizationCommandBuilder::default() + .name("".into()) + .product_id(product_id.clone()) + .customization_id(UUID.clone()) + .build() + .is_err(),); } } +