203 lines
6.5 KiB
Rust
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)
|
|
)
|
|
}
|
|
}
|