2022-11-12 21:58:08 +01:00
|
|
|
use channels::stocks::{detailed_product, detailed_products, Error};
|
2022-11-08 07:49:06 +01:00
|
|
|
use channels::AsyncClient;
|
|
|
|
use config::SharedAppConfig;
|
2022-11-11 16:32:07 +01:00
|
|
|
use db_utils::PgT;
|
2022-11-28 17:00:19 +01:00
|
|
|
use model::{Limit, Offset};
|
2022-11-08 07:49:06 +01:00
|
|
|
|
2022-11-28 17:00:19 +01:00
|
|
|
use crate::db::{
|
|
|
|
AllCategories, Database, PhotosForProductVariants, ProductVariantsStock, ProductsVariants,
|
|
|
|
};
|
2022-11-12 21:58:08 +01:00
|
|
|
use crate::{begin_t, dbm_run};
|
2022-11-08 07:49:06 +01:00
|
|
|
|
|
|
|
pub async fn detailed_product(
|
2022-11-12 21:58:08 +01:00
|
|
|
input: detailed_product::Input,
|
|
|
|
db: Database,
|
2022-11-11 16:32:07 +01:00
|
|
|
_mqtt: AsyncClient,
|
|
|
|
_config: SharedAppConfig,
|
2022-11-08 07:49:06 +01:00
|
|
|
) -> detailed_product::Output {
|
2022-11-12 21:58:08 +01:00
|
|
|
let mut t = begin_t!(db, Error::InternalServerError);
|
|
|
|
|
|
|
|
let res = inner_detailed_product(input, &mut t, Some(_mqtt), Some(_config)).await;
|
|
|
|
|
|
|
|
t.commit().await.ok();
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn inner_detailed_product(
|
|
|
|
input: detailed_product::Input,
|
|
|
|
t: &mut PgT<'_>,
|
|
|
|
_mqtt: Option<AsyncClient>,
|
2022-11-28 17:00:19 +01:00
|
|
|
config: Option<SharedAppConfig>,
|
2022-11-12 21:58:08 +01:00
|
|
|
) -> detailed_product::Output {
|
|
|
|
let dbm = crate::db::FindProduct {
|
|
|
|
product_id: input.product_id,
|
|
|
|
};
|
|
|
|
let product = dbm_run!(dbm, &mut *t, Error::ProductNotFound(input.product_id));
|
|
|
|
let dbm = ProductsVariants {
|
|
|
|
product_ids: vec![input.product_id],
|
|
|
|
limit: Some(10.into()),
|
|
|
|
offset: Some(0.into()),
|
|
|
|
};
|
|
|
|
let variants = dbm_run!(dbm, &mut *t, Error::ProductVariants(vec![input.product_id]));
|
|
|
|
let dbm = ProductVariantsStock {
|
|
|
|
product_variant_ids: variants.iter().map(|p| p.id).collect(),
|
|
|
|
};
|
|
|
|
let stocks = dbm_run!(
|
|
|
|
dbm,
|
|
|
|
t,
|
|
|
|
Error::VariantStocks(variants.into_iter().map(|p| p.id).collect())
|
|
|
|
);
|
|
|
|
let dbm = PhotosForProductVariants {
|
|
|
|
product_variant_ids: variants.iter().map(|p| p.id).collect(),
|
|
|
|
};
|
|
|
|
let photos = dbm_run!(
|
|
|
|
dbm,
|
|
|
|
t,
|
|
|
|
Error::VariantPhotos(variants.into_iter().map(|p| p.id).collect())
|
|
|
|
);
|
|
|
|
|
2022-11-28 17:00:19 +01:00
|
|
|
let dbm = AllCategories {
|
|
|
|
limit: Limit::from_u32(2000),
|
|
|
|
offset: Offset::from_u32(0),
|
|
|
|
};
|
|
|
|
let categories = dbm_run!(dbm, t, Error::Categories);
|
|
|
|
|
2022-11-12 21:58:08 +01:00
|
|
|
let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id);
|
|
|
|
let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id);
|
|
|
|
let mut photos = utils::vec_to_hash_vec(photos, 10, |p| p.product_variant_id);
|
|
|
|
|
2022-11-28 17:00:19 +01:00
|
|
|
let product = utils::map_product(
|
|
|
|
product,
|
|
|
|
&mut variants,
|
|
|
|
&mut stocks,
|
|
|
|
&mut photos,
|
|
|
|
&categories,
|
|
|
|
&config
|
|
|
|
.as_ref()
|
|
|
|
.map(|c: &SharedAppConfig| c.lock().files().public_path())
|
|
|
|
.unwrap_or_else(|| "https://example.com/".into()),
|
|
|
|
);
|
2022-11-12 21:58:08 +01:00
|
|
|
|
2022-11-28 17:00:19 +01:00
|
|
|
Ok(detailed_product::Details { product })
|
2022-11-08 07:49:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn detailed_products(
|
|
|
|
input: detailed_products::Input,
|
|
|
|
db: Database,
|
2022-11-11 16:32:07 +01:00
|
|
|
_mqtt: AsyncClient,
|
|
|
|
_config: SharedAppConfig,
|
2022-11-08 07:49:06 +01:00
|
|
|
) -> detailed_products::Output {
|
2022-11-12 21:58:08 +01:00
|
|
|
let mut t = begin_t!(db, Error::InternalServerError);
|
2022-11-11 16:32:07 +01:00
|
|
|
|
|
|
|
let res = inner_detailed_products(input, &mut t, Some(_mqtt), Some(_config)).await;
|
|
|
|
|
|
|
|
t.commit().await.ok();
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn inner_detailed_products(
|
|
|
|
input: detailed_products::Input,
|
|
|
|
t: &mut PgT<'_>,
|
|
|
|
_mqtt: Option<AsyncClient>,
|
2022-11-28 17:00:19 +01:00
|
|
|
config: Option<SharedAppConfig>,
|
2022-11-11 16:32:07 +01:00
|
|
|
) -> detailed_products::Output {
|
|
|
|
let dbm = crate::db::AllProducts {
|
|
|
|
limit: input.limit,
|
|
|
|
offset: input.offset,
|
|
|
|
};
|
2022-11-12 21:58:08 +01:00
|
|
|
let products = dbm_run!(dbm, &mut *t, Error::Products);
|
2022-11-11 16:32:07 +01:00
|
|
|
let dbm = ProductsVariants {
|
|
|
|
product_ids: products.iter().map(|p| p.id).collect(),
|
|
|
|
limit: Some(Limit::from_u32(products.len() as u32 * 10)),
|
|
|
|
offset: Some(0.into()),
|
|
|
|
};
|
2022-11-12 21:58:08 +01:00
|
|
|
let variants = dbm_run!(
|
|
|
|
dbm,
|
|
|
|
&mut *t,
|
|
|
|
Error::ProductVariants(products.into_iter().map(|p| p.id).collect(),)
|
|
|
|
);
|
2022-11-11 16:32:07 +01:00
|
|
|
let dbm = ProductVariantsStock {
|
|
|
|
product_variant_ids: variants.iter().map(|p| p.id).collect(),
|
|
|
|
};
|
|
|
|
let stocks = match dbm.run(&mut *t).await {
|
|
|
|
Ok(stocks) => stocks,
|
|
|
|
Err(e) => {
|
2022-11-18 16:31:52 +01:00
|
|
|
tracing::warn!("{}", e);
|
2022-11-12 21:58:08 +01:00
|
|
|
return Err(Error::VariantStocks(
|
2022-11-11 16:32:07 +01:00
|
|
|
variants.into_iter().map(|p| p.id).collect(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let dbm = PhotosForProductVariants {
|
|
|
|
product_variant_ids: variants.iter().map(|p| p.id).collect(),
|
|
|
|
};
|
|
|
|
let photos = match dbm.run(t).await {
|
|
|
|
Ok(photos) => photos,
|
|
|
|
Err(e) => {
|
2022-11-18 16:31:52 +01:00
|
|
|
tracing::warn!("{}", e);
|
2022-11-12 21:58:08 +01:00
|
|
|
return Err(Error::VariantPhotos(
|
2022-11-11 16:32:07 +01:00
|
|
|
variants.into_iter().map(|p| p.id).collect(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
};
|
2022-11-28 17:00:19 +01:00
|
|
|
let dbm = AllCategories {
|
|
|
|
limit: Limit::from_u32(2000),
|
|
|
|
offset: Offset::from_u32(0),
|
|
|
|
};
|
|
|
|
let categories = dbm_run!(dbm, t, Error::Categories);
|
2022-11-11 16:32:07 +01:00
|
|
|
|
2022-11-12 21:58:08 +01:00
|
|
|
let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id);
|
|
|
|
let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id);
|
|
|
|
let mut photos = utils::vec_to_hash_vec(photos, 10, |p| p.product_variant_id);
|
2022-11-11 16:32:07 +01:00
|
|
|
|
|
|
|
let products = products
|
|
|
|
.into_iter()
|
2022-11-28 17:00:19 +01:00
|
|
|
.map(|product| {
|
|
|
|
utils::map_product(
|
|
|
|
product,
|
|
|
|
&mut variants,
|
|
|
|
&mut stocks,
|
|
|
|
&mut photos,
|
|
|
|
&categories,
|
|
|
|
&config
|
|
|
|
.as_ref()
|
|
|
|
.map(|config: &SharedAppConfig| config.lock().files().public_path())
|
|
|
|
.unwrap_or_else(|| "https:///example.com".into()),
|
|
|
|
)
|
|
|
|
})
|
2022-11-11 16:32:07 +01:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(detailed_products::Details { products })
|
|
|
|
}
|
|
|
|
|
2022-11-12 21:58:08 +01:00
|
|
|
mod utils {
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::hash::Hash;
|
|
|
|
|
|
|
|
use model::v2::*;
|
2022-11-28 17:00:19 +01:00
|
|
|
use model::CategoryMapper;
|
2022-11-12 21:58:08 +01:00
|
|
|
|
|
|
|
pub fn vec_to_hash_vec<Id: Hash + Eq, R, F: Fn(&R) -> Id>(
|
|
|
|
v: Vec<R>,
|
|
|
|
cap: usize,
|
|
|
|
f: F,
|
|
|
|
) -> HashMap<Id, Vec<R>> {
|
|
|
|
let len = v.len();
|
|
|
|
v.into_iter().fold(HashMap::with_capacity(len), |mut h, r| {
|
|
|
|
h.entry(f(&r))
|
|
|
|
.or_insert_with(|| Vec::with_capacity(cap))
|
|
|
|
.push(r);
|
|
|
|
h
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn map_product(
|
|
|
|
product: Product,
|
|
|
|
variants: &mut HashMap<ProductId, Vec<ProductVariant>>,
|
|
|
|
stocks: &mut HashMap<ProductVariantId, Vec<Stock>>,
|
|
|
|
photos: &mut HashMap<ProductVariantId, Vec<ProductLinkedPhoto>>,
|
2022-11-28 17:00:19 +01:00
|
|
|
categories: &[Category],
|
|
|
|
public_path: &str,
|
2022-11-12 21:58:08 +01:00
|
|
|
) -> DetailedProduct {
|
|
|
|
let Product {
|
|
|
|
id,
|
|
|
|
name,
|
|
|
|
category,
|
|
|
|
deliver_days_flag,
|
|
|
|
} = product;
|
|
|
|
DetailedProduct {
|
|
|
|
id,
|
|
|
|
name,
|
2022-11-28 17:00:19 +01:00
|
|
|
category: category.and_then(|name| CategoryMapper::db_into_api(name, categories)),
|
2022-11-12 21:58:08 +01:00
|
|
|
deliver_days_flag,
|
|
|
|
variants: variants
|
|
|
|
.remove(&id)
|
|
|
|
.unwrap_or(vec![])
|
|
|
|
.into_iter()
|
|
|
|
.map(
|
|
|
|
|ProductVariant {
|
|
|
|
id,
|
|
|
|
product_id: _,
|
|
|
|
name,
|
|
|
|
short_description,
|
|
|
|
long_description,
|
|
|
|
price,
|
2022-11-28 17:00:19 +01:00
|
|
|
quantity_unit,
|
|
|
|
}| {
|
|
|
|
let stocks = stocks.remove(&id).unwrap_or_default();
|
|
|
|
DetailedProductVariant {
|
|
|
|
id,
|
|
|
|
name,
|
|
|
|
short_description,
|
|
|
|
long_description,
|
|
|
|
price,
|
|
|
|
quantity_unit,
|
|
|
|
available: !stocks.is_empty(),
|
|
|
|
stocks,
|
|
|
|
photos: photos
|
|
|
|
.remove(&id)
|
|
|
|
.unwrap_or_default()
|
|
|
|
.into_iter()
|
|
|
|
.map(
|
|
|
|
|ProductLinkedPhoto {
|
|
|
|
photo_id,
|
2022-11-29 08:46:02 +01:00
|
|
|
local_path: _,
|
2022-11-28 17:00:19 +01:00
|
|
|
file_name,
|
|
|
|
unique_name,
|
|
|
|
product_variant_id: _,
|
|
|
|
}| model::api::Photo {
|
|
|
|
id: photo_id,
|
|
|
|
file_name,
|
|
|
|
url: format!("{public_path}/{unique_name}"),
|
|
|
|
unique_name,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.collect(),
|
|
|
|
}
|
2022-11-12 21:58:08 +01:00
|
|
|
},
|
|
|
|
)
|
|
|
|
.collect(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-11 16:32:07 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-11-13 15:04:14 +01:00
|
|
|
use channels::stocks::{detailed_product, detailed_products, Error};
|
2022-11-11 16:32:07 +01:00
|
|
|
use config::UpdateConfig;
|
|
|
|
use db_utils::PgT;
|
|
|
|
use model::v2::*;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
2022-11-13 15:04:14 +01:00
|
|
|
use crate::actions::load::{inner_detailed_product, inner_detailed_products};
|
2022-11-11 16:32:07 +01:00
|
|
|
use crate::db::*;
|
|
|
|
|
|
|
|
pub struct NoOpts;
|
|
|
|
impl UpdateConfig for NoOpts {}
|
|
|
|
|
|
|
|
async fn test_product(t: &mut PgT<'_>) -> Product {
|
|
|
|
CreateProduct {
|
|
|
|
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
|
|
|
category: None,
|
|
|
|
deliver_days_flag: Days(vec![Day::Friday, Day::Sunday]),
|
|
|
|
}
|
|
|
|
.run(t)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
|
|
|
|
CreateProductVariant {
|
|
|
|
product_id,
|
2022-11-14 16:51:24 +01:00
|
|
|
name: ProductVariantName::new(format!("{}", Uuid::new_v4())),
|
2022-11-11 16:32:07 +01:00
|
|
|
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
|
|
|
|
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
|
|
|
|
price: Default::default(),
|
2022-11-28 17:00:19 +01:00
|
|
|
quantity_unit: QuantityUnit::Gram,
|
2022-11-11 16:32:07 +01:00
|
|
|
}
|
|
|
|
.run(t)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn test_photo(t: &mut PgT<'_>) -> Photo {
|
|
|
|
CreatePhoto {
|
|
|
|
local_path: LocalPath::new(format!("{}", Uuid::new_v4())),
|
|
|
|
file_name: FileName::new(format!("{}", Uuid::new_v4())),
|
|
|
|
unique_name: UniqueName::new(format!("{}", Uuid::new_v4())),
|
|
|
|
}
|
|
|
|
.run(t)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn test_product_photo(
|
|
|
|
product_variant_id: ProductVariantId,
|
|
|
|
photo_id: PhotoId,
|
|
|
|
t: &mut PgT<'_>,
|
|
|
|
) -> ProductPhoto {
|
|
|
|
CreateProductPhoto {
|
|
|
|
product_variant_id,
|
|
|
|
photo_id,
|
|
|
|
}
|
|
|
|
.run(t)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn n_test_photo(
|
|
|
|
n: usize,
|
|
|
|
product_variant_id: ProductVariantId,
|
|
|
|
t: &mut PgT<'_>,
|
|
|
|
) -> Vec<(Photo, ProductPhoto)> {
|
|
|
|
let mut res = Vec::with_capacity(n);
|
|
|
|
|
|
|
|
for _ in 0..n {
|
|
|
|
let photo = test_photo(t).await;
|
|
|
|
let product_photo = test_product_photo(product_variant_id, photo.id, t).await;
|
|
|
|
|
|
|
|
res.push((photo, product_photo));
|
|
|
|
}
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn test_stock(product_variant_id: ProductVariantId, pool: &mut PgT<'_>) -> Stock {
|
|
|
|
let quantity = Quantity::from_u32(345);
|
|
|
|
let quantity_unit = QuantityUnit::Piece;
|
|
|
|
|
|
|
|
CreateStock {
|
|
|
|
product_variant_id,
|
|
|
|
quantity_unit,
|
|
|
|
quantity,
|
|
|
|
}
|
|
|
|
.run(&mut *pool)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn n_test_variant(
|
|
|
|
variant_count: usize,
|
|
|
|
product_id: ProductId,
|
|
|
|
t: &mut PgT<'_>,
|
|
|
|
) -> Vec<(ProductVariant, Stock, Vec<(Photo, ProductPhoto)>)> {
|
|
|
|
let mut variants = Vec::with_capacity(variant_count);
|
|
|
|
|
|
|
|
for _ in 0..variant_count {
|
|
|
|
let variant = test_product_variant(product_id, t).await;
|
|
|
|
let stock = test_stock(variant.id, t).await;
|
|
|
|
let photos = n_test_photo(3, variant.id, t).await;
|
|
|
|
|
|
|
|
variants.push((variant, stock, photos));
|
|
|
|
}
|
|
|
|
variants
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn load_details() {
|
|
|
|
testx::db_t_ref!(t);
|
|
|
|
|
|
|
|
let product_1 = test_product(&mut t).await;
|
|
|
|
let _variants_1 = n_test_variant(3, product_1.id, &mut t).await;
|
|
|
|
|
|
|
|
let product_2 = test_product(&mut t).await;
|
|
|
|
let _variants_2 = n_test_variant(5, product_2.id, &mut t).await;
|
|
|
|
|
|
|
|
let product_3 = test_product(&mut t).await;
|
|
|
|
let _variants_2 = n_test_variant(2, product_3.id, &mut t).await;
|
|
|
|
|
|
|
|
let res = inner_detailed_products(
|
|
|
|
detailed_products::Input {
|
|
|
|
limit: Limit::from_u32(2000),
|
|
|
|
offset: Offset::from_u32(0),
|
|
|
|
},
|
|
|
|
&mut t,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
testx::db_rollback!(t);
|
|
|
|
|
|
|
|
let mut res = res.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(res.products.len(), 3);
|
|
|
|
let product = res.products.remove(0);
|
2022-11-12 21:58:08 +01:00
|
|
|
|
2022-11-11 16:32:07 +01:00
|
|
|
assert_eq!(product.variants.len(), 3);
|
|
|
|
for variant in product.variants {
|
|
|
|
assert_eq!(variant.photos.len(), 3);
|
|
|
|
assert_eq!(variant.stocks.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
let product = res.products.remove(0);
|
|
|
|
assert_eq!(product.variants.len(), 5);
|
|
|
|
for variant in product.variants {
|
|
|
|
assert_eq!(variant.photos.len(), 3);
|
|
|
|
assert_eq!(variant.stocks.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
let product = res.products.remove(0);
|
|
|
|
assert_eq!(product.variants.len(), 2);
|
|
|
|
for variant in product.variants {
|
|
|
|
assert_eq!(variant.photos.len(), 3);
|
|
|
|
assert_eq!(variant.stocks.len(), 1);
|
|
|
|
}
|
|
|
|
}
|
2022-11-13 15:04:14 +01:00
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn load_detail() {
|
|
|
|
testx::db_t_ref!(t);
|
|
|
|
|
|
|
|
let product_1 = test_product(&mut t).await;
|
|
|
|
let _variants_1 = n_test_variant(3, product_1.id, &mut t).await;
|
|
|
|
|
|
|
|
let product_2 = test_product(&mut t).await;
|
|
|
|
let _variants_2 = n_test_variant(5, product_2.id, &mut t).await;
|
|
|
|
|
|
|
|
let product_3 = test_product(&mut t).await;
|
|
|
|
let _variants_2 = n_test_variant(2, product_3.id, &mut t).await;
|
|
|
|
|
|
|
|
let res1 = inner_detailed_product(
|
|
|
|
detailed_product::Input {
|
|
|
|
product_id: product_1.id,
|
|
|
|
},
|
|
|
|
&mut t,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let res2 = inner_detailed_product(
|
|
|
|
detailed_product::Input {
|
|
|
|
product_id: product_3.id,
|
|
|
|
},
|
|
|
|
&mut t,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
let res3 = inner_detailed_product(
|
|
|
|
detailed_product::Input {
|
|
|
|
product_id: (-1).into(),
|
|
|
|
},
|
|
|
|
&mut t,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
testx::db_rollback!(t);
|
|
|
|
|
|
|
|
{
|
|
|
|
let res = res1.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(res.product.id, product_1.id);
|
|
|
|
let product = res.product;
|
|
|
|
|
|
|
|
assert_eq!(product.variants.len(), 3);
|
|
|
|
for variant in product.variants {
|
|
|
|
assert_eq!(variant.photos.len(), 3);
|
|
|
|
assert_eq!(variant.stocks.len(), 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
let res = res2.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(res.product.id, product_3.id);
|
|
|
|
let product = res.product;
|
|
|
|
|
|
|
|
assert_eq!(product.variants.len(), 2);
|
|
|
|
for variant in product.variants {
|
|
|
|
assert_eq!(variant.photos.len(), 3);
|
|
|
|
assert_eq!(variant.stocks.len(), 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(res3, Err(Error::ProductNotFound((-1).into())));
|
|
|
|
}
|
2022-11-08 07:49:06 +01:00
|
|
|
}
|