feat: check for Customization constraint violation #43
12 changed files with 444 additions and 12 deletions
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (\n SELECT 1\n FROM cqrs_inventory_product_customizations_query\n WHERE\n customization_id = $1\n );",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "3292856681e8d41391ba1d213b118a1f879677b14a2e6aad113b513d93b7c9a3"
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "SELECT EXISTS (\n SELECT 1\n FROM cqrs_inventory_product_customizations_query\n WHERE\n name = $1\n AND\n product_id = $2\n AND\n deleted = false\n );",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "exists",
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Uuid"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "78c1fa1b50afb781ed7c28c5454f43c61894b22bcde01097ca41ab691d985aea"
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::InventoryDBPostgresAdapter;
|
||||||
|
use crate::inventory::application::port::output::db::{customization_id_exists::*, errors::*};
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl CustomizationIDExistsDBPort for InventoryDBPostgresAdapter {
|
||||||
|
async fn customization_id_exists(&self, customization_id: &Uuid) -> InventoryDBResult<bool> {
|
||||||
|
let res = sqlx::query!(
|
||||||
|
"SELECT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM cqrs_inventory_product_customizations_query
|
||||||
|
WHERE
|
||||||
|
customization_id = $1
|
||||||
|
);",
|
||||||
|
customization_id
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await?;
|
||||||
|
if let Some(x) = res.exists {
|
||||||
|
Ok(x)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::inventory::domain::add_product_command::tests::get_customizations;
|
||||||
|
use crate::inventory::domain::product_aggregate::*;
|
||||||
|
use crate::utils::uuid::tests::UUID;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_postgres_customization_exists() {
|
||||||
|
let settings = crate::settings::tests::get_settings().await;
|
||||||
|
settings.create_db().await;
|
||||||
|
let db = super::InventoryDBPostgresAdapter::new(
|
||||||
|
sqlx::postgres::PgPool::connect(&settings.database.url)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let customization = {
|
||||||
|
let c = get_customizations().first().unwrap().clone();
|
||||||
|
CustomizationBuilder::default()
|
||||||
|
.name(c.name().into())
|
||||||
|
.customization_id(UUID)
|
||||||
|
.deleted(false)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// state doesn't exist
|
||||||
|
assert!(!db
|
||||||
|
.customization_id_exists(customization.customization_id())
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
create_dummy_customization_record(&customization, &db).await;
|
||||||
|
|
||||||
|
// state exists
|
||||||
|
assert!(db
|
||||||
|
.customization_id_exists(customization.customization_id())
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
settings.drop_db().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_dummy_customization_record(
|
||||||
|
c: &Customization,
|
||||||
|
db: &InventoryDBPostgresAdapter,
|
||||||
|
) {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO cqrs_inventory_product_customizations_query (
|
||||||
|
version,
|
||||||
|
name,
|
||||||
|
customization_id,
|
||||||
|
product_id,
|
||||||
|
deleted
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5
|
||||||
|
);",
|
||||||
|
1,
|
||||||
|
c.name(),
|
||||||
|
c.customization_id(),
|
||||||
|
UUID,
|
||||||
|
c.deleted().clone(),
|
||||||
|
)
|
||||||
|
.execute(&db.pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::InventoryDBPostgresAdapter;
|
||||||
|
use crate::inventory::application::port::output::db::{
|
||||||
|
customization_name_exists_for_product::*, errors::*,
|
||||||
|
};
|
||||||
|
use crate::inventory::domain::product_aggregate::*;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl CustomizationNameExistsForProductDBPort for InventoryDBPostgresAdapter {
|
||||||
|
async fn customization_name_exists_for_product(
|
||||||
|
&self,
|
||||||
|
c: &Customization,
|
||||||
|
product_id: &Uuid,
|
||||||
|
) -> InventoryDBResult<bool> {
|
||||||
|
let res = sqlx::query!(
|
||||||
|
"SELECT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM cqrs_inventory_product_customizations_query
|
||||||
|
WHERE
|
||||||
|
name = $1
|
||||||
|
AND
|
||||||
|
product_id = $2
|
||||||
|
AND
|
||||||
|
deleted = false
|
||||||
|
);",
|
||||||
|
c.name(),
|
||||||
|
product_id,
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await?;
|
||||||
|
if let Some(x) = res.exists {
|
||||||
|
Ok(x)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::inventory::adapters::output::db::postgres::customization_id_exists::tests::create_dummy_customization_record;
|
||||||
|
use crate::utils::uuid::tests::UUID;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_postgres_customization_exists() {
|
||||||
|
let customization_name = "foo_customization";
|
||||||
|
let product_id = UUID;
|
||||||
|
|
||||||
|
let settings = crate::settings::tests::get_settings().await;
|
||||||
|
settings.create_db().await;
|
||||||
|
let db = super::InventoryDBPostgresAdapter::new(
|
||||||
|
sqlx::postgres::PgPool::connect(&settings.database.url)
|
||||||
|
.await
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let customization = {
|
||||||
|
CustomizationBuilder::default()
|
||||||
|
.name(customization_name.into())
|
||||||
|
.customization_id(UUID)
|
||||||
|
.deleted(false)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// state doesn't exist
|
||||||
|
assert!(!db
|
||||||
|
.customization_name_exists_for_product(&customization, &product_id)
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
create_dummy_customization_record(&customization, &db).await;
|
||||||
|
|
||||||
|
// state exists
|
||||||
|
assert!(db
|
||||||
|
.customization_name_exists_for_product(&customization, &product_id)
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
// Set customization.deleted = true; now db.customization_name_exists_for_product must return false
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE
|
||||||
|
cqrs_inventory_product_customizations_query
|
||||||
|
SET
|
||||||
|
deleted = true
|
||||||
|
WHERE
|
||||||
|
customization_id = $1
|
||||||
|
AND
|
||||||
|
product_id = $2
|
||||||
|
AND
|
||||||
|
name = $3;",
|
||||||
|
customization.customization_id(),
|
||||||
|
&product_id,
|
||||||
|
customization.name()
|
||||||
|
)
|
||||||
|
.execute(&db.pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(!db
|
||||||
|
.customization_name_exists_for_product(&customization, &product_id)
|
||||||
|
.await
|
||||||
|
.unwrap());
|
||||||
|
|
||||||
|
settings.drop_db().await;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,10 @@ impl From<SqlxError> for InventoryDBError {
|
||||||
return Self::DuplicateStoreID;
|
return Self::DuplicateStoreID;
|
||||||
} else if msg.contains("cqrs_inventory_store_query_product_id_key") {
|
} else if msg.contains("cqrs_inventory_store_query_product_id_key") {
|
||||||
return Self::DuplicateProductID;
|
return Self::DuplicateProductID;
|
||||||
|
} else if msg
|
||||||
|
.contains("cqrs_inventory_product_customizations_query_customization_id_key")
|
||||||
|
{
|
||||||
|
return Self::DuplicateCustomizationID;
|
||||||
} else if msg.contains("cqrs_inventory_store_query_category_id_key") {
|
} else if msg.contains("cqrs_inventory_store_query_category_id_key") {
|
||||||
return Self::DuplicateCategoryID;
|
return Self::DuplicateCategoryID;
|
||||||
} else if msg.contains("cqrs_inventory_product_query_name_key") {
|
} else if msg.contains("cqrs_inventory_product_query_name_key") {
|
||||||
|
@ -27,6 +31,8 @@ impl From<SqlxError> for InventoryDBError {
|
||||||
return Self::DuplicateProductName;
|
return Self::DuplicateProductName;
|
||||||
} else if msg.contains("cqrs_inventory_store_query_name_key") {
|
} else if msg.contains("cqrs_inventory_store_query_name_key") {
|
||||||
return Self::DuplicateStoreName;
|
return Self::DuplicateStoreName;
|
||||||
|
} else if msg.contains("cqrs_inventory_product_customizations_query_name_key") {
|
||||||
|
return Self::DuplicateCustomizationName;
|
||||||
} else {
|
} else {
|
||||||
println!("{msg}");
|
println!("{msg}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ use crate::db::{migrate::RunMigrations, sqlx_postgres::Postgres};
|
||||||
mod category_id_exists;
|
mod category_id_exists;
|
||||||
mod category_name_exists_for_store;
|
mod category_name_exists_for_store;
|
||||||
mod category_view;
|
mod category_view;
|
||||||
|
mod customization_id_exists;
|
||||||
|
mod customization_name_exists_for_product;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod product_id_exists;
|
mod product_id_exists;
|
||||||
mod product_name_exists_for_category;
|
mod product_name_exists_for_category;
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use mockall::predicate::*;
|
||||||
|
use mockall::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::errors::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use tests::*;
|
||||||
|
|
||||||
|
#[automock]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait CustomizationIDExistsDBPort: Send + Sync {
|
||||||
|
async fn customization_id_exists(&self, c: &Uuid) -> InventoryDBResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CustomizationIDExistsDBPortObj = std::sync::Arc<dyn CustomizationIDExistsDBPort>;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub fn mock_customization_id_exists_db_port_false(
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> CustomizationIDExistsDBPortObj {
|
||||||
|
let mut m = MockCustomizationIDExistsDBPort::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_customization_id_exists()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_| Ok(false));
|
||||||
|
} else {
|
||||||
|
m.expect_customization_id_exists().returning(|_| Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mock_customization_id_exists_db_port_true(
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> CustomizationIDExistsDBPortObj {
|
||||||
|
let mut m = MockCustomizationIDExistsDBPort::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_customization_id_exists()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_| Ok(true));
|
||||||
|
} else {
|
||||||
|
m.expect_customization_id_exists().returning(|_| Ok(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use mockall::predicate::*;
|
||||||
|
use mockall::*;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::inventory::domain::product_aggregate::Customization;
|
||||||
|
|
||||||
|
use super::errors::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use tests::*;
|
||||||
|
|
||||||
|
#[automock]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait CustomizationNameExistsForProductDBPort: Send + Sync {
|
||||||
|
async fn customization_name_exists_for_product(
|
||||||
|
&self,
|
||||||
|
c: &Customization,
|
||||||
|
product_id: &Uuid,
|
||||||
|
) -> InventoryDBResult<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CustomizationNameExistsForProductDBPortObj =
|
||||||
|
std::sync::Arc<dyn CustomizationNameExistsForProductDBPort>;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub fn mock_customization_name_exists_for_product_db_port_false(
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> CustomizationNameExistsForProductDBPortObj {
|
||||||
|
let mut m = MockCustomizationNameExistsForProductDBPort::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_customization_name_exists_for_product()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_, _| Ok(false));
|
||||||
|
} else {
|
||||||
|
m.expect_customization_name_exists_for_product()
|
||||||
|
.returning(|_, _| Ok(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mock_customization_name_exists_for_product_db_port_true(
|
||||||
|
times: Option<usize>,
|
||||||
|
) -> CustomizationNameExistsForProductDBPortObj {
|
||||||
|
let mut m = MockCustomizationNameExistsForProductDBPort::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_customization_name_exists_for_product()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_, _| Ok(true));
|
||||||
|
} else {
|
||||||
|
m.expect_customization_name_exists_for_product()
|
||||||
|
.returning(|_, _| Ok(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,5 +15,7 @@ pub enum InventoryDBError {
|
||||||
DuplicateStoreID,
|
DuplicateStoreID,
|
||||||
DuplicateProductName,
|
DuplicateProductName,
|
||||||
DuplicateProductID,
|
DuplicateProductID,
|
||||||
|
DuplicateCustomizationID,
|
||||||
|
DuplicateCustomizationName,
|
||||||
InternalError,
|
InternalError,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
pub mod category_id_exists;
|
pub mod category_id_exists;
|
||||||
pub mod category_name_exists_for_store;
|
pub mod category_name_exists_for_store;
|
||||||
|
pub mod customization_id_exists;
|
||||||
|
pub mod customization_name_exists_for_product;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod product_id_exists;
|
pub mod product_id_exists;
|
||||||
pub mod product_name_exists_for_category;
|
pub mod product_name_exists_for_category;
|
||||||
|
|
|
@ -10,7 +10,12 @@ use mockall::*;
|
||||||
|
|
||||||
use super::errors::*;
|
use super::errors::*;
|
||||||
use crate::inventory::{
|
use crate::inventory::{
|
||||||
application::port::output::db::{product_id_exists::*, product_name_exists_for_category::*},
|
application::port::output::db::{
|
||||||
|
customization_id_exists::{self, *},
|
||||||
|
customization_name_exists_for_product::*,
|
||||||
|
product_id_exists::*,
|
||||||
|
product_name_exists_for_category::*,
|
||||||
|
},
|
||||||
domain::{
|
domain::{
|
||||||
add_product_command::AddProductCommand,
|
add_product_command::AddProductCommand,
|
||||||
product_added_event::{ProductAddedEvent, ProductAddedEventBuilder},
|
product_added_event::{ProductAddedEvent, ProductAddedEventBuilder},
|
||||||
|
@ -31,6 +36,8 @@ pub type AddProductServiceObj = Arc<dyn AddProductUseCase>;
|
||||||
pub struct AddProductService {
|
pub struct AddProductService {
|
||||||
db_product_name_exists_for_category: ProductNameExistsForCategoryDBPortObj,
|
db_product_name_exists_for_category: ProductNameExistsForCategoryDBPortObj,
|
||||||
db_product_id_exists: ProductIDExistsDBPortObj,
|
db_product_id_exists: ProductIDExistsDBPortObj,
|
||||||
|
db_customization_id_exists: CustomizationIDExistsDBPortObj,
|
||||||
|
db_customization_name_exists_for_product: CustomizationNameExistsForProductDBPortObj,
|
||||||
get_uuid: GetUUIDInterfaceObj,
|
get_uuid: GetUUIDInterfaceObj,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,19 +62,35 @@ impl AddProductUseCase for AddProductService {
|
||||||
let mut customizations = Vec::with_capacity(cmd.customizations().len());
|
let mut customizations = Vec::with_capacity(cmd.customizations().len());
|
||||||
for c in cmd.customizations().iter() {
|
for c in cmd.customizations().iter() {
|
||||||
let mut customization_id = self.get_uuid.get_uuid();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO:
|
let customization = CustomizationBuilder::default()
|
||||||
// 1. check if customization.name exists for product
|
.name(c.name().into())
|
||||||
// 2. check customization.customization_id duplicate in query table
|
.deleted(false)
|
||||||
|
.customization_id(customization_id)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
customizations.push(
|
if self
|
||||||
CustomizationBuilder::default()
|
.db_customization_name_exists_for_product
|
||||||
.name(c.name().into())
|
.customization_name_exists_for_product(&customization, &product_id)
|
||||||
.deleted(false)
|
.await?
|
||||||
.customization_id(customization_id)
|
{
|
||||||
.build()
|
return Err(InventoryError::DuplicateCustomizationName);
|
||||||
.unwrap(),
|
}
|
||||||
);
|
|
||||||
|
customizations.push(customization);
|
||||||
}
|
}
|
||||||
|
|
||||||
let product = ProductBuilder::default()
|
let product = ProductBuilder::default()
|
||||||
|
@ -168,7 +191,13 @@ pub mod tests {
|
||||||
.db_product_name_exists_for_category(
|
.db_product_name_exists_for_category(
|
||||||
mock_product_name_exists_for_category_db_port_false(IS_CALLED_ONLY_ONCE),
|
mock_product_name_exists_for_category_db_port_false(IS_CALLED_ONLY_ONCE),
|
||||||
)
|
)
|
||||||
|
.db_customization_id_exists(mock_customization_id_exists_db_port_false(
|
||||||
|
IS_CALLED_ONLY_THRICE,
|
||||||
|
))
|
||||||
.db_product_id_exists(mock_product_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
.db_product_id_exists(mock_product_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_THRICE),
|
||||||
|
)
|
||||||
.get_uuid(mock_get_uuid(IS_CALLED_ONLY_FOUR_TIMES))
|
.get_uuid(mock_get_uuid(IS_CALLED_ONLY_FOUR_TIMES))
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -195,6 +224,12 @@ pub mod tests {
|
||||||
)
|
)
|
||||||
.get_uuid(mock_get_uuid(IS_CALLED_ONLY_FOUR_TIMES))
|
.get_uuid(mock_get_uuid(IS_CALLED_ONLY_FOUR_TIMES))
|
||||||
.db_product_id_exists(mock_product_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
.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_false(
|
||||||
|
IS_CALLED_ONLY_THRICE,
|
||||||
|
))
|
||||||
|
.db_customization_name_exists_for_product(
|
||||||
|
mock_customization_name_exists_for_product_db_port_false(IS_CALLED_ONLY_THRICE),
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub enum InventoryError {
|
||||||
DuplicateCategoryName,
|
DuplicateCategoryName,
|
||||||
DuplicateStoreName,
|
DuplicateStoreName,
|
||||||
DuplicateProductName,
|
DuplicateProductName,
|
||||||
|
DuplicateCustomizationName,
|
||||||
InternalError,
|
InternalError,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ impl From<InventoryDBError> for InventoryError {
|
||||||
InventoryDBError::DuplicateCategoryName => Self::DuplicateCategoryName,
|
InventoryDBError::DuplicateCategoryName => Self::DuplicateCategoryName,
|
||||||
InventoryDBError::DuplicateStoreName => Self::DuplicateStoreName,
|
InventoryDBError::DuplicateStoreName => Self::DuplicateStoreName,
|
||||||
InventoryDBError::DuplicateProductName => Self::DuplicateProductName,
|
InventoryDBError::DuplicateProductName => Self::DuplicateProductName,
|
||||||
|
InventoryDBError::DuplicateCustomizationName => Self::DuplicateCustomizationName,
|
||||||
InventoryDBError::DuplicateStoreID => {
|
InventoryDBError::DuplicateStoreID => {
|
||||||
error!("DuplicateStoreID");
|
error!("DuplicateStoreID");
|
||||||
Self::InternalError
|
Self::InternalError
|
||||||
|
@ -36,6 +38,10 @@ impl From<InventoryDBError> for InventoryError {
|
||||||
error!("DuplicateCategoryID");
|
error!("DuplicateCategoryID");
|
||||||
Self::InternalError
|
Self::InternalError
|
||||||
}
|
}
|
||||||
|
InventoryDBError::DuplicateCustomizationID => {
|
||||||
|
error!("DuplicateCustomizationID");
|
||||||
|
Self::InternalError
|
||||||
|
}
|
||||||
InventoryDBError::InternalError => Self::InternalError,
|
InventoryDBError::InternalError => Self::InternalError,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue