vanikam/src/inventory/application/services/add_customization_service.rs
Aravinth Manivannan d43d8683e9
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/pr/woodpecker Pipeline was successful
ci/woodpecker/pull_request_closed/woodpecker Pipeline was successful
feat: define and impl add customization user case
2024-07-16 15:56:22 +05:30

203 lines
6.5 KiB
Rust

// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// 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::{
customization_id_exists::{self, *},
customization_name_exists_for_product::*,
product_id_exists::{self, *},
product_name_exists_for_category::*,
},
domain::{
add_customization_command::AddCustomizationCommand,
customization_added_event::{self, *},
customization_aggregate::*,
product_added_event::{self, ProductAddedEvent, ProductAddedEventBuilder},
product_aggregate::*,
},
};
use crate::utils::uuid::*;
#[automock]
#[async_trait::async_trait]
pub trait AddCustomizationUseCase: Send + Sync {
async fn add_customization(
&self,
cmd: AddCustomizationCommand,
) -> InventoryResult<CustomizationAddedEvent>;
}
pub type AddCustomizationServiceObj = Arc<dyn AddCustomizationUseCase>;
#[derive(Clone, Builder)]
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]
impl AddCustomizationUseCase for AddCustomizationService {
async fn add_customization(
&self,
cmd: AddCustomizationCommand,
) -> InventoryResult<CustomizationAddedEvent> {
if !self
.db_product_id_exists
.product_id_exists(cmd.product_id())
.await?
{
return Err(InventoryError::ProductIDNotFound);
}
let mut customization_id = self.get_uuid.get_uuid();
loop {
if self
.db_customization_id_exists
.customization_id_exists(&customization_id)
.await?
{
customization_id = self.get_uuid.get_uuid();
continue;
} else {
break;
}
}
let customization = CustomizationBuilder::default()
.name(cmd.name().into())
.deleted(false)
.customization_id(customization_id)
.build()
.unwrap();
if self
.db_customization_name_exists_for_product
.customization_name_exists_for_product(&customization, cmd.product_id())
.await?
{
return Err(InventoryError::DuplicateCustomizationName);
}
Ok(CustomizationAddedEventBuilder::default()
.customization(customization)
.product_id(cmd.product_id().clone())
.build()
.unwrap())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use uuid::Uuid;
use crate::inventory::domain::add_customization_command::tests::get_command;
use crate::utils::uuid::tests::UUID;
use crate::{tests::bdd::*, utils::uuid::tests::mock_get_uuid};
pub fn mock_add_customization_service(
times: Option<usize>,
cmd: AddCustomizationCommand,
) -> AddCustomizationServiceObj {
let mut m = MockAddCustomizationUseCase::new();
let customization = CustomizationBuilder::default()
.name(cmd.name().into())
.deleted(false)
.customization_id(UUID.clone())
.build()
.unwrap();
let res = CustomizationAddedEventBuilder::default()
.customization(customization)
.product_id(cmd.product_id().clone())
.build()
.unwrap();
if let Some(times) = times {
m.expect_add_customization()
.times(times)
.returning(move |_| Ok(res.clone()));
} else {
m.expect_add_customization()
.returning(move |_| Ok(res.clone()));
}
Arc::new(m)
}
#[actix_rt::test]
async fn test_service_product_doesnt_exist() {
let cmd = get_command();
let s = AddCustomizationServiceBuilder::default()
.db_product_id_exists(mock_product_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
.db_customization_id_exists(mock_customization_id_exists_db_port_false(
IS_CALLED_ONLY_ONCE,
))
.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!(res.product_id(), cmd.product_id());
// assert_eq!(customization_added_events.len(), cmd.customizations().len());
}
#[actix_rt::test]
async fn test_service_product_name_exists_for_store() {
let cmd = get_command();
let s = AddCustomizationServiceBuilder::default()
.db_product_id_exists(mock_product_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
.db_customization_id_exists(mock_customization_id_exists_db_port_false(
IS_CALLED_ONLY_ONCE,
))
.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();
assert_eq!(
s.add_customization(cmd.clone()).await,
Err(InventoryError::DuplicateCustomizationName)
)
}
#[actix_rt::test]
async fn test_service_product_id_not_found() {
let cmd = get_command();
let s = AddCustomizationServiceBuilder::default()
.db_product_id_exists(mock_product_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
.db_customization_id_exists(mock_customization_id_exists_db_port_true(IS_NEVER_CALLED))
.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();
assert_eq!(
s.add_customization(cmd.clone()).await,
Err(InventoryError::ProductIDNotFound)
)
}
}