diff --git a/src/inventory/adapters/output/db/postgres/store_id_exists.rs b/src/inventory/adapters/output/db/postgres/store_id_exists.rs index 64cb992..8cdcc06 100644 --- a/src/inventory/adapters/output/db/postgres/store_id_exists.rs +++ b/src/inventory/adapters/output/db/postgres/store_id_exists.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 Aravinth Manivannan // // SPDX-License-Identifier: AGPL-3.0-or-later +use uuid::Uuid; use super::InventoryDBPostgresAdapter; use crate::inventory::application::port::output::db::{errors::*, store_id_exists::*}; @@ -8,7 +9,7 @@ use crate::inventory::domain::store_aggregate::*; #[async_trait::async_trait] impl StoreIDExistsDBPort for InventoryDBPostgresAdapter { - async fn store_id_exists(&self, s: &Store) -> InventoryDBResult { + async fn store_id_exists(&self, store_id: &Uuid) -> InventoryDBResult { let res = sqlx::query!( "SELECT EXISTS ( SELECT 1 @@ -16,7 +17,7 @@ impl StoreIDExistsDBPort for InventoryDBPostgresAdapter { WHERE store_id = $1 );", - s.store_id(), + store_id ) .fetch_one(&self.pool) .await?; @@ -73,12 +74,12 @@ pub mod tests { .unwrap(); // state doesn't exist - assert!(!db.store_id_exists(&store).await.unwrap()); + assert!(!db.store_id_exists(store.store_id()).await.unwrap()); create_dummy_store_record(&store, &db).await; // state exists - assert!(db.store_id_exists(&store).await.unwrap()); + assert!(db.store_id_exists(store.store_id()).await.unwrap()); settings.drop_db().await; } diff --git a/src/inventory/application/port/output/db/store_id_exists.rs b/src/inventory/application/port/output/db/store_id_exists.rs index a1321af..1c8733e 100644 --- a/src/inventory/application/port/output/db/store_id_exists.rs +++ b/src/inventory/application/port/output/db/store_id_exists.rs @@ -4,6 +4,7 @@ use mockall::predicate::*; use mockall::*; +use uuid::Uuid; use crate::inventory::domain::store_aggregate::Store; @@ -15,7 +16,7 @@ pub use tests::*; #[automock] #[async_trait::async_trait] pub trait StoreIDExistsDBPort: Send + Sync { - async fn store_id_exists(&self, s: &Store) -> InventoryDBResult; + async fn store_id_exists(&self, store_id: &Uuid) -> InventoryDBResult; } pub type StoreIDExistsDBPortObj = std::sync::Arc; diff --git a/src/inventory/application/services/add_category_service.rs b/src/inventory/application/services/add_category_service.rs index 4fe8c78..8e16448 100644 --- a/src/inventory/application/services/add_category_service.rs +++ b/src/inventory/application/services/add_category_service.rs @@ -10,7 +10,9 @@ use mockall::*; use super::errors::*; use crate::inventory::{ - application::port::output::db::{category_id_exists::*, category_name_exists_for_store::*}, + application::port::output::db::{ + category_id_exists::*, category_name_exists_for_store::*, store_id_exists::*, + }, domain::{ add_category_command::AddCategoryCommand, category_added_event::{CategoryAddedEvent, CategoryAddedEventBuilder}, @@ -29,7 +31,7 @@ pub type AddCategoryServiceObj = Arc; #[derive(Clone, Builder)] pub struct AddCategoryService { - // TODO: check if store ID exists + db_store_id_exists: StoreIDExistsDBPortObj, db_category_name_exists_for_store: CategoryNameExistsForStoreDBPortObj, db_category_id_exists: CategoryIDExistsDBPortObj, get_uuid: GetUUIDInterfaceObj, @@ -38,6 +40,14 @@ pub struct AddCategoryService { #[async_trait::async_trait] impl AddCategoryUseCase for AddCategoryService { async fn add_category(&self, cmd: AddCategoryCommand) -> InventoryResult { + if !self + .db_store_id_exists + .store_id_exists(cmd.store_id()) + .await? + { + return Err(InventoryError::StoreIDNotFound); + } + let mut category_id = self.get_uuid.get_uuid(); loop { @@ -128,6 +138,7 @@ pub mod tests { .unwrap(); let s = AddCategoryServiceBuilder::default() + .db_store_id_exists(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) .db_category_name_exists_for_store(mock_category_name_exists_for_store_db_port_false( IS_CALLED_ONLY_ONCE, )) @@ -156,6 +167,7 @@ pub mod tests { .unwrap(); let s = AddCategoryServiceBuilder::default() + .db_store_id_exists(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) .db_category_name_exists_for_store(mock_category_name_exists_for_store_db_port_true( IS_CALLED_ONLY_ONCE, )) @@ -169,4 +181,31 @@ pub mod tests { Err(InventoryError::DuplicateCategoryName) ) } + + #[actix_rt::test] + async fn test_service_store_doesnt_exist() { + let name = "foo"; + let description = "bar"; + let user_id = UUID; + let store_id = Uuid::new_v4(); + + // description = None + let cmd = AddCategoryCommand::new(name.into(), Some(description.into()), store_id, user_id) + .unwrap(); + + let s = AddCategoryServiceBuilder::default() + .db_store_id_exists(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) + .db_category_name_exists_for_store(mock_category_name_exists_for_store_db_port_false( + IS_NEVER_CALLED, + )) + .db_category_id_exists(mock_category_id_exists_db_port_false(IS_NEVER_CALLED)) + .get_uuid(mock_get_uuid(IS_NEVER_CALLED)) + .build() + .unwrap(); + + assert_eq!( + s.add_category(cmd.clone()).await, + Err(InventoryError::StoreIDNotFound) + ) + } } diff --git a/src/inventory/application/services/add_store_service.rs b/src/inventory/application/services/add_store_service.rs index 75d94d8..30d6912 100644 --- a/src/inventory/application/services/add_store_service.rs +++ b/src/inventory/application/services/add_store_service.rs @@ -38,7 +38,17 @@ pub struct AddStoreService { impl AddStoreUseCase for AddStoreService { async fn add_store(&self, cmd: AddStoreCommand) -> InventoryResult { let mut store_id = self.get_uuid.get_uuid(); - let mut store = StoreBuilder::default() + + loop { + if self.db_store_id_exists.store_id_exists(&store_id).await? { + store_id = self.get_uuid.get_uuid(); + continue; + } else { + break; + } + } + + let store = StoreBuilder::default() .name(cmd.name().into()) .address(cmd.address().as_ref().map(|s| s.to_string())) .owner(*cmd.owner()) @@ -50,22 +60,6 @@ impl AddStoreUseCase for AddStoreService { return Err(InventoryError::DuplicateStoreName); } - loop { - if self.db_store_id_exists.store_id_exists(&store).await? { - store_id = self.get_uuid.get_uuid(); - store = StoreBuilder::default() - .name(cmd.name().into()) - .address(cmd.address().as_ref().map(|s| s.to_string())) - .owner(*cmd.owner()) - .store_id(store_id) - .build() - .unwrap(); - continue; - } else { - break; - } - } - Ok(StoreAddedEventBuilder::default() .name(store.name().into()) .address(store.address().as_ref().map(|s| s.to_string())) @@ -141,7 +135,7 @@ pub mod tests { let cmd = AddStoreCommand::new(name.into(), Some(address.into()), owner).unwrap(); let s = AddStoreServiceBuilder::default() - .db_store_id_exists(mock_store_id_exists_db_port_false(IS_NEVER_CALLED)) + .db_store_id_exists(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE)) .db_store_name_exists(mock_store_name_exists_db_port_true(IS_CALLED_ONLY_ONCE)) .get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE)) .build()