// SPDX-FileCopyrightText: 2024 Aravinth Manivannan // // SPDX-License-Identifier: AGPL-3.0-or-later use std::sync::Arc; use derive_builder::Builder; use mockall::predicate::*; use mockall::*; use super::errors::*; use crate::inventory::{ application::port::output::db::{store_id_exists::*, store_name_exists::*}, domain::{ store_aggregate::*, store_updated_event::*, update_store_command::UpdateStoreCommand, }, }; use crate::utils::uuid::*; #[automock] #[async_trait::async_trait] pub trait UpdateStoreUseCase: Send + Sync { async fn update_store(&self, cmd: UpdateStoreCommand) -> InventoryResult; } pub type UpdateStoreServiceObj = Arc; #[derive(Clone, Builder)] pub struct UpdateStoreService { db_store_id_exists: StoreIDExistsDBPortObj, db_store_name_exists: StoreNameExistsDBPortObj, } #[async_trait::async_trait] impl UpdateStoreUseCase for UpdateStoreService { async fn update_store(&self, cmd: UpdateStoreCommand) -> InventoryResult { if !self .db_store_id_exists .store_id_exists(cmd.old_store().store_id()) .await? { return Err(InventoryError::StoreIDNotFound); } let store = StoreBuilder::default() .name(cmd.name().into()) .address(cmd.address().as_ref().map(|s| s.to_string())) .owner(*cmd.owner()) .store_id(*cmd.old_store().store_id()) .build() .unwrap(); if cmd.name() != cmd.old_store().name() { if self.db_store_name_exists.store_name_exists(&store).await? { return Err(InventoryError::DuplicateStoreName); } } Ok(StoreUpdatedEventBuilder::default() .added_by_user(*cmd.adding_by()) .new_store(store) .old_store(cmd.old_store().clone()) .build() .unwrap()) } } #[cfg(test)] pub mod tests { use super::*; use crate::inventory::domain::store_updated_event::tests::get_store_updated_event_from_command; use crate::inventory::domain::update_store_command::tests::get_update_store_cmd; use crate::tests::bdd::*; use crate::utils::uuid::tests::*; pub fn mock_update_store_service( times: Option, cmd: UpdateStoreCommand, ) -> UpdateStoreServiceObj { let mut m = MockUpdateStoreUseCase::new(); let res = get_store_updated_event_from_command(&cmd); if let Some(times) = times { m.expect_update_store() .times(times) .returning(move |_| Ok(res.clone())); } else { m.expect_update_store().returning(move |_| Ok(res.clone())); } Arc::new(m) } #[actix_rt::test] async fn test_service() { let cmd = get_update_store_cmd(); let s = UpdateStoreServiceBuilder::default() .db_store_id_exists(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) .db_store_name_exists(mock_store_name_exists_db_port_false(IS_CALLED_ONLY_ONCE)) .build() .unwrap(); let res = s.update_store(cmd.clone()).await.unwrap(); assert_eq!(res.new_store().name(), cmd.name()); assert_eq!(res.new_store().address(), cmd.address()); assert_eq!(res.new_store().owner(), cmd.owner()); assert_eq!(res.new_store().store_id(), cmd.old_store().store_id()); assert_eq!(res.old_store(), cmd.old_store()); assert_eq!(res.added_by_user(), cmd.adding_by()); } #[actix_rt::test] async fn test_service_store_name_exists() { let cmd = get_update_store_cmd(); let s = UpdateStoreServiceBuilder::default() .db_store_id_exists(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE)) .db_store_name_exists(mock_store_name_exists_db_port_true(IS_CALLED_ONLY_ONCE)) .build() .unwrap(); assert_eq!( s.update_store(cmd.clone()).await, Err(InventoryError::DuplicateStoreName) ); } #[actix_rt::test] async fn test_service_store_id_doesnt_exist() { let cmd = get_update_store_cmd(); let s = UpdateStoreServiceBuilder::default() .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_false(IS_NEVER_CALLED)) .build() .unwrap(); assert_eq!( s.update_store(cmd.clone()).await, Err(InventoryError::StoreIDNotFound) ); } }