Commit fdfca502e5859011cf8bf41d8dad3665de069f6d
1 parent
f93e2579
test: 完善 PR 修复回归测试并启用商城历史用例
启用并修复商品、DIY 页面及模板、交易订单测试,补齐测试表结构与依赖。 增加 CRM 删除日志上下文、优惠券模板已删除场景的回归测试,并对齐相关注释。 关联 #1313、#1315、#1317、#1319、#1321、#1323。
Showing
15 changed files
with
913 additions
and
546 deletions
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImpl.java
| ... | ... | @@ -118,6 +118,7 @@ public class CrmProductCategoryServiceImpl implements CrmProductCategoryService |
| 118 | 118 | if (crmProductService.getProductByCategoryId(id) > 0) { |
| 119 | 119 | throw exception(PRODUCT_CATEGORY_USED); |
| 120 | 120 | } |
| 121 | + | |
| 121 | 122 | // 2. 删除 |
| 122 | 123 | productCategoryMapper.deleteById(id); |
| 123 | 124 | ... | ... |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImpl.java
| ... | ... | @@ -135,9 +135,9 @@ public class CrmProductServiceImpl implements CrmProductService { |
| 135 | 135 | success = CRM_PRODUCT_DELETE_SUCCESS) |
| 136 | 136 | @CrmPermission(bizType = CrmBizTypeEnum.CRM_PRODUCT, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) |
| 137 | 137 | public void deleteProduct(Long id) { |
| 138 | - // 校验存在 | |
| 138 | + // 1.1 校验存在 | |
| 139 | 139 | CrmProductDO product = validateProductExists(id); |
| 140 | - // 校验未被合同/商机引用,避免删除后产生悬空引用 | |
| 140 | + // 1.2 校验未被合同/商机引用,避免删除后产生悬空引用 | |
| 141 | 141 | if (contractService.getContractProductCountByProductId(id) > 0) { |
| 142 | 142 | throw exception(PRODUCT_DELETE_FAIL_CONTRACT_EXISTS); |
| 143 | 143 | } |
| ... | ... | @@ -145,10 +145,10 @@ public class CrmProductServiceImpl implements CrmProductService { |
| 145 | 145 | throw exception(PRODUCT_DELETE_FAIL_BUSINESS_EXISTS); |
| 146 | 146 | } |
| 147 | 147 | |
| 148 | - // 删除 | |
| 148 | + // 2. 删除 | |
| 149 | 149 | productMapper.deleteById(id); |
| 150 | 150 | |
| 151 | - // 记录操作日志上下文 | |
| 151 | + // 3. 记录操作日志上下文 | |
| 152 | 152 | LogRecordContext.putVariable("product", product); |
| 153 | 153 | } |
| 154 | 154 | ... | ... |
yudao-module-crm/src/test/java/cn/iocoder/yudao/module/crm/service/product/CrmProductCategoryServiceImplTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.product; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 4 | +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; | |
| 5 | +import cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductCategoryMapper; | |
| 6 | +import com.mzt.logapi.context.LogRecordContext; | |
| 7 | +import org.junit.jupiter.api.AfterEach; | |
| 8 | +import org.junit.jupiter.api.BeforeEach; | |
| 9 | +import org.junit.jupiter.api.Test; | |
| 10 | +import org.mockito.InjectMocks; | |
| 11 | +import org.mockito.Mock; | |
| 12 | + | |
| 13 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | |
| 14 | +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; | |
| 15 | +import static org.junit.jupiter.api.Assertions.*; | |
| 16 | +import static org.mockito.ArgumentMatchers.anyLong; | |
| 17 | +import static org.mockito.Mockito.*; | |
| 18 | + | |
| 19 | +/** | |
| 20 | + * {@link CrmProductCategoryServiceImpl} 的单元测试类 | |
| 21 | + * | |
| 22 | + * @author 芋道源码 | |
| 23 | + */ | |
| 24 | +public class CrmProductCategoryServiceImplTest extends BaseMockitoUnitTest { | |
| 25 | + | |
| 26 | + @InjectMocks | |
| 27 | + private CrmProductCategoryServiceImpl productCategoryService; | |
| 28 | + | |
| 29 | + @Mock | |
| 30 | + private CrmProductCategoryMapper productCategoryMapper; | |
| 31 | + @Mock | |
| 32 | + private CrmProductService crmProductService; | |
| 33 | + | |
| 34 | + @BeforeEach | |
| 35 | + public void setUp() { | |
| 36 | + LogRecordContext.putEmptySpan(); | |
| 37 | + } | |
| 38 | + | |
| 39 | + @AfterEach | |
| 40 | + public void tearDown() { | |
| 41 | + LogRecordContext.clear(); | |
| 42 | + } | |
| 43 | + | |
| 44 | + @Test | |
| 45 | + public void testDeleteProductCategory_success() { | |
| 46 | + // mock 数据 | |
| 47 | + CrmProductCategoryDO category = new CrmProductCategoryDO().setId(1L).setName("芋道分类").setParentId(0L); | |
| 48 | + when(productCategoryMapper.selectById(1L)).thenReturn(category); | |
| 49 | + // mock 方法(无子分类、未被产品使用) | |
| 50 | + when(productCategoryMapper.selectCountByParentId(1L)).thenReturn(0L); | |
| 51 | + when(crmProductService.getProductByCategoryId(1L)).thenReturn(0L); | |
| 52 | + | |
| 53 | + // 调用 | |
| 54 | + productCategoryService.deleteProductCategory(1L); | |
| 55 | + // 断言 | |
| 56 | + verify(productCategoryMapper).deleteById(1L); | |
| 57 | + // 断言操作日志上下文,为删除前的分类 | |
| 58 | + CrmProductCategoryDO logCategory = (CrmProductCategoryDO) LogRecordContext.getVariable("productCategory"); | |
| 59 | + assertSame(category, logCategory); | |
| 60 | + assertEquals("芋道分类", logCategory.getName()); | |
| 61 | + } | |
| 62 | + | |
| 63 | + @Test | |
| 64 | + public void testDeleteProductCategory_notExists() { | |
| 65 | + // 调用,并断言异常 | |
| 66 | + assertServiceException(() -> productCategoryService.deleteProductCategory(1L), PRODUCT_CATEGORY_NOT_EXISTS); | |
| 67 | + // 断言 | |
| 68 | + verify(productCategoryMapper, never()).deleteById(anyLong()); | |
| 69 | + assertNull(LogRecordContext.getVariable("productCategory")); | |
| 70 | + } | |
| 71 | + | |
| 72 | + @Test | |
| 73 | + public void testDeleteProductCategory_existsChildren() { | |
| 74 | + // mock 数据 | |
| 75 | + when(productCategoryMapper.selectById(1L)).thenReturn(new CrmProductCategoryDO().setId(1L).setName("芋道分类")); | |
| 76 | + // mock 方法(存在子分类) | |
| 77 | + when(productCategoryMapper.selectCountByParentId(1L)).thenReturn(1L); | |
| 78 | + | |
| 79 | + // 调用,并断言异常 | |
| 80 | + assertServiceException(() -> productCategoryService.deleteProductCategory(1L), PRODUCT_CATEGORY_EXISTS_CHILDREN); | |
| 81 | + // 断言 | |
| 82 | + verify(productCategoryMapper, never()).deleteById(anyLong()); | |
| 83 | + verifyNoInteractions(crmProductService); | |
| 84 | + assertNull(LogRecordContext.getVariable("productCategory")); | |
| 85 | + } | |
| 86 | + | |
| 87 | + @Test | |
| 88 | + public void testDeleteProductCategory_used() { | |
| 89 | + // mock 数据 | |
| 90 | + when(productCategoryMapper.selectById(1L)).thenReturn(new CrmProductCategoryDO().setId(1L).setName("芋道分类")); | |
| 91 | + // mock 方法(被产品使用) | |
| 92 | + when(productCategoryMapper.selectCountByParentId(1L)).thenReturn(0L); | |
| 93 | + when(crmProductService.getProductByCategoryId(1L)).thenReturn(1L); | |
| 94 | + | |
| 95 | + // 调用,并断言异常 | |
| 96 | + assertServiceException(() -> productCategoryService.deleteProductCategory(1L), PRODUCT_CATEGORY_USED); | |
| 97 | + // 断言 | |
| 98 | + verify(productCategoryMapper, never()).deleteById(anyLong()); | |
| 99 | + assertNull(LogRecordContext.getVariable("productCategory")); | |
| 100 | + } | |
| 101 | + | |
| 102 | +} | ... | ... |
yudao-module-crm/src/test/java/cn/iocoder/yudao/module/crm/service/product/CrmProductServiceImplTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.crm.service.product; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 4 | +import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; | |
| 5 | +import cn.iocoder.yudao.module.crm.dal.mysql.product.CrmProductMapper; | |
| 6 | +import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; | |
| 7 | +import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; | |
| 8 | +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; | |
| 9 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 10 | +import com.mzt.logapi.context.LogRecordContext; | |
| 11 | +import org.junit.jupiter.api.AfterEach; | |
| 12 | +import org.junit.jupiter.api.BeforeEach; | |
| 13 | +import org.junit.jupiter.api.Test; | |
| 14 | +import org.mockito.InjectMocks; | |
| 15 | +import org.mockito.Mock; | |
| 16 | + | |
| 17 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | |
| 18 | +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; | |
| 19 | +import static org.junit.jupiter.api.Assertions.*; | |
| 20 | +import static org.mockito.ArgumentMatchers.anyLong; | |
| 21 | +import static org.mockito.Mockito.*; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * {@link CrmProductServiceImpl} 的单元测试类 | |
| 25 | + * | |
| 26 | + * @author 芋道源码 | |
| 27 | + */ | |
| 28 | +public class CrmProductServiceImplTest extends BaseMockitoUnitTest { | |
| 29 | + | |
| 30 | + @InjectMocks | |
| 31 | + private CrmProductServiceImpl productService; | |
| 32 | + | |
| 33 | + @Mock | |
| 34 | + private CrmProductMapper productMapper; | |
| 35 | + @Mock | |
| 36 | + private CrmProductCategoryService productCategoryService; | |
| 37 | + @Mock | |
| 38 | + private CrmContractService contractService; | |
| 39 | + @Mock | |
| 40 | + private CrmBusinessService businessService; | |
| 41 | + @Mock | |
| 42 | + private CrmPermissionService permissionService; | |
| 43 | + @Mock | |
| 44 | + private AdminUserApi adminUserApi; | |
| 45 | + | |
| 46 | + @BeforeEach | |
| 47 | + public void setUp() { | |
| 48 | + LogRecordContext.putEmptySpan(); | |
| 49 | + } | |
| 50 | + | |
| 51 | + @AfterEach | |
| 52 | + public void tearDown() { | |
| 53 | + LogRecordContext.clear(); | |
| 54 | + } | |
| 55 | + | |
| 56 | + @Test | |
| 57 | + public void testDeleteProduct_success() { | |
| 58 | + // mock 数据 | |
| 59 | + CrmProductDO product = new CrmProductDO().setId(1L).setName("芋道产品"); | |
| 60 | + when(productMapper.selectById(1L)).thenReturn(product); | |
| 61 | + // mock 方法(未被合同、商机引用) | |
| 62 | + when(contractService.getContractProductCountByProductId(1L)).thenReturn(0L); | |
| 63 | + when(businessService.getBusinessProductCountByProductId(1L)).thenReturn(0L); | |
| 64 | + | |
| 65 | + // 调用 | |
| 66 | + productService.deleteProduct(1L); | |
| 67 | + // 断言 | |
| 68 | + verify(productMapper).deleteById(1L); | |
| 69 | + // 断言操作日志上下文,为删除前的产品 | |
| 70 | + CrmProductDO logProduct = (CrmProductDO) LogRecordContext.getVariable("product"); | |
| 71 | + assertSame(product, logProduct); | |
| 72 | + assertEquals("芋道产品", logProduct.getName()); | |
| 73 | + } | |
| 74 | + | |
| 75 | + @Test | |
| 76 | + public void testDeleteProduct_notExists() { | |
| 77 | + // 调用,并断言异常 | |
| 78 | + assertServiceException(() -> productService.deleteProduct(1L), PRODUCT_NOT_EXISTS); | |
| 79 | + // 断言 | |
| 80 | + verify(productMapper, never()).deleteById(anyLong()); | |
| 81 | + assertNull(LogRecordContext.getVariable("product")); | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Test | |
| 85 | + public void testDeleteProduct_contractExists() { | |
| 86 | + // mock 数据 | |
| 87 | + when(productMapper.selectById(1L)).thenReturn(new CrmProductDO().setId(1L).setName("芋道产品")); | |
| 88 | + // mock 方法(被合同引用) | |
| 89 | + when(contractService.getContractProductCountByProductId(1L)).thenReturn(1L); | |
| 90 | + | |
| 91 | + // 调用,并断言异常 | |
| 92 | + assertServiceException(() -> productService.deleteProduct(1L), PRODUCT_DELETE_FAIL_CONTRACT_EXISTS); | |
| 93 | + // 断言 | |
| 94 | + verify(productMapper, never()).deleteById(anyLong()); | |
| 95 | + assertNull(LogRecordContext.getVariable("product")); | |
| 96 | + } | |
| 97 | + | |
| 98 | + @Test | |
| 99 | + public void testDeleteProduct_businessExists() { | |
| 100 | + // mock 数据 | |
| 101 | + when(productMapper.selectById(1L)).thenReturn(new CrmProductDO().setId(1L).setName("芋道产品")); | |
| 102 | + // mock 方法(被商机引用) | |
| 103 | + when(contractService.getContractProductCountByProductId(1L)).thenReturn(0L); | |
| 104 | + when(businessService.getBusinessProductCountByProductId(1L)).thenReturn(1L); | |
| 105 | + | |
| 106 | + // 调用,并断言异常 | |
| 107 | + assertServiceException(() -> productService.deleteProduct(1L), PRODUCT_DELETE_FAIL_BUSINESS_EXISTS); | |
| 108 | + // 断言 | |
| 109 | + verify(productMapper, never()).deleteById(anyLong()); | |
| 110 | + assertNull(LogRecordContext.getVariable("product")); | |
| 111 | + } | |
| 112 | + | |
| 113 | +} | ... | ... |
yudao-module-mall/yudao-module-product/pom.xml
| ... | ... | @@ -24,7 +24,7 @@ |
| 24 | 24 | <version>${revision}</version> |
| 25 | 25 | </dependency> |
| 26 | 26 | |
| 27 | - <!-- trade-api:配送方式枚举 DeliveryTypeEnum 校验(A68) --> | |
| 27 | + <!-- trade-api:配送方式枚举 DeliveryTypeEnum 校验 --> | |
| 28 | 28 | <dependency> |
| 29 | 29 | <groupId>cn.iocoder.boot</groupId> |
| 30 | 30 | <artifactId>yudao-module-trade-api</artifactId> | ... | ... |
yudao-module-mall/yudao-module-product/src/test/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImplTest.java
| ... | ... | @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.product.service.spu; |
| 3 | 3 | import cn.hutool.core.date.DateUtil; |
| 4 | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.hutool.core.util.RandomUtil; |
| 6 | -import cn.iocoder.yudao.framework.common.exception.ServiceException; | |
| 7 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; | |
| 8 | 8 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 9 | 9 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSkuSaveReqVO; |
| 10 | 10 | import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuPageReqVO; |
| ... | ... | @@ -17,29 +17,37 @@ import cn.iocoder.yudao.module.product.service.category.ProductCategoryServiceIm |
| 17 | 17 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyService; |
| 18 | 18 | import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; |
| 19 | 19 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuServiceImpl; |
| 20 | +import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; | |
| 20 | 21 | import com.google.common.collect.Lists; |
| 21 | 22 | import jakarta.annotation.Resource; |
| 22 | -import org.junit.jupiter.api.Assertions; | |
| 23 | -import org.junit.jupiter.api.Disabled; | |
| 23 | +import jakarta.validation.ConstraintViolation; | |
| 24 | +import jakarta.validation.Validation; | |
| 25 | +import jakarta.validation.Validator; | |
| 24 | 26 | import org.junit.jupiter.api.Test; |
| 25 | 27 | import org.springframework.context.annotation.Import; |
| 26 | 28 | import org.springframework.test.context.bean.override.mockito.MockitoBean; |
| 27 | 29 | |
| 28 | 30 | import java.math.RoundingMode; |
| 29 | -import java.util.ArrayList; | |
| 30 | -import java.util.Date; | |
| 31 | -import java.util.List; | |
| 32 | -import java.util.Map; | |
| 31 | +import java.util.*; | |
| 32 | +import java.util.function.Consumer; | |
| 33 | 33 | import java.util.stream.Collectors; |
| 34 | 34 | import java.util.stream.Stream; |
| 35 | 35 | |
| 36 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 37 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 38 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | |
| 39 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | |
| 36 | 40 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 37 | 41 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 42 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | |
| 38 | 43 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 44 | +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; | |
| 45 | +import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_RECYCLE; | |
| 46 | +import static java.util.Collections.singletonList; | |
| 39 | 47 | import static org.assertj.core.util.Lists.newArrayList; |
| 40 | -import static org.junit.jupiter.api.Assertions.assertEquals; | |
| 48 | +import static org.junit.jupiter.api.Assertions.*; | |
| 41 | 49 | import static org.mockito.ArgumentMatchers.eq; |
| 42 | -import static org.mockito.Mockito.when; | |
| 50 | +import static org.mockito.Mockito.*; | |
| 43 | 51 | |
| 44 | 52 | // TODO @芋艿:review 下单元测试 |
| 45 | 53 | |
| ... | ... | @@ -48,10 +56,12 @@ import static org.mockito.Mockito.when; |
| 48 | 56 | * |
| 49 | 57 | * @author 芋道源码 |
| 50 | 58 | */ |
| 51 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | |
| 59 | +// DONE 芋艿:后续 fix 补充的单测 | |
| 52 | 60 | @Import(ProductSpuServiceImpl.class) |
| 53 | 61 | public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 54 | 62 | |
| 63 | + private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator(); | |
| 64 | + | |
| 55 | 65 | @Resource |
| 56 | 66 | private ProductSpuServiceImpl productSpuService; |
| 57 | 67 | |
| ... | ... | @@ -84,18 +94,7 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 84 | 94 | @Test |
| 85 | 95 | public void testCreateSpu_success() { |
| 86 | 96 | // 准备参数 |
| 87 | - ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomPojo(ProductSkuSaveReqVO.class, o->{ | |
| 88 | - // 限制范围为正整数 | |
| 89 | - o.setCostPrice(generaInt()); | |
| 90 | - o.setPrice(generaInt()); | |
| 91 | - o.setMarketPrice(generaInt()); | |
| 92 | - o.setStock(generaInt()); | |
| 93 | - o.setFirstBrokeragePrice(generaInt()); | |
| 94 | - o.setSecondBrokeragePrice(generaInt()); | |
| 95 | - // 限制分数为两位数 | |
| 96 | - o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | |
| 97 | - o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | |
| 98 | - }); | |
| 97 | + ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomSkuSaveReqVO(); | |
| 99 | 98 | ProductSpuSaveReqVO createReqVO = randomPojo(ProductSpuSaveReqVO.class,o->{ |
| 100 | 99 | o.setCategoryId(generateId()); |
| 101 | 100 | o.setBrandId(generateId()); |
| ... | ... | @@ -104,291 +103,215 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 104 | 103 | o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 |
| 105 | 104 | o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO)); |
| 106 | 105 | }); |
| 106 | + // mock 方法(分类层级) | |
| 107 | 107 | when(categoryService.getCategoryLevel(eq(createReqVO.getCategoryId()))).thenReturn(2); |
| 108 | - Long spu = productSpuService.createSpu(createReqVO); | |
| 109 | - ProductSpuDO productSpuDO = productSpuMapper.selectById(spu); | |
| 110 | - assertPojoEquals(createReqVO, productSpuDO); | |
| 108 | + | |
| 109 | + // 调用 | |
| 110 | + Long spuId = productSpuService.createSpu(createReqVO); | |
| 111 | + // 断言:销量、浏览量为初始值 | |
| 112 | + ProductSpuDO productSpuDO = productSpuMapper.selectById(spuId); | |
| 113 | + assertPojoEquals(createReqVO, productSpuDO, "salesCount", "browseCount"); | |
| 114 | + assertEquals(ProductSpuStatusEnum.ENABLE.getStatus(), productSpuDO.getStatus()); | |
| 115 | + assertEquals(0, productSpuDO.getSalesCount()); | |
| 116 | + assertEquals(0, productSpuDO.getBrowseCount()); | |
| 117 | + // 断言:基于 SKU 初始化价格、库存 | |
| 118 | + assertEquals(skuCreateOrUpdateReqVO.getPrice(), productSpuDO.getPrice()); | |
| 119 | + assertEquals(skuCreateOrUpdateReqVO.getMarketPrice(), productSpuDO.getMarketPrice()); | |
| 120 | + assertEquals(skuCreateOrUpdateReqVO.getCostPrice(), productSpuDO.getCostPrice()); | |
| 121 | + assertEquals(skuCreateOrUpdateReqVO.getStock() * 3, productSpuDO.getStock()); | |
| 122 | + // 断言:创建 SKU | |
| 123 | + verify(productSkuService).createSkuList(eq(spuId), eq(createReqVO.getSkus())); | |
| 111 | 124 | } |
| 112 | 125 | |
| 113 | 126 | @Test |
| 114 | 127 | public void testUpdateSpu_success() { |
| 128 | + // mock 数据 | |
| 129 | + ProductSpuDO dbSpu = randomSpuDO(); | |
| 130 | + productSpuMapper.insert(dbSpu); | |
| 115 | 131 | // 准备参数 |
| 116 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | |
| 117 | - o.setCategoryId(generateId()); | |
| 118 | - o.setBrandId(generateId()); | |
| 119 | - o.setDeliveryTemplateId(generateId()); | |
| 120 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 121 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 122 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 123 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 124 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 125 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 126 | - o.setStock(generaInt()); // 限制范围为正整数 | |
| 127 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 128 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 129 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 130 | - }); | |
| 131 | - productSpuMapper.insert(createReqVO); | |
| 132 | - // 准备参数 | |
| 133 | - ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomPojo(ProductSkuSaveReqVO.class, o->{ | |
| 134 | - // 限制范围为正整数 | |
| 135 | - o.setCostPrice(generaInt()); | |
| 136 | - o.setPrice(generaInt()); | |
| 137 | - o.setMarketPrice(generaInt()); | |
| 138 | - o.setStock(generaInt()); | |
| 139 | - o.setFirstBrokeragePrice(generaInt()); | |
| 140 | - o.setSecondBrokeragePrice(generaInt()); | |
| 141 | - // 限制分数为两位数 | |
| 142 | - o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | |
| 143 | - o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | |
| 144 | - }); | |
| 145 | - // 准备参数 | |
| 132 | + ProductSkuSaveReqVO skuCreateOrUpdateReqVO = randomSkuSaveReqVO(); | |
| 146 | 133 | ProductSpuSaveReqVO reqVO = randomPojo(ProductSpuSaveReqVO.class, o -> { |
| 147 | - o.setId(createReqVO.getId()); // 设置更新的 ID | |
| 134 | + o.setId(dbSpu.getId()); // 设置更新的 ID | |
| 148 | 135 | o.setCategoryId(generateId()); |
| 149 | 136 | o.setBrandId(generateId()); |
| 150 | 137 | o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 |
| 151 | 138 | o.setGiveIntegral(generaInt()); // 限制范围为正整数 |
| 152 | 139 | o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 |
| 153 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 154 | 140 | o.setSalesCount(generaInt()); // 限制范围为正整数 |
| 155 | 141 | o.setBrowseCount(generaInt()); // 限制范围为正整数 |
| 156 | 142 | o.setSkus(newArrayList(skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO,skuCreateOrUpdateReqVO)); |
| 157 | 143 | }); |
| 144 | + // mock 方法(分类层级) | |
| 158 | 145 | when(categoryService.getCategoryLevel(eq(reqVO.getCategoryId()))).thenReturn(2); |
| 146 | + | |
| 159 | 147 | // 调用 |
| 160 | 148 | productSpuService.updateSpu(reqVO); |
| 161 | - // 校验是否更新正确 | |
| 149 | + // 校验是否更新正确:状态保持不变 | |
| 162 | 150 | ProductSpuDO spu = productSpuMapper.selectById(reqVO.getId()); // 获取最新的 |
| 163 | 151 | assertPojoEquals(reqVO, spu); |
| 152 | + assertEquals(dbSpu.getStatus(), spu.getStatus()); | |
| 153 | + assertEquals(skuCreateOrUpdateReqVO.getStock() * 3, spu.getStock()); | |
| 154 | + // 断言:更新 SKU | |
| 155 | + verify(productSkuService).updateSkuList(eq(reqVO.getId()), eq(reqVO.getSkus())); | |
| 164 | 156 | } |
| 165 | 157 | |
| 166 | 158 | @Test |
| 167 | - public void testValidateSpuExists_exception() { | |
| 159 | + public void testUpdateSpu_notExists() { | |
| 160 | + // 准备参数 | |
| 168 | 161 | ProductSpuSaveReqVO reqVO = randomPojo(ProductSpuSaveReqVO.class); |
| 169 | - // 调用 | |
| 170 | - Assertions.assertThrows(ServiceException.class, () -> productSpuService.updateSpu(reqVO)); | |
| 162 | + | |
| 163 | + // 调用, 并断言异常 | |
| 164 | + assertServiceException(() -> productSpuService.updateSpu(reqVO), SPU_NOT_EXISTS); | |
| 171 | 165 | } |
| 172 | 166 | |
| 173 | 167 | @Test |
| 174 | - void deleteSpu() { | |
| 175 | - // 准备参数 | |
| 176 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | |
| 177 | - o.setCategoryId(generateId()); | |
| 178 | - o.setBrandId(generateId()); | |
| 179 | - o.setDeliveryTemplateId(generateId()); | |
| 180 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 181 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 182 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 183 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 184 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 185 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 186 | - o.setStock(generaInt()); // 限制范围为正整数 | |
| 187 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 188 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 189 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 190 | - o.setStatus(-1); // 加入回收站才可删除 | |
| 191 | - }); | |
| 192 | - productSpuMapper.insert(createReqVO); | |
| 168 | + public void testValidateDeliveryTypes_success() { | |
| 169 | + // 调用,并断言:快递发货、用户自提,及其组合 | |
| 170 | + assertEquals(Collections.emptySet(), validateDeliveryTypes(singletonList(DeliveryTypeEnum.EXPRESS.getType()))); | |
| 171 | + assertEquals(Collections.emptySet(), validateDeliveryTypes(singletonList(DeliveryTypeEnum.PICK_UP.getType()))); | |
| 172 | + assertEquals(Collections.emptySet(), validateDeliveryTypes(Arrays.asList( | |
| 173 | + DeliveryTypeEnum.EXPRESS.getType(), DeliveryTypeEnum.PICK_UP.getType()))); | |
| 174 | + } | |
| 175 | + | |
| 176 | + @Test | |
| 177 | + public void testValidateDeliveryTypes_invalid() { | |
| 178 | + // 调用,并断言:不在枚举范围内 | |
| 179 | + Set<String> invalidMessages = Collections.singleton("配送方式不正确"); | |
| 180 | + assertEquals(invalidMessages, validateDeliveryTypes(singletonList(0))); | |
| 181 | + assertEquals(invalidMessages, validateDeliveryTypes(singletonList(3))); | |
| 182 | + assertEquals(invalidMessages, validateDeliveryTypes(singletonList(-1))); | |
| 183 | + assertEquals(invalidMessages, validateDeliveryTypes(Arrays.asList(DeliveryTypeEnum.EXPRESS.getType(), 3))); | |
| 184 | + assertEquals(invalidMessages, validateDeliveryTypes(Arrays.asList(DeliveryTypeEnum.EXPRESS.getType(), null))); | |
| 185 | + // 调用,并断言:为空 | |
| 186 | + Set<String> emptyMessages = Collections.singleton("配送方式不能为空"); | |
| 187 | + assertEquals(emptyMessages, validateDeliveryTypes(null)); | |
| 188 | + assertEquals(emptyMessages, validateDeliveryTypes(Collections.emptyList())); | |
| 189 | + } | |
| 190 | + | |
| 191 | + @Test | |
| 192 | + public void testDeleteSpu_success() { | |
| 193 | + // mock 数据 | |
| 194 | + ProductSpuDO dbSpu = randomSpuDO(o -> o.setStatus(ProductSpuStatusEnum.RECYCLE.getStatus())); // 加入回收站才可删除 | |
| 195 | + productSpuMapper.insert(dbSpu); | |
| 193 | 196 | |
| 194 | 197 | // 调用 |
| 195 | - productSpuService.deleteSpu(createReqVO.getId()); | |
| 198 | + productSpuService.deleteSpu(dbSpu.getId()); | |
| 199 | + // 断言 | |
| 200 | + assertNull(productSpuMapper.selectById(dbSpu.getId())); | |
| 201 | + verify(productSkuService).deleteSkuBySpuId(eq(dbSpu.getId())); | |
| 202 | + } | |
| 203 | + | |
| 204 | + @Test | |
| 205 | + public void testDeleteSpu_notRecycle() { | |
| 206 | + // mock 数据 | |
| 207 | + ProductSpuDO dbSpu = randomSpuDO(o -> o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus())); | |
| 208 | + productSpuMapper.insert(dbSpu); | |
| 196 | 209 | |
| 197 | - Assertions.assertNull(productSpuMapper.selectById(createReqVO.getId())); | |
| 210 | + // 调用, 并断言异常 | |
| 211 | + assertServiceException(() -> productSpuService.deleteSpu(dbSpu.getId()), SPU_NOT_RECYCLE); | |
| 212 | + // 断言 | |
| 213 | + assertNotNull(productSpuMapper.selectById(dbSpu.getId())); | |
| 214 | + verify(productSkuService, never()).deleteSkuBySpuId(any()); | |
| 198 | 215 | } |
| 199 | 216 | |
| 200 | 217 | @Test |
| 201 | - void getSpu() { | |
| 202 | - // 准备参数 | |
| 203 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | |
| 204 | - o.setCategoryId(generateId()); | |
| 205 | - o.setBrandId(generateId()); | |
| 206 | - o.setDeliveryTemplateId(generateId()); | |
| 207 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 208 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 209 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 210 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 211 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 212 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 213 | - o.setStock(generaInt()); // 限制范围为正整数 | |
| 214 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 215 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 216 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 217 | - }); | |
| 218 | - productSpuMapper.insert(createReqVO); | |
| 218 | + public void testGetSpu() { | |
| 219 | + // mock 数据 | |
| 220 | + ProductSpuDO dbSpu = randomSpuDO(); | |
| 221 | + productSpuMapper.insert(dbSpu); | |
| 219 | 222 | |
| 220 | - ProductSpuDO spu = productSpuService.getSpu(createReqVO.getId()); | |
| 221 | - assertPojoEquals(createReqVO, spu); | |
| 223 | + // 调用 | |
| 224 | + ProductSpuDO spu = productSpuService.getSpu(dbSpu.getId()); | |
| 225 | + // 断言 | |
| 226 | + assertPojoEquals(dbSpu, spu); | |
| 222 | 227 | } |
| 223 | 228 | |
| 224 | 229 | @Test |
| 225 | - void getSpuList() { | |
| 226 | - // 准备参数 | |
| 227 | - ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{ | |
| 228 | - o.setCategoryId(generateId()); | |
| 229 | - o.setBrandId(generateId()); | |
| 230 | - o.setDeliveryTemplateId(generateId()); | |
| 231 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 232 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 233 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 234 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 235 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 236 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 237 | - o.setStock(generaInt()); // 限制范围为正整数 | |
| 238 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 239 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 240 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 241 | - }), randomPojo(ProductSpuDO.class,o->{ | |
| 242 | - o.setCategoryId(generateId()); | |
| 243 | - o.setBrandId(generateId()); | |
| 244 | - o.setDeliveryTemplateId(generateId()); | |
| 245 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 246 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 247 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 248 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 249 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 250 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 251 | - o.setStock(generaInt()); // 限制范围为正整数 | |
| 252 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 253 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 254 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 255 | - })); | |
| 256 | - productSpuMapper.insertBatch(createReqVOs); | |
| 230 | + public void testGetSpuList() { | |
| 231 | + // mock 数据 | |
| 232 | + ArrayList<ProductSpuDO> dbSpus = Lists.newArrayList(randomSpuDO(), randomSpuDO()); | |
| 233 | + productSpuMapper.insertBatch(dbSpus); | |
| 234 | + // 准备参数:倒序传入 | |
| 235 | + List<Long> ids = Arrays.asList(dbSpus.get(1).getId(), dbSpus.get(0).getId()); | |
| 257 | 236 | |
| 258 | 237 | // 调用 |
| 259 | - List<ProductSpuDO> spuList = productSpuService.getSpuList(createReqVOs.stream().map(ProductSpuDO::getId).collect(Collectors.toList())); | |
| 260 | - Assertions.assertIterableEquals(createReqVOs, spuList); | |
| 238 | + List<ProductSpuDO> spuList = productSpuService.getSpuList(ids); | |
| 239 | + // 断言:按照 ids 顺序返回 | |
| 240 | + assertEquals(ids, convertList(spuList, ProductSpuDO::getId)); | |
| 241 | + assertPojoEquals(dbSpus.get(1), spuList.get(0)); | |
| 242 | + assertPojoEquals(dbSpus.get(0), spuList.get(1)); | |
| 261 | 243 | } |
| 262 | 244 | |
| 263 | 245 | @Test |
| 264 | - void getSpuPage_alarmStock_empty() { | |
| 246 | + public void testGetSpuPage_alertStockEmpty() { | |
| 247 | + // mock 数据 | |
| 248 | + ArrayList<ProductSpuDO> dbSpus = Lists.newArrayList(randomSpuDO(o -> o.setStock(11)), | |
| 249 | + randomSpuDO(o -> o.setStock(11))); | |
| 250 | + productSpuMapper.insertBatch(dbSpus); | |
| 265 | 251 | // 准备参数 |
| 266 | - ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{ | |
| 267 | - o.setCategoryId(generateId()); | |
| 268 | - o.setBrandId(generateId()); | |
| 269 | - o.setDeliveryTemplateId(generateId()); | |
| 270 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 271 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 272 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 273 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 274 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 275 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 276 | - o.setStock(11); // 限制范围为正整数 | |
| 277 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 278 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 279 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 280 | - }), randomPojo(ProductSpuDO.class,o->{ | |
| 281 | - o.setCategoryId(generateId()); | |
| 282 | - o.setBrandId(generateId()); | |
| 283 | - o.setDeliveryTemplateId(generateId()); | |
| 284 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 285 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 286 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 287 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 288 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 289 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 290 | - o.setStock(11); // 限制范围为正整数 | |
| 291 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 292 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 293 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 294 | - })); | |
| 295 | - productSpuMapper.insertBatch(createReqVOs); | |
| 296 | - // 调用 | |
| 297 | 252 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); |
| 298 | 253 | productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); |
| 299 | 254 | |
| 255 | + // 调用 | |
| 300 | 256 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); |
| 301 | - | |
| 302 | - PageResult<Object> result = PageResult.empty(); | |
| 303 | - Assertions.assertIterableEquals(result.getList(), spuPage.getList()); | |
| 304 | - assertEquals(spuPage.getTotal(), result.getTotal()); | |
| 257 | + // 断言 | |
| 258 | + assertEquals(0, spuPage.getTotal()); | |
| 259 | + assertEquals(0, spuPage.getList().size()); | |
| 305 | 260 | } |
| 306 | 261 | |
| 307 | 262 | @Test |
| 308 | - void getSpuPage_alarmStock() { | |
| 263 | + public void testGetSpuPage_alertStock() { | |
| 264 | + // mock 数据 | |
| 265 | + ArrayList<ProductSpuDO> dbSpus = Lists.newArrayList(randomSpuDO(o -> o.setStock(5)), | |
| 266 | + randomSpuDO(o -> o.setStock(9))); | |
| 267 | + productSpuMapper.insertBatch(dbSpus); | |
| 268 | + // 测试 stock 不匹配 | |
| 269 | + productSpuMapper.insert(randomSpuDO(o -> o.setStock(11))); | |
| 270 | + // 测试 status 不匹配(回收站) | |
| 271 | + productSpuMapper.insert(randomSpuDO(o -> o.setStock(5).setStatus(ProductSpuStatusEnum.RECYCLE.getStatus()))); | |
| 309 | 272 | // 准备参数 |
| 310 | - ArrayList<ProductSpuDO> createReqVOs = Lists.newArrayList(randomPojo(ProductSpuDO.class,o->{ | |
| 311 | - o.setCategoryId(generateId()); | |
| 312 | - o.setBrandId(generateId()); | |
| 313 | - o.setDeliveryTemplateId(generateId()); | |
| 314 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 315 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 316 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 317 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 318 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 319 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 320 | - o.setStock(5); // 限制范围为正整数 | |
| 321 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 322 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 323 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 324 | - }), randomPojo(ProductSpuDO.class,o->{ | |
| 325 | - o.setCategoryId(generateId()); | |
| 326 | - o.setBrandId(generateId()); | |
| 327 | - o.setDeliveryTemplateId(generateId()); | |
| 328 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 329 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 330 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 331 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 332 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 333 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 334 | - o.setStock(9); // 限制范围为正整数 | |
| 335 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 336 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 337 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 338 | - })); | |
| 339 | - productSpuMapper.insertBatch(createReqVOs); | |
| 340 | - // 调用 | |
| 341 | 273 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); |
| 342 | 274 | productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); |
| 275 | + | |
| 276 | + // 调用 | |
| 343 | 277 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); |
| 344 | - assertEquals(createReqVOs.size(), spuPage.getTotal()); | |
| 278 | + // 断言 | |
| 279 | + assertEquals(dbSpus.size(), spuPage.getTotal()); | |
| 280 | + assertEquals(convertSet(dbSpus, ProductSpuDO::getId), convertSet(spuPage.getList(), ProductSpuDO::getId)); | |
| 345 | 281 | } |
| 346 | 282 | |
| 347 | 283 | @Test |
| 348 | - void testGetSpuPage() { | |
| 349 | - // 准备参数 | |
| 350 | - ProductSpuDO createReqVO = randomPojo(ProductSpuDO.class,o->{ | |
| 351 | - o.setCategoryId(generateId()); | |
| 352 | - o.setBrandId(generateId()); | |
| 353 | - o.setDeliveryTemplateId(generateId()); | |
| 354 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 355 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 356 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 357 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 358 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 359 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 360 | - o.setStock(generaInt()); // 限制范围为正整数 | |
| 361 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 362 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 363 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 284 | + public void testGetSpuPage() { | |
| 285 | + // mock 数据 | |
| 286 | + ProductSpuDO dbSpu = randomSpuDO(o -> { // 等会查询到 | |
| 287 | + o.setName("芋道商品"); | |
| 288 | + o.setCategoryId(1L); | |
| 289 | + o.setStatus(ProductSpuStatusEnum.ENABLE.getStatus()); | |
| 290 | + o.setCreateTime(buildTime(2023, 2, 10)); | |
| 364 | 291 | }); |
| 365 | - | |
| 292 | + productSpuMapper.insert(dbSpu); | |
| 293 | + // 测试 name 不匹配 | |
| 294 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setName("土豆商品"))); | |
| 295 | + // 测试 categoryId 不匹配 | |
| 296 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCategoryId(2L))); | |
| 297 | + // 测试 createTime 不匹配 | |
| 298 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setCreateTime(buildTime(2023, 3, 10)))); | |
| 299 | + // 测试 status 不匹配(出售中) | |
| 300 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus()))); | |
| 301 | + productSpuMapper.insert(cloneIgnoreId(dbSpu, o -> o.setStatus(ProductSpuStatusEnum.RECYCLE.getStatus()))); | |
| 366 | 302 | // 准备参数 |
| 367 | - productSpuMapper.insert(createReqVO); | |
| 368 | - // 测试 status 不匹配 | |
| 369 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setStatus(ProductSpuStatusEnum.DISABLE.getStatus()))); | |
| 370 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setStatus(ProductSpuStatusEnum.RECYCLE.getStatus()))); | |
| 371 | - // 测试 SpecType 不匹配 | |
| 372 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setSpecType(true))); | |
| 373 | - // 测试 BrandId 不匹配 | |
| 374 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setBrandId(generateId()))); | |
| 375 | - // 测试 CategoryId 不匹配 | |
| 376 | - productSpuMapper.insert(cloneIgnoreId(createReqVO, o -> o.setCategoryId(generateId()))); | |
| 377 | - | |
| 378 | - // 调用 | |
| 379 | 303 | ProductSpuPageReqVO productSpuPageReqVO = new ProductSpuPageReqVO(); |
| 380 | - // 查询条件 按需打开 | |
| 381 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.ALERT_STOCK); | |
| 382 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.RECYCLE_BIN); | |
| 383 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.FOR_SALE); | |
| 384 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.IN_WAREHOUSE); | |
| 385 | - //productSpuPageReqVO.setTabType(ProductSpuPageReqVO.SOLD_OUT); | |
| 386 | - //productSpuPageReqVO.setName(createReqVO.getName()); | |
| 387 | - //productSpuPageReqVO.setCategoryId(createReqVO.getCategoryId()); | |
| 304 | + productSpuPageReqVO.setName("芋道"); | |
| 305 | + productSpuPageReqVO.setCategoryId(1L); | |
| 306 | + productSpuPageReqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | |
| 307 | + productSpuPageReqVO.setTabType(ProductSpuPageReqVO.FOR_SALE); | |
| 388 | 308 | |
| 309 | + // 调用 | |
| 389 | 310 | PageResult<ProductSpuDO> spuPage = productSpuService.getSpuPage(productSpuPageReqVO); |
| 390 | - | |
| 311 | + // 断言 | |
| 391 | 312 | assertEquals(1, spuPage.getTotal()); |
| 313 | + assertEquals(1, spuPage.getList().size()); | |
| 314 | + assertPojoEquals(dbSpu, spuPage.getList().get(0)); | |
| 392 | 315 | } |
| 393 | 316 | |
| 394 | 317 | /** |
| ... | ... | @@ -427,23 +350,49 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 427 | 350 | // 准备参数 |
| 428 | 351 | Map<Long, Integer> stockIncrCounts = MapUtil.builder(1L, 10).put(2L, -20).build(); |
| 429 | 352 | // mock 方法(数据) |
| 430 | - productSpuMapper.insert(randomPojo(ProductSpuDO.class, o ->{ | |
| 431 | - o.setCategoryId(generateId()); | |
| 432 | - o.setBrandId(generateId()); | |
| 433 | - o.setDeliveryTemplateId(generateId()); | |
| 434 | - o.setSort(RandomUtil.randomInt(1,100)); // 限制排序范围 | |
| 435 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 436 | - o.setVirtualSalesCount(generaInt()); // 限制范围为正整数 | |
| 437 | - o.setPrice(generaInt()); // 限制范围为正整数 | |
| 438 | - o.setMarketPrice(generaInt()); // 限制范围为正整数 | |
| 439 | - o.setCostPrice(generaInt()); // 限制范围为正整数 | |
| 440 | - o.setStock(generaInt()); // 限制范围为正整数 | |
| 441 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 442 | - o.setSalesCount(generaInt()); // 限制范围为正整数 | |
| 443 | - o.setBrowseCount(generaInt()); // 限制范围为正整数 | |
| 444 | - o.setId(1L).setStock(20); | |
| 445 | - })); | |
| 446 | - productSpuMapper.insert(randomPojo(ProductSpuDO.class, o -> { | |
| 353 | + productSpuMapper.insert(randomSpuDO(o -> o.setId(1L).setStock(20))); | |
| 354 | + productSpuMapper.insert(randomSpuDO(o -> o.setId(2L).setStock(30))); | |
| 355 | + | |
| 356 | + // 调用 | |
| 357 | + productSpuService.updateSpuStock(stockIncrCounts); | |
| 358 | + // 断言 | |
| 359 | + assertEquals(productSpuService.getSpu(1L).getStock(), 30); | |
| 360 | + assertEquals(productSpuService.getSpu(2L).getStock(), 10); | |
| 361 | + } | |
| 362 | + | |
| 363 | + /** | |
| 364 | + * 校验 {@link ProductSpuSaveReqVO#getDeliveryTypes()} 字段 | |
| 365 | + * | |
| 366 | + * @param deliveryTypes 配送方式数组 | |
| 367 | + * @return 校验不通过的提示 | |
| 368 | + */ | |
| 369 | + private static Set<String> validateDeliveryTypes(List<Integer> deliveryTypes) { | |
| 370 | + ProductSpuSaveReqVO reqVO = new ProductSpuSaveReqVO(); | |
| 371 | + reqVO.setDeliveryTypes(deliveryTypes); | |
| 372 | + Set<ConstraintViolation<ProductSpuSaveReqVO>> violations = VALIDATOR.validateProperty(reqVO, "deliveryTypes"); | |
| 373 | + return convertSet(violations, ConstraintViolation::getMessage); | |
| 374 | + } | |
| 375 | + | |
| 376 | + // ========== 随机对象 ========== | |
| 377 | + | |
| 378 | + private ProductSkuSaveReqVO randomSkuSaveReqVO() { | |
| 379 | + return randomPojo(ProductSkuSaveReqVO.class, o->{ | |
| 380 | + // 限制范围为正整数 | |
| 381 | + o.setCostPrice(generaInt()); | |
| 382 | + o.setPrice(generaInt()); | |
| 383 | + o.setMarketPrice(generaInt()); | |
| 384 | + o.setStock(generaInt()); | |
| 385 | + o.setFirstBrokeragePrice(generaInt()); | |
| 386 | + o.setSecondBrokeragePrice(generaInt()); | |
| 387 | + // 限制分数为两位数 | |
| 388 | + o.setWeight(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | |
| 389 | + o.setVolume(RandomUtil.randomDouble(10,2, RoundingMode.HALF_UP)); | |
| 390 | + }); | |
| 391 | + } | |
| 392 | + | |
| 393 | + @SafeVarargs | |
| 394 | + private final ProductSpuDO randomSpuDO(Consumer<ProductSpuDO>... consumers) { | |
| 395 | + Consumer<ProductSpuDO> consumer = o -> { | |
| 447 | 396 | o.setCategoryId(generateId()); |
| 448 | 397 | o.setBrandId(generateId()); |
| 449 | 398 | o.setDeliveryTemplateId(generateId()); |
| ... | ... | @@ -454,17 +403,10 @@ public class ProductSpuServiceImplTest extends BaseDbUnitTest { |
| 454 | 403 | o.setMarketPrice(generaInt()); // 限制范围为正整数 |
| 455 | 404 | o.setCostPrice(generaInt()); // 限制范围为正整数 |
| 456 | 405 | o.setStock(generaInt()); // 限制范围为正整数 |
| 457 | - o.setGiveIntegral(generaInt()); // 限制范围为正整数 | |
| 458 | 406 | o.setSalesCount(generaInt()); // 限制范围为正整数 |
| 459 | 407 | o.setBrowseCount(generaInt()); // 限制范围为正整数 |
| 460 | - o.setId(2L).setStock(30); | |
| 461 | - })); | |
| 462 | - | |
| 463 | - // 调用 | |
| 464 | - productSpuService.updateSpuStock(stockIncrCounts); | |
| 465 | - // 断言 | |
| 466 | - assertEquals(productSpuService.getSpu(1L).getStock(), 30); | |
| 467 | - assertEquals(productSpuService.getSpu(2L).getStock(), 10); | |
| 408 | + }; | |
| 409 | + return randomPojo(ProductSpuDO.class, ArrayUtils.append(consumer, consumers)); | |
| 468 | 410 | } |
| 469 | 411 | |
| 470 | 412 | } | ... | ... |
yudao-module-mall/yudao-module-product/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -28,13 +28,10 @@ CREATE TABLE IF NOT EXISTS `product_spu` ( |
| 28 | 28 | `keyword` varchar(256) NOT NULL COMMENT '关键字', |
| 29 | 29 | `introduction` varchar(256) NOT NULL COMMENT '商品简介', |
| 30 | 30 | `description` text NOT NULL COMMENT '商品详情', |
| 31 | - `bar_code` varchar(64) NOT NULL COMMENT '条形码', | |
| 32 | 31 | `category_id` bigint NOT NULL COMMENT '商品分类编号', |
| 33 | - `brand_id` int DEFAULT NULL COMMENT '商品品牌编号', | |
| 32 | + `brand_id` bigint DEFAULT NULL COMMENT '商品品牌编号', | |
| 34 | 33 | `pic_url` varchar(256) NOT NULL COMMENT '商品封面图', |
| 35 | 34 | `slider_pic_urls` varchar(2000) DEFAULT '' COMMENT '商品轮播图地址\n 数组,以逗号分隔\n 最多上传15张', |
| 36 | - `video_url` varchar(256) DEFAULT NULL COMMENT '商品视频', | |
| 37 | - `unit` tinyint NOT NULL COMMENT '单位', | |
| 38 | 35 | `sort` int NOT NULL DEFAULT '0' COMMENT '排序字段', |
| 39 | 36 | `status` tinyint NOT NULL COMMENT '商品状态: 0 上架(开启) 1 下架(禁用)-1 回收', |
| 40 | 37 | `spec_type` bit(1) NOT NULL COMMENT '规格类型:0 单规格 1 多规格', |
| ... | ... | @@ -42,16 +39,10 @@ CREATE TABLE IF NOT EXISTS `product_spu` ( |
| 42 | 39 | `market_price` int NOT NULL COMMENT '市场价,单位使用:分', |
| 43 | 40 | `cost_price` int NOT NULL DEFAULT '-1' COMMENT '成本价,单位: 分', |
| 44 | 41 | `stock` int NOT NULL DEFAULT '0' COMMENT '库存', |
| 42 | + `delivery_types` varchar(255) NOT NULL COMMENT '配送方式数组', | |
| 45 | 43 | `delivery_template_id` bigint NOT NULL COMMENT '物流配置模板编号', |
| 46 | - `recommend_hot` bit(1) NOT NULL COMMENT '是否热卖推荐: 0 默认 1 热卖', | |
| 47 | - `recommend_benefit` bit(1) NOT NULL COMMENT '是否优惠推荐: 0 默认 1 优选', | |
| 48 | - `recommend_best` bit(1) NOT NULL COMMENT '是否精品推荐: 0 默认 1 精品', | |
| 49 | - `recommend_new` bit(1) NOT NULL COMMENT '是否新品推荐: 0 默认 1 新品', | |
| 50 | - `recommend_good` bit(1) NOT NULL COMMENT '是否优品推荐', | |
| 51 | 44 | `give_integral` int NOT NULL COMMENT '赠送积分', |
| 52 | - `give_coupon_template_ids` varchar(512) DEFAULT '' COMMENT '赠送的优惠劵编号的数组', | |
| 53 | 45 | `sub_commission_type` bit(1) NOT NULL COMMENT '分销类型', |
| 54 | - `activity_orders` varchar(16) NOT NULL DEFAULT '' COMMENT '活动显示排序0=默认, 1=秒杀,2=砍价,3=拼团', | |
| 55 | 46 | `sales_count` int DEFAULT '0' COMMENT '商品销量', |
| 56 | 47 | `virtual_sales_count` int DEFAULT '0' COMMENT '虚拟销量', |
| 57 | 48 | `browse_count` int DEFAULT '0' COMMENT '商品点击量', | ... | ... |
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImpl.java
| ... | ... | @@ -102,7 +102,7 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { |
| 102 | 102 | } |
| 103 | 103 | // 删除 |
| 104 | 104 | diyTemplateMapper.deleteById(id); |
| 105 | - // 级联删除模板下自动生成的默认页面(createDiyTemplate 的「首页/我的」),避免页面列表残留孤儿页 | |
| 105 | + // 删除模板下的页面 | |
| 106 | 106 | diyPageService.getDiyPageByTemplateId(id) |
| 107 | 107 | .forEach(page -> diyPageService.deleteDiyPage(page.getId())); |
| 108 | 108 | } | ... | ... |
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImplTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.promotion.service.coupon; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 4 | +import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | |
| 5 | +import cn.iocoder.yudao.module.product.api.category.ProductCategoryApi; | |
| 6 | +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | |
| 7 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponDO; | |
| 8 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; | |
| 9 | +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponMapper; | |
| 10 | +import cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper; | |
| 11 | +import cn.iocoder.yudao.module.promotion.enums.coupon.CouponStatusEnum; | |
| 12 | +import jakarta.annotation.Resource; | |
| 13 | +import org.junit.jupiter.api.Test; | |
| 14 | +import org.springframework.context.annotation.Import; | |
| 15 | +import org.springframework.jdbc.core.JdbcTemplate; | |
| 16 | +import org.springframework.test.context.bean.override.mockito.MockitoBean; | |
| 17 | + | |
| 18 | +import javax.sql.DataSource; | |
| 19 | +import java.util.function.Consumer; | |
| 20 | + | |
| 21 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | |
| 22 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | |
| 23 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | |
| 24 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_DELETE_FAIL_USED; | |
| 25 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NOT_EXISTS; | |
| 26 | +import static org.junit.jupiter.api.Assertions.*; | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * {@link CouponServiceImpl} 的单元测试类 | |
| 30 | + * | |
| 31 | + * @author 芋道源码 | |
| 32 | + */ | |
| 33 | +@Import({CouponServiceImpl.class, CouponTemplateServiceImpl.class}) | |
| 34 | +public class CouponServiceImplTest extends BaseDbUnitTest { | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private CouponServiceImpl couponService; | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + private CouponMapper couponMapper; | |
| 41 | + @Resource | |
| 42 | + private CouponTemplateMapper couponTemplateMapper; | |
| 43 | + | |
| 44 | + @Resource | |
| 45 | + private DataSource dataSource; | |
| 46 | + | |
| 47 | + @MockitoBean | |
| 48 | + private MemberUserApi memberUserApi; | |
| 49 | + @MockitoBean | |
| 50 | + private ProductCategoryApi productCategoryApi; | |
| 51 | + @MockitoBean | |
| 52 | + private ProductSpuApi productSpuApi; | |
| 53 | + | |
| 54 | + @Test | |
| 55 | + public void testDeleteCoupon_success() { | |
| 56 | + // mock 数据 | |
| 57 | + CouponTemplateDO dbTemplate = randomCouponTemplateDO(o -> o.setTakeCount(5)); | |
| 58 | + couponTemplateMapper.insert(dbTemplate); | |
| 59 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(dbTemplate.getId()) | |
| 60 | + .setStatus(CouponStatusEnum.UNUSED.getStatus())); | |
| 61 | + couponMapper.insert(dbCoupon); | |
| 62 | + | |
| 63 | + // 调用 | |
| 64 | + couponService.deleteCoupon(dbCoupon.getId()); | |
| 65 | + // 断言:优惠劵被删除,模板领取数量 -1 | |
| 66 | + assertNull(couponMapper.selectById(dbCoupon.getId())); | |
| 67 | + assertEquals(4, couponTemplateMapper.selectById(dbTemplate.getId()).getTakeCount()); | |
| 68 | + } | |
| 69 | + | |
| 70 | + @Test | |
| 71 | + public void testDeleteCoupon_templateDeleted() { | |
| 72 | + // mock 数据(模板已删除) | |
| 73 | + CouponTemplateDO dbTemplate = randomCouponTemplateDO(o -> o.setTakeCount(5)); | |
| 74 | + couponTemplateMapper.insert(dbTemplate); | |
| 75 | + couponTemplateMapper.deleteById(dbTemplate.getId()); | |
| 76 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(dbTemplate.getId()) | |
| 77 | + .setStatus(CouponStatusEnum.UNUSED.getStatus())); | |
| 78 | + couponMapper.insert(dbCoupon); | |
| 79 | + | |
| 80 | + // 调用 | |
| 81 | + couponService.deleteCoupon(dbCoupon.getId()); | |
| 82 | + // 断言:优惠劵被删除,已删除模板的领取数量不变 | |
| 83 | + assertNull(couponMapper.selectById(dbCoupon.getId())); | |
| 84 | + assertEquals(5, selectTemplateTakeCount(dbTemplate.getId())); | |
| 85 | + } | |
| 86 | + | |
| 87 | + @Test | |
| 88 | + public void testDeleteCoupon_templateNotExists() { | |
| 89 | + // mock 数据(模板不存在) | |
| 90 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(randomLongId()) | |
| 91 | + .setStatus(CouponStatusEnum.EXPIRE.getStatus())); | |
| 92 | + couponMapper.insert(dbCoupon); | |
| 93 | + | |
| 94 | + // 调用 | |
| 95 | + couponService.deleteCoupon(dbCoupon.getId()); | |
| 96 | + // 断言:优惠劵被删除 | |
| 97 | + assertNull(couponMapper.selectById(dbCoupon.getId())); | |
| 98 | + } | |
| 99 | + | |
| 100 | + @Test | |
| 101 | + public void testDeleteCoupon_used() { | |
| 102 | + // mock 数据 | |
| 103 | + CouponTemplateDO dbTemplate = randomCouponTemplateDO(o -> o.setTakeCount(5)); | |
| 104 | + couponTemplateMapper.insert(dbTemplate); | |
| 105 | + CouponDO dbCoupon = randomCouponDO(o -> o.setTemplateId(dbTemplate.getId()) | |
| 106 | + .setStatus(CouponStatusEnum.USED.getStatus())); | |
| 107 | + couponMapper.insert(dbCoupon); | |
| 108 | + | |
| 109 | + // 调用, 并断言异常 | |
| 110 | + assertServiceException(() -> couponService.deleteCoupon(dbCoupon.getId()), COUPON_DELETE_FAIL_USED); | |
| 111 | + // 断言:优惠劵未删除,模板领取数量不变 | |
| 112 | + CouponDO coupon = couponMapper.selectById(dbCoupon.getId()); | |
| 113 | + assertNotNull(coupon); | |
| 114 | + assertEquals(CouponStatusEnum.USED.getStatus(), coupon.getStatus()); | |
| 115 | + assertEquals(5, couponTemplateMapper.selectById(dbTemplate.getId()).getTakeCount()); | |
| 116 | + } | |
| 117 | + | |
| 118 | + @Test | |
| 119 | + public void testDeleteCoupon_notExists() { | |
| 120 | + // 准备参数 | |
| 121 | + Long id = randomLongId(); | |
| 122 | + | |
| 123 | + // 调用, 并断言异常 | |
| 124 | + assertServiceException(() -> couponService.deleteCoupon(id), COUPON_NOT_EXISTS); | |
| 125 | + } | |
| 126 | + | |
| 127 | + /** | |
| 128 | + * 查询模板的领取数量,包括已删除的模板 | |
| 129 | + * | |
| 130 | + * @param id 模板编号 | |
| 131 | + * @return 领取数量 | |
| 132 | + */ | |
| 133 | + private Integer selectTemplateTakeCount(Long id) { | |
| 134 | + return new JdbcTemplate(dataSource).queryForObject( | |
| 135 | + "SELECT take_count FROM promotion_coupon_template WHERE id = ?", Integer.class, id); | |
| 136 | + } | |
| 137 | + | |
| 138 | + // ========== 随机对象 ========== | |
| 139 | + | |
| 140 | + @SafeVarargs | |
| 141 | + private static CouponTemplateDO randomCouponTemplateDO(Consumer<CouponTemplateDO>... consumers) { | |
| 142 | + return randomPojo(CouponTemplateDO.class, consumers); | |
| 143 | + } | |
| 144 | + | |
| 145 | + @SafeVarargs | |
| 146 | + private static CouponDO randomCouponDO(Consumer<CouponDO>... consumers) { | |
| 147 | + return randomPojo(CouponDO.class, consumers); | |
| 148 | + } | |
| 149 | + | |
| 150 | +} | ... | ... |
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImplTest.java
| ... | ... | @@ -7,18 +7,19 @@ import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPagePag |
| 7 | 7 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.page.DiyPageUpdateReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO; |
| 9 | 9 | import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper; |
| 10 | -import org.junit.jupiter.api.Disabled; | |
| 11 | 10 | import org.junit.jupiter.api.Test; |
| 12 | 11 | import org.springframework.context.annotation.Import; |
| 13 | 12 | |
| 14 | 13 | import jakarta.annotation.Resource; |
| 15 | 14 | |
| 16 | 15 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; |
| 16 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | |
| 17 | 17 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 18 | 18 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 19 | 19 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| 20 | 20 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
| 21 | 21 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 22 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NAME_USED; | |
| 22 | 23 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_PAGE_NOT_EXISTS; |
| 23 | 24 | import static org.junit.jupiter.api.Assertions.*; |
| 24 | 25 | |
| ... | ... | @@ -27,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.*; |
| 27 | 28 | * |
| 28 | 29 | * @author owen |
| 29 | 30 | */ |
| 30 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | |
| 31 | +// DONE 芋艿:后续 fix 补充的单测 | |
| 31 | 32 | @Import(DiyPageServiceImpl.class) |
| 32 | 33 | public class DiyPageServiceImplTest extends BaseDbUnitTest { |
| 33 | 34 | |
| ... | ... | @@ -49,6 +50,21 @@ public class DiyPageServiceImplTest extends BaseDbUnitTest { |
| 49 | 50 | // 校验记录的属性是否正确 |
| 50 | 51 | DiyPageDO diyPage = diyPageMapper.selectById(diyPageId); |
| 51 | 52 | assertPojoEquals(reqVO, diyPage); |
| 53 | + assertEquals("{}", diyPage.getProperty()); | |
| 54 | + } | |
| 55 | + | |
| 56 | + @Test | |
| 57 | + public void testCreateDiyPage_nameUsed() { | |
| 58 | + // mock 数据(独立页面) | |
| 59 | + diyPageMapper.insert(randomPojo(DiyPageDO.class, o -> o.setTemplateId(null).setName("芋道首页"))); | |
| 60 | + // 准备参数 | |
| 61 | + DiyPageCreateReqVO reqVO = randomPojo(DiyPageCreateReqVO.class, o -> { | |
| 62 | + o.setTemplateId(null); | |
| 63 | + o.setName("芋道首页"); | |
| 64 | + }); | |
| 65 | + | |
| 66 | + // 调用, 并断言异常 | |
| 67 | + assertServiceException(() -> diyPageService.createDiyPage(reqVO), DIY_PAGE_NAME_USED, "芋道首页"); | |
| 52 | 68 | } |
| 53 | 69 | |
| 54 | 70 | @Test |
| ... | ... | @@ -101,21 +117,24 @@ public class DiyPageServiceImplTest extends BaseDbUnitTest { |
| 101 | 117 | } |
| 102 | 118 | |
| 103 | 119 | @Test |
| 104 | - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 120 | + // DONE 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 105 | 121 | public void testGetDiyPagePage() { |
| 106 | 122 | // mock 数据 |
| 107 | 123 | DiyPageDO dbDiyPage = randomPojo(DiyPageDO.class, o -> { // 等会查询到 |
| 108 | - o.setName(null); | |
| 109 | - o.setCreateTime(null); | |
| 124 | + o.setTemplateId(null); // 独立页面 | |
| 125 | + o.setName("芋道首页"); | |
| 126 | + o.setCreateTime(buildTime(2023, 2, 10)); | |
| 110 | 127 | }); |
| 111 | 128 | diyPageMapper.insert(dbDiyPage); |
| 112 | 129 | // 测试 name 不匹配 |
| 113 | - diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setName(null))); | |
| 130 | + diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setName("土豆首页"))); | |
| 114 | 131 | // 测试 createTime 不匹配 |
| 115 | - diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setCreateTime(null))); | |
| 132 | + diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setCreateTime(buildTime(2023, 3, 10)))); | |
| 133 | + // 测试 templateId 不匹配(模板下的页面,不在独立页面列表) | |
| 134 | + diyPageMapper.insert(cloneIgnoreId(dbDiyPage, o -> o.setTemplateId(1L))); | |
| 116 | 135 | // 准备参数 |
| 117 | 136 | DiyPagePageReqVO reqVO = new DiyPagePageReqVO(); |
| 118 | - reqVO.setName(null); | |
| 137 | + reqVO.setName("芋道"); | |
| 119 | 138 | reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
| 120 | 139 | |
| 121 | 140 | // 调用 | ... | ... |
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImplTest.java
| ... | ... | @@ -5,30 +5,40 @@ import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 5 | 5 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateCreateReqVO; |
| 6 | 6 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplatePageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template.DiyTemplateUpdateReqVO; |
| 8 | +import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyPageDO; | |
| 8 | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.diy.DiyTemplateDO; |
| 10 | +import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyPageMapper; | |
| 9 | 11 | import cn.iocoder.yudao.module.promotion.dal.mysql.diy.DiyTemplateMapper; |
| 10 | -import org.junit.jupiter.api.Disabled; | |
| 12 | +import jakarta.annotation.Resource; | |
| 11 | 13 | import org.junit.jupiter.api.Test; |
| 12 | 14 | import org.springframework.context.annotation.Import; |
| 15 | +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; | |
| 13 | 16 | |
| 14 | -import jakarta.annotation.Resource; | |
| 17 | +import java.util.List; | |
| 18 | +import java.util.function.Consumer; | |
| 15 | 19 | |
| 20 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 21 | +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | |
| 16 | 22 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; |
| 23 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | |
| 17 | 24 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; |
| 18 | 25 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 19 | 26 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| 20 | 27 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
| 21 | 28 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 22 | 29 | import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_NOT_EXISTS; |
| 30 | +import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.DIY_TEMPLATE_USED_CANNOT_DELETE; | |
| 23 | 31 | import static org.junit.jupiter.api.Assertions.*; |
| 32 | +import static org.mockito.ArgumentMatchers.anyLong; | |
| 33 | +import static org.mockito.Mockito.doCallRealMethod; | |
| 24 | 34 | |
| 25 | 35 | /** |
| 26 | 36 | * {@link DiyTemplateServiceImpl} 的单元测试类 |
| 27 | 37 | * |
| 28 | 38 | * @author owen |
| 29 | 39 | */ |
| 30 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | |
| 31 | -@Import(DiyTemplateServiceImpl.class) | |
| 40 | +// DONE 芋艿:后续 fix 补充的单测 | |
| 41 | +@Import({DiyTemplateServiceImpl.class, DiyPageServiceImpl.class}) | |
| 32 | 42 | public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 33 | 43 | |
| 34 | 44 | @Resource |
| ... | ... | @@ -36,6 +46,11 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 36 | 46 | |
| 37 | 47 | @Resource |
| 38 | 48 | private DiyTemplateMapper diyTemplateMapper; |
| 49 | + @Resource | |
| 50 | + private DiyPageMapper diyPageMapper; | |
| 51 | + | |
| 52 | + @MockitoSpyBean | |
| 53 | + private DiyPageService diyPageService; | |
| 39 | 54 | |
| 40 | 55 | @Test |
| 41 | 56 | public void testCreateDiyTemplate_success() { |
| ... | ... | @@ -49,6 +64,15 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 49 | 64 | // 校验记录的属性是否正确 |
| 50 | 65 | DiyTemplateDO diyTemplate = diyTemplateMapper.selectById(diyTemplateId); |
| 51 | 66 | assertPojoEquals(reqVO, diyTemplate); |
| 67 | + assertEquals("{}", diyTemplate.getProperty()); | |
| 68 | + // 校验默认页面:首页、我的 | |
| 69 | + List<DiyPageDO> pages = diyPageMapper.selectListByTemplateId(diyTemplateId); | |
| 70 | + assertEquals(asSet("首页", "我的"), convertSet(pages, DiyPageDO::getName)); | |
| 71 | + String remark = String.format("模板【%s】自动创建", reqVO.getName()); | |
| 72 | + pages.forEach(page -> { | |
| 73 | + assertEquals(remark, page.getRemark()); | |
| 74 | + assertEquals("{}", page.getProperty()); | |
| 75 | + }); | |
| 52 | 76 | } |
| 53 | 77 | |
| 54 | 78 | @Test |
| ... | ... | @@ -79,9 +103,20 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 79 | 103 | |
| 80 | 104 | @Test |
| 81 | 105 | public void testDeleteDiyTemplate_success() { |
| 82 | - // mock 数据 | |
| 83 | - DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class); | |
| 106 | + // mock 数据(模板) | |
| 107 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(false)); | |
| 84 | 108 | diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据 |
| 109 | + DiyTemplateDO otherDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(false)); | |
| 110 | + diyTemplateMapper.insert(otherDiyTemplate); | |
| 111 | + // mock 数据(页面):该模板下的全部页面,包括默认页面及后续新增页面 | |
| 112 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()).setName("首页"))); | |
| 113 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()).setName("我的"))); | |
| 114 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()).setName("活动页"))); | |
| 115 | + // mock 数据(页面):其它模板下的页面、独立页面 | |
| 116 | + DiyPageDO otherTemplatePage = randomDiyPageDO(o -> o.setTemplateId(otherDiyTemplate.getId())); | |
| 117 | + diyPageMapper.insert(otherTemplatePage); | |
| 118 | + DiyPageDO standalonePage = randomDiyPageDO(o -> o.setTemplateId(null)); | |
| 119 | + diyPageMapper.insert(standalonePage); | |
| 85 | 120 | // 准备参数 |
| 86 | 121 | Long id = dbDiyTemplate.getId(); |
| 87 | 122 | |
| ... | ... | @@ -89,6 +124,11 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 89 | 124 | diyTemplateService.deleteDiyTemplate(id); |
| 90 | 125 | // 校验数据不存在了 |
| 91 | 126 | assertNull(diyTemplateMapper.selectById(id)); |
| 127 | + assertEquals(0, diyPageMapper.selectListByTemplateId(id).size()); | |
| 128 | + // 校验其它模板、其它模板下的页面、独立页面保留 | |
| 129 | + assertNotNull(diyTemplateMapper.selectById(otherDiyTemplate.getId())); | |
| 130 | + assertNotNull(diyPageMapper.selectById(otherTemplatePage.getId())); | |
| 131 | + assertNotNull(diyPageMapper.selectById(standalonePage.getId())); | |
| 92 | 132 | } |
| 93 | 133 | |
| 94 | 134 | @Test |
| ... | ... | @@ -101,37 +141,65 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 101 | 141 | } |
| 102 | 142 | |
| 103 | 143 | @Test |
| 104 | - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 144 | + public void testDeleteDiyTemplate_used() { | |
| 145 | + // mock 数据 | |
| 146 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(true)); | |
| 147 | + diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据 | |
| 148 | + DiyPageDO dbDiyPage = randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId())); | |
| 149 | + diyPageMapper.insert(dbDiyPage); | |
| 150 | + // 准备参数 | |
| 151 | + Long id = dbDiyTemplate.getId(); | |
| 152 | + | |
| 153 | + // 调用, 并断言异常 | |
| 154 | + assertServiceException(() -> diyTemplateService.deleteDiyTemplate(id), DIY_TEMPLATE_USED_CANNOT_DELETE); | |
| 155 | + // 校验模板、页面都未删除 | |
| 156 | + assertNotNull(diyTemplateMapper.selectById(id)); | |
| 157 | + assertNotNull(diyPageMapper.selectById(dbDiyPage.getId())); | |
| 158 | + } | |
| 159 | + | |
| 160 | + @Test | |
| 161 | + public void testDeleteDiyTemplate_deletePageFailRollback() { | |
| 162 | + // mock 数据 | |
| 163 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(false)); | |
| 164 | + diyTemplateMapper.insert(dbDiyTemplate);// @Sql: 先插入出一条存在的数据 | |
| 165 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()))); | |
| 166 | + diyPageMapper.insert(randomDiyPageDO(o -> o.setTemplateId(dbDiyTemplate.getId()))); | |
| 167 | + // 准备参数 | |
| 168 | + Long id = dbDiyTemplate.getId(); | |
| 169 | + // mock 方法(第 1 个页面真实删除,第 2 个页面删除失败) | |
| 170 | + doCallRealMethod().doThrow(new IllegalStateException("删除页面失败")) | |
| 171 | + .when(diyPageService).deleteDiyPage(anyLong()); | |
| 172 | + | |
| 173 | + // 调用, 并断言异常 | |
| 174 | + assertThrows(IllegalStateException.class, () -> diyTemplateService.deleteDiyTemplate(id)); | |
| 175 | + // 校验模板、页面都已回滚 | |
| 176 | + assertNotNull(diyTemplateMapper.selectById(id)); | |
| 177 | + assertEquals(2, diyPageMapper.selectListByTemplateId(id).size()); | |
| 178 | + } | |
| 179 | + | |
| 180 | + @Test | |
| 181 | + // DONE 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 105 | 182 | public void testGetDiyTemplatePage() { |
| 106 | 183 | // mock 数据 |
| 107 | 184 | DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> { // 等会查询到 |
| 108 | - o.setName(null); | |
| 109 | - o.setUsed(null); | |
| 110 | - o.setUsedTime(null); | |
| 111 | - o.setRemark(null); | |
| 112 | - o.setPreviewPicUrls(null); | |
| 113 | - o.setProperty(null); | |
| 114 | - o.setCreateTime(null); | |
| 185 | + o.setName("芋道商城"); | |
| 186 | + o.setUsed(true); | |
| 187 | + o.setUsedTime(buildTime(2023, 2, 10)); | |
| 188 | + o.setCreateTime(buildTime(2023, 2, 10)); | |
| 115 | 189 | }); |
| 116 | 190 | diyTemplateMapper.insert(dbDiyTemplate); |
| 117 | 191 | // 测试 name 不匹配 |
| 118 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName(null))); | |
| 192 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName("土豆商城"))); | |
| 119 | 193 | // 测试 used 不匹配 |
| 120 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(null))); | |
| 194 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(false))); | |
| 121 | 195 | // 测试 usedTime 不匹配 |
| 122 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(null))); | |
| 123 | - // 测试 remark 不匹配 | |
| 124 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setRemark(null))); | |
| 125 | - // 测试 previewPicUrls 不匹配 | |
| 126 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setPreviewPicUrls(null))); | |
| 127 | - // 测试 property 不匹配 | |
| 128 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setProperty(null))); | |
| 196 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(buildTime(2023, 3, 10)))); | |
| 129 | 197 | // 测试 createTime 不匹配 |
| 130 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(null))); | |
| 198 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(buildTime(2023, 3, 10)))); | |
| 131 | 199 | // 准备参数 |
| 132 | 200 | DiyTemplatePageReqVO reqVO = new DiyTemplatePageReqVO(); |
| 133 | - reqVO.setName(null); | |
| 134 | - reqVO.setUsed(null); | |
| 201 | + reqVO.setName("芋道"); | |
| 202 | + reqVO.setUsed(true); | |
| 135 | 203 | reqVO.setUsedTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
| 136 | 204 | reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); |
| 137 | 205 | |
| ... | ... | @@ -144,33 +212,25 @@ public class DiyTemplateServiceImplTest extends BaseDbUnitTest { |
| 144 | 212 | } |
| 145 | 213 | |
| 146 | 214 | @Test |
| 147 | - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 | |
| 148 | - public void testGetDiyTemplateList() { | |
| 215 | + // DONE 请修改 null 为需要的值,然后删除 @Disabled 注解(DiyTemplateService 无列表查询方法,原 testGetDiyTemplateList 改为测试 getUsedDiyTemplate) | |
| 216 | + public void testGetUsedDiyTemplate() { | |
| 149 | 217 | // mock 数据 |
| 150 | - DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> { // 等会查询到 | |
| 151 | - o.setName(null); | |
| 152 | - o.setUsed(null); | |
| 153 | - o.setUsedTime(null); | |
| 154 | - o.setRemark(null); | |
| 155 | - o.setPreviewPicUrls(null); | |
| 156 | - o.setProperty(null); | |
| 157 | - o.setCreateTime(null); | |
| 158 | - }); | |
| 218 | + DiyTemplateDO dbDiyTemplate = randomPojo(DiyTemplateDO.class, o -> o.setUsed(true)); | |
| 159 | 219 | diyTemplateMapper.insert(dbDiyTemplate); |
| 160 | - // 测试 name 不匹配 | |
| 161 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName(null))); | |
| 162 | 220 | // 测试 used 不匹配 |
| 163 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsed(null))); | |
| 164 | - // 测试 usedTime 不匹配 | |
| 165 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setUsedTime(null))); | |
| 166 | - // 测试 remark 不匹配 | |
| 167 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setRemark(null))); | |
| 168 | - // 测试 previewPicUrls 不匹配 | |
| 169 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setPreviewPicUrls(null))); | |
| 170 | - // 测试 property 不匹配 | |
| 171 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setProperty(null))); | |
| 172 | - // 测试 createTime 不匹配 | |
| 173 | - diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setCreateTime(null))); | |
| 221 | + diyTemplateMapper.insert(cloneIgnoreId(dbDiyTemplate, o -> o.setName("土豆商城").setUsed(false))); | |
| 222 | + | |
| 223 | + // 调用 | |
| 224 | + DiyTemplateDO usedDiyTemplate = diyTemplateService.getUsedDiyTemplate(); | |
| 225 | + // 断言 | |
| 226 | + assertPojoEquals(dbDiyTemplate, usedDiyTemplate); | |
| 227 | + } | |
| 228 | + | |
| 229 | + // ========== 随机对象 ========== | |
| 230 | + | |
| 231 | + @SafeVarargs | |
| 232 | + private static DiyPageDO randomDiyPageDO(Consumer<DiyPageDO>... consumers) { | |
| 233 | + return randomPojo(DiyPageDO.class, consumers); | |
| 174 | 234 | } |
| 175 | 235 | |
| 176 | 236 | } | ... | ... |
yudao-module-mall/yudao-module-promotion/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -277,8 +277,8 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_template" |
| 277 | 277 | ( |
| 278 | 278 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 279 | 279 | "name" varchar NOT NULL, |
| 280 | - "used" bit NOT NULL, | |
| 281 | - "used_time" varchar, | |
| 280 | + "used" bit NOT NULL DEFAULT FALSE, | |
| 281 | + "used_time" datetime, | |
| 282 | 282 | "remark" varchar, |
| 283 | 283 | "preview_pic_urls" varchar, |
| 284 | 284 | "property" varchar NOT NULL, |
| ... | ... | @@ -293,7 +293,7 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_template" |
| 293 | 293 | CREATE TABLE IF NOT EXISTS "promotion_diy_page" |
| 294 | 294 | ( |
| 295 | 295 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 296 | - "template_id" bigint NOT NULL, | |
| 296 | + "template_id" bigint, | |
| 297 | 297 | "name" varchar NOT NULL, |
| 298 | 298 | "remark" varchar, |
| 299 | 299 | "preview_pic_urls" varchar, |
| ... | ... | @@ -303,6 +303,6 @@ CREATE TABLE IF NOT EXISTS "promotion_diy_page" |
| 303 | 303 | "updater" varchar DEFAULT '', |
| 304 | 304 | "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| 305 | 305 | "deleted" bit NOT NULL DEFAULT FALSE, |
| 306 | - "tenant_id" bigint NOT NULL, | |
| 306 | + "tenant_id" bigint NOT NULL DEFAULT 0, | |
| 307 | 307 | PRIMARY KEY ("id") |
| 308 | 308 | ) COMMENT '装修页面'; | ... | ... |
yudao-module-mall/yudao-module-trade/src/test/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceTest.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.service.order; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.util.IdUtil; |
| 4 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 4 | +import cn.iocoder.yudao.framework.common.enums.TerminalEnum; | |
| 5 | 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 6 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | |
| 6 | 7 | import cn.iocoder.yudao.module.member.api.address.MemberAddressApi; |
| 7 | -import cn.iocoder.yudao.module.member.api.user.MemberUserApi; | |
| 8 | +import cn.iocoder.yudao.module.member.api.address.dto.MemberAddressRespDTO; | |
| 8 | 9 | import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; |
| 9 | 10 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; |
| 11 | +import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; | |
| 10 | 12 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 11 | 13 | import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; |
| 12 | -import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi; | |
| 13 | -import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; | |
| 14 | -import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi; | |
| 15 | -import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; | |
| 16 | -import cn.iocoder.yudao.module.trade.controller.admin.delivery.vo.express.DeliveryExpressCreateReqVO; | |
| 14 | +import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; | |
| 15 | +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; | |
| 17 | 16 | import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; |
| 17 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO; | |
| 18 | +import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO; | |
| 19 | +import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO; | |
| 20 | +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; | |
| 18 | 21 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 22 | +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; | |
| 19 | 23 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; |
| 20 | 24 | import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; |
| 21 | 25 | import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO; |
| 22 | 26 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 27 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum; | |
| 23 | 28 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderRefundStatusEnum; |
| 24 | 29 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; |
| 25 | -import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderConfig; | |
| 30 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 26 | 31 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 27 | -import cn.iocoder.yudao.module.trade.service.cart.CartServiceImpl; | |
| 32 | +import cn.iocoder.yudao.module.trade.framework.order.core.aop.TradeOrderLogAspect; | |
| 33 | +import cn.iocoder.yudao.module.trade.service.cart.CartService; | |
| 28 | 34 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; |
| 29 | -import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressServiceImpl; | |
| 30 | -import cn.iocoder.yudao.module.trade.service.message.TradeMessageServiceImpl; | |
| 35 | +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService; | |
| 36 | +import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; | |
| 31 | 37 | import cn.iocoder.yudao.module.trade.service.order.handler.TradeOrderHandler; |
| 32 | -import cn.iocoder.yudao.module.trade.service.price.TradePriceServiceImpl; | |
| 33 | -import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator; | |
| 38 | +import cn.iocoder.yudao.module.trade.service.price.TradePriceService; | |
| 39 | +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; | |
| 34 | 40 | import jakarta.annotation.Resource; |
| 41 | +import org.junit.jupiter.api.AfterEach; | |
| 35 | 42 | import org.junit.jupiter.api.BeforeEach; |
| 36 | -import org.junit.jupiter.api.Disabled; | |
| 37 | 43 | import org.junit.jupiter.api.Test; |
| 38 | 44 | import org.springframework.context.annotation.Import; |
| 45 | +import org.springframework.mock.web.MockHttpServletRequest; | |
| 39 | 46 | import org.springframework.test.context.bean.override.mockito.MockitoBean; |
| 47 | +import org.springframework.test.util.ReflectionTestUtils; | |
| 48 | +import org.springframework.web.context.request.RequestContextHolder; | |
| 49 | +import org.springframework.web.context.request.ServletRequestAttributes; | |
| 40 | 50 | |
| 41 | 51 | import java.time.Duration; |
| 52 | +import java.util.Arrays; | |
| 53 | +import java.util.List; | |
| 42 | 54 | |
| 55 | +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | |
| 43 | 56 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 44 | 57 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 45 | 58 | import static org.junit.jupiter.api.Assertions.*; |
| 46 | -import static org.mockito.ArgumentMatchers.anyString; | |
| 47 | -import static org.mockito.ArgumentMatchers.eq; | |
| 59 | +import static org.mockito.ArgumentMatchers.*; | |
| 60 | +import static org.mockito.Mockito.verify; | |
| 48 | 61 | import static org.mockito.Mockito.when; |
| 49 | 62 | |
| 50 | 63 | /** |
| ... | ... | @@ -53,10 +66,8 @@ import static org.mockito.Mockito.when; |
| 53 | 66 | * @author LeeYan9 |
| 54 | 67 | * @since 2022-09-07 |
| 55 | 68 | */ |
| 56 | -@Disabled // TODO 芋艿:后续 fix 补充的单测 | |
| 57 | -@Import({TradeOrderUpdateServiceImpl.class, TradeOrderConfig.class, CartServiceImpl.class, TradePriceServiceImpl.class, | |
| 58 | - DeliveryExpressServiceImpl.class, TradeMessageServiceImpl.class | |
| 59 | -}) | |
| 69 | +// DONE 芋艿:后续 fix 补充的单测 | |
| 70 | +@Import(TradeOrderUpdateServiceImpl.class) | |
| 60 | 71 | public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 61 | 72 | |
| 62 | 73 | @Resource |
| ... | ... | @@ -68,34 +79,33 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 68 | 79 | private TradeOrderItemMapper tradeOrderItemMapper; |
| 69 | 80 | |
| 70 | 81 | @MockitoBean |
| 71 | - private MemberUserApi memberUserApi; | |
| 82 | + private TradeNoRedisDAO tradeNoRedisDAO; | |
| 72 | 83 | @MockitoBean |
| 73 | - private ProductSpuApi productSpuApi; | |
| 84 | + private TradeOrderHandler tradeOrderHandler; | |
| 74 | 85 | @MockitoBean |
| 75 | - private ProductSkuApi productSkuApi; | |
| 86 | + private CartService cartService; | |
| 76 | 87 | @MockitoBean |
| 77 | - private ProductCommentApi productCommentApi; | |
| 78 | - // @MockitoBean | |
| 79 | -// private PriceApi priceApi; | |
| 88 | + private TradePriceService tradePriceService; | |
| 80 | 89 | @MockitoBean |
| 81 | - private PayOrderApi payOrderApi; | |
| 90 | + private DeliveryExpressService deliveryExpressService; | |
| 82 | 91 | @MockitoBean |
| 83 | - private MemberAddressApi addressApi; | |
| 92 | + private TradeMessageService tradeMessageService; | |
| 84 | 93 | @MockitoBean |
| 85 | - private CouponApi couponApi; | |
| 86 | - | |
| 94 | + private DeliveryPickUpStoreService pickUpStoreService; | |
| 87 | 95 | @MockitoBean |
| 88 | - private TradeOrderProperties tradeOrderProperties; | |
| 96 | + private PayOrderApi payOrderApi; | |
| 89 | 97 | @MockitoBean |
| 90 | - private TradeNoRedisDAO tradeNoRedisDAO; | |
| 98 | + private MemberAddressApi addressApi; | |
| 91 | 99 | @MockitoBean |
| 92 | - private TradeOrderHandler tradeOrderHandler; | |
| 100 | + private ProductCommentApi productCommentApi; | |
| 93 | 101 | @MockitoBean |
| 94 | - private TradePriceCalculator tradePriceCalculator; | |
| 102 | + private SocialClientApi socialClientApi; | |
| 95 | 103 | @MockitoBean |
| 96 | - private NotifyMessageSendApi notifyMessageSendApi; | |
| 104 | + private PayRefundApi payRefundApi; | |
| 97 | 105 | @MockitoBean |
| 98 | - private DeliveryExpressService deliveryExpressService; | |
| 106 | + private CombinationRecordApi combinationRecordApi; | |
| 107 | + @MockitoBean | |
| 108 | + private TradeOrderProperties tradeOrderProperties; | |
| 99 | 109 | |
| 100 | 110 | @BeforeEach |
| 101 | 111 | public void setUp() { |
| ... | ... | @@ -104,159 +114,134 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 104 | 114 | when(tradeNoRedisDAO.generate(anyString())).thenReturn(IdUtil.randomUUID()); |
| 105 | 115 | } |
| 106 | 116 | |
| 107 | -// @Test | |
| 108 | -// public void testCreateTradeOrder_success() { | |
| 109 | -// // 准备参数 | |
| 110 | -// Long userId = 100L; | |
| 111 | -// String userIp = "127.0.0.1"; | |
| 112 | -//// AppTradeOrderCreateReqVO reqVO = new AppTradeOrderCreateReqVO() | |
| 113 | -//// .setAddressId(10L).setCouponId(101L).setRemark("我是备注").setFromCart(true) | |
| 114 | -//// .setItems(Arrays.asList(new AppTradeOrderCreateReqVO.Item().setSkuId(1L).setCount(3), | |
| 115 | -//// new AppTradeOrderCreateReqVO.Item().setSkuId(2L).setCount(4))); | |
| 116 | -// AppTradeOrderCreateReqVO reqVO = null; | |
| 117 | -// // TODO 芋艿:重新高下 | |
| 118 | -// // mock 方法(商品 SKU 检查) | |
| 119 | -// ProductSkuRespDTO sku01 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(1L).setSpuId(11L) | |
| 120 | -// .setPrice(50).setStock(100) | |
| 121 | -// .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(111L).setValueId(222L)))); | |
| 122 | -// ProductSkuRespDTO sku02 = randomPojo(ProductSkuRespDTO.class, o -> o.setId(2L).setSpuId(21L) | |
| 123 | -// .setPrice(20).setStock(50)) | |
| 124 | -// .setProperties(singletonList(new ProductPropertyValueDetailRespDTO().setPropertyId(333L).setValueId(444L))); | |
| 125 | -// when(productSkuApi.getSkuList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(sku01, sku02)); | |
| 126 | -// // mock 方法(商品 SPU 检查) | |
| 127 | -// ProductSpuRespDTO spu01 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(11L) | |
| 128 | -// .setStatus(ProductSpuStatusEnum.ENABLE.getStatus()).setName("商品 1")); | |
| 129 | -// ProductSpuRespDTO spu02 = randomPojo(ProductSpuRespDTO.class, o -> o.setId(21L) | |
| 130 | -// .setStatus(ProductSpuStatusEnum.ENABLE.getStatus())); | |
| 131 | -// when(productSpuApi.getSpuList(eq(asSet(11L, 21L)))).thenReturn(Arrays.asList(spu01, spu02)); | |
| 132 | -// // mock 方法(用户收件地址的校验) | |
| 133 | -// MemberAddressRespDTO addressRespDTO = new MemberAddressRespDTO().setId(10L).setUserId(userId).setName("芋艿") | |
| 134 | -// .setMobile("15601691300").setAreaId(3306).setDetailAddress("土豆村"); | |
| 135 | -// when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(addressRespDTO); | |
| 136 | -// // mock 方法(价格计算) | |
| 137 | -// PriceCalculateRespDTO.OrderItem priceOrderItem01 = new PriceCalculateRespDTO.OrderItem() | |
| 138 | -// .setSpuId(11L).setSkuId(1L).setCount(3).setOriginalPrice(150).setOriginalUnitPrice(50) | |
| 139 | -// .setDiscountPrice(20).setPayPrice(130).setOrderPartPrice(7).setOrderDividePrice(35); | |
| 140 | -// PriceCalculateRespDTO.OrderItem priceOrderItem02 = new PriceCalculateRespDTO.OrderItem() | |
| 141 | -// .setSpuId(21L).setSkuId(2L).setCount(4).setOriginalPrice(80).setOriginalUnitPrice(20) | |
| 142 | -// .setDiscountPrice(40).setPayPrice(40).setOrderPartPrice(15).setOrderDividePrice(25); | |
| 143 | -// PriceCalculateRespDTO.Order priceOrder = new PriceCalculateRespDTO.Order() | |
| 144 | -// .setTotalPrice(230).setDiscountPrice(0).setCouponPrice(30) | |
| 145 | -// .setPointPrice(10).setDeliveryPrice(20).setPayPrice(80).setCouponId(101L).setCouponPrice(30) | |
| 146 | -// .setItems(Arrays.asList(priceOrderItem01, priceOrderItem02)); | |
| 147 | -// when(priceApi.calculatePrice(argThat(priceCalculateReqDTO -> { | |
| 148 | -// assertEquals(priceCalculateReqDTO.getUserId(), 100L); | |
| 149 | -// assertEquals(priceCalculateReqDTO.getCouponId(), 101L); | |
| 150 | -// assertEquals(priceCalculateReqDTO.getItems().get(0).getSkuId(), 1L); | |
| 151 | -// assertEquals(priceCalculateReqDTO.getItems().get(0).getCount(), 3); | |
| 152 | -// assertEquals(priceCalculateReqDTO.getItems().get(1).getSkuId(), 2L); | |
| 153 | -// assertEquals(priceCalculateReqDTO.getItems().get(1).getCount(), 4); | |
| 154 | -// return true; | |
| 155 | -// }))).thenReturn(new PriceCalculateRespDTO().setOrder(priceOrder)); | |
| 156 | -// // mock 方法(创建支付单) | |
| 157 | -// when(payOrderApi.createOrder(argThat(createReqDTO -> { | |
| 158 | -// assertEquals(createReqDTO.getAppId(), 888L); | |
| 159 | -// assertEquals(createReqDTO.getUserIp(), userIp); | |
| 160 | -// assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空 | |
| 161 | -// assertEquals(createReqDTO.getSubject(), "商品 1 等多件"); | |
| 162 | -// assertNull(createReqDTO.getBody()); | |
| 163 | -// assertEquals(createReqDTO.getPrice(), 80); | |
| 164 | -// assertNotNull(createReqDTO.getExpireTime()); | |
| 165 | -// return true; | |
| 166 | -// }))).thenReturn(1000L); | |
| 167 | -// | |
| 168 | -// // 调用方法 | |
| 169 | -// TradeOrderDO order = tradeOrderUpdateService.createOrder(userId, userIp, reqVO, null); | |
| 170 | -// // 断言 TradeOrderDO 订单 | |
| 171 | -// List<TradeOrderDO> tradeOrderDOs = tradeOrderMapper.selectList(); | |
| 172 | -// assertEquals(tradeOrderDOs.size(), 1); | |
| 173 | -// TradeOrderDO tradeOrderDO = tradeOrderDOs.get(0); | |
| 174 | -// assertEquals(tradeOrderDO.getId(), order.getId()); | |
| 175 | -// assertNotNull(tradeOrderDO.getNo()); | |
| 176 | -// assertEquals(tradeOrderDO.getType(), TradeOrderTypeEnum.NORMAL.getType()); | |
| 177 | -// assertEquals(tradeOrderDO.getTerminal(), TerminalEnum.H5.getTerminal()); | |
| 178 | -// assertEquals(tradeOrderDO.getUserId(), userId); | |
| 179 | -// assertEquals(tradeOrderDO.getUserIp(), userIp); | |
| 180 | -// assertEquals(tradeOrderDO.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus()); | |
| 181 | -// assertEquals(tradeOrderDO.getProductCount(), 7); | |
| 182 | -// assertNull(tradeOrderDO.getFinishTime()); | |
| 183 | -// assertNull(tradeOrderDO.getCancelTime()); | |
| 184 | -// assertNull(tradeOrderDO.getCancelType()); | |
| 185 | -// assertEquals(tradeOrderDO.getUserRemark(), "我是备注"); | |
| 186 | -// assertNull(tradeOrderDO.getRemark()); | |
| 187 | -// assertFalse(tradeOrderDO.getPayStatus()); | |
| 188 | -// assertNull(tradeOrderDO.getPayTime()); | |
| 189 | -// assertEquals(tradeOrderDO.getTotalPrice(), 230); | |
| 190 | -// assertEquals(tradeOrderDO.getDiscountPrice(), 0); | |
| 191 | -// assertEquals(tradeOrderDO.getAdjustPrice(), 0); | |
| 192 | -// assertEquals(tradeOrderDO.getPayPrice(), 80); | |
| 193 | -// assertEquals(tradeOrderDO.getPayOrderId(), 1000L); | |
| 194 | -// assertNull(tradeOrderDO.getPayChannelCode()); | |
| 195 | -// assertNull(tradeOrderDO.getLogisticsId()); | |
| 196 | -// assertNull(tradeOrderDO.getDeliveryTime()); | |
| 197 | -// assertNull(tradeOrderDO.getReceiveTime()); | |
| 198 | -// assertEquals(tradeOrderDO.getReceiverName(), "芋艿"); | |
| 199 | -// assertEquals(tradeOrderDO.getReceiverMobile(), "15601691300"); | |
| 200 | -// assertEquals(tradeOrderDO.getReceiverAreaId(), 3306); | |
| 201 | -// assertEquals(tradeOrderDO.getReceiverDetailAddress(), "土豆村"); | |
| 202 | -// assertEquals(tradeOrderDO.getRefundStatus(), TradeOrderRefundStatusEnum.NONE.getStatus()); | |
| 203 | -// assertEquals(tradeOrderDO.getRefundPrice(), 0); | |
| 204 | -// assertEquals(tradeOrderDO.getCouponPrice(), 30); | |
| 205 | -// assertEquals(tradeOrderDO.getPointPrice(), 10); | |
| 206 | -// // 断言 TradeOrderItemDO 订单(第 1 个) | |
| 207 | -// List<TradeOrderItemDO> tradeOrderItemDOs = tradeOrderItemMapper.selectList(); | |
| 208 | -// assertEquals(tradeOrderItemDOs.size(), 2); | |
| 209 | -// TradeOrderItemDO tradeOrderItemDO01 = tradeOrderItemDOs.get(0); | |
| 210 | -// assertNotNull(tradeOrderItemDO01.getId()); | |
| 211 | -// assertEquals(tradeOrderItemDO01.getUserId(), userId); | |
| 212 | -// assertEquals(tradeOrderItemDO01.getOrderId(), order.getId()); | |
| 213 | -// assertEquals(tradeOrderItemDO01.getSpuId(), 11L); | |
| 214 | -// assertEquals(tradeOrderItemDO01.getSkuId(), 1L); | |
| 215 | -// assertEquals(tradeOrderItemDO01.getProperties().size(), 1); | |
| 216 | -// assertEquals(tradeOrderItemDO01.getProperties().get(0).getPropertyId(), 111L); | |
| 217 | -// assertEquals(tradeOrderItemDO01.getProperties().get(0).getValueId(), 222L); | |
| 218 | -// //assertEquals(tradeOrderItemDO01.getSpuName(), sku01.getSpuName()); TODO 找不到spuName | |
| 219 | -// assertEquals(tradeOrderItemDO01.getPicUrl(), sku01.getPicUrl()); | |
| 220 | -// assertEquals(tradeOrderItemDO01.getCount(), 3); | |
| 221 | -//// assertEquals(tradeOrderItemDO01.getOriginalPrice(), 150); | |
| 222 | -// assertEquals(tradeOrderItemDO01.getPrice(), 50); | |
| 223 | -// assertEquals(tradeOrderItemDO01.getDiscountPrice(), 20); | |
| 224 | -// assertEquals(tradeOrderItemDO01.getPayPrice(), 130); | |
| 225 | -// assertEquals(tradeOrderItemDO01.getAfterSaleStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); | |
| 226 | -// // 断言 TradeOrderItemDO 订单(第 2 个) | |
| 227 | -// TradeOrderItemDO tradeOrderItemDO02 = tradeOrderItemDOs.get(1); | |
| 228 | -// assertNotNull(tradeOrderItemDO02.getId()); | |
| 229 | -// assertEquals(tradeOrderItemDO02.getUserId(), userId); | |
| 230 | -// assertEquals(tradeOrderItemDO02.getOrderId(), order.getId()); | |
| 231 | -// assertEquals(tradeOrderItemDO02.getSpuId(), 21L); | |
| 232 | -// assertEquals(tradeOrderItemDO02.getSkuId(), 2L); | |
| 233 | -// assertEquals(tradeOrderItemDO02.getProperties().size(), 1); | |
| 234 | -// assertEquals(tradeOrderItemDO02.getProperties().get(0).getPropertyId(), 333L); | |
| 235 | -// assertEquals(tradeOrderItemDO02.getProperties().get(0).getValueId(), 444L); | |
| 236 | -// //assertEquals(tradeOrderItemDO02.getSpuName(), sku02.getSpuName()); TODO 找不到spuName | |
| 237 | -// assertEquals(tradeOrderItemDO02.getPicUrl(), sku02.getPicUrl()); | |
| 238 | -// assertEquals(tradeOrderItemDO02.getCount(), 4); | |
| 239 | -//// assertEquals(tradeOrderItemDO02.getOriginalPrice(), 80); | |
| 240 | -// assertEquals(tradeOrderItemDO02.getPrice(), 20); | |
| 241 | -// assertEquals(tradeOrderItemDO02.getDiscountPrice(), 40); | |
| 242 | -// assertEquals(tradeOrderItemDO02.getPayPrice(), 40); | |
| 243 | -// assertEquals(tradeOrderItemDO02.getAfterSaleStatus(), TradeOrderItemAfterSaleStatusEnum.NONE.getStatus()); | |
| 244 | -// // 校验调用 | |
| 245 | -// verify(productSkuApi).updateSkuStock(argThat(updateStockReqDTO -> { | |
| 246 | -// assertEquals(updateStockReqDTO.getItems().size(), 2); | |
| 247 | -// assertEquals(updateStockReqDTO.getItems().get(0).getId(), 1L); | |
| 248 | -// assertEquals(updateStockReqDTO.getItems().get(0).getIncrCount(), 3); | |
| 249 | -// assertEquals(updateStockReqDTO.getItems().get(1).getId(), 2L); | |
| 250 | -// assertEquals(updateStockReqDTO.getItems().get(1).getIncrCount(), 4); | |
| 251 | -// return true; | |
| 252 | -// })); | |
| 253 | -// verify(couponApi).useCoupon(argThat(reqDTO -> { | |
| 254 | -// assertEquals(reqDTO.getId(), reqVO.getCouponId()); | |
| 255 | -// assertEquals(reqDTO.getUserId(), userId); | |
| 256 | -// assertEquals(reqDTO.getOrderId(), order.getId()); | |
| 257 | -// return true; | |
| 258 | -// })); | |
| 259 | -// } | |
| 117 | + @AfterEach | |
| 118 | + public void tearDown() { | |
| 119 | + RequestContextHolder.resetRequestAttributes(); | |
| 120 | + // 清理订单日志的上下文,单测没有 TradeOrderLogAspect 切面清理 | |
| 121 | + ReflectionTestUtils.invokeMethod(TradeOrderLogAspect.class, "clear"); | |
| 122 | + } | |
| 123 | + | |
| 124 | + @Test | |
| 125 | + public void testCreateOrder_success() { | |
| 126 | + // DONE 芋艿:重新高下 | |
| 127 | + // 准备参数 | |
| 128 | + Long userId = 100L; | |
| 129 | + String userIp = "127.0.0.1"; | |
| 130 | + AppTradeOrderCreateReqVO reqVO = new AppTradeOrderCreateReqVO(); | |
| 131 | + reqVO.setRemark("我是备注"); | |
| 132 | + reqVO.setItems(Arrays.asList(new AppTradeOrderSettlementReqVO.Item().setCartId(1L), | |
| 133 | + new AppTradeOrderSettlementReqVO.Item().setCartId(2L))); | |
| 134 | + reqVO.setCouponId(101L); | |
| 135 | + reqVO.setPointStatus(false); | |
| 136 | + reqVO.setDeliveryType(DeliveryTypeEnum.EXPRESS.getType()); | |
| 137 | + reqVO.setAddressId(10L); | |
| 138 | + // mock 请求(用户 IP、终端) | |
| 139 | + MockHttpServletRequest request = new MockHttpServletRequest(); | |
| 140 | + request.setRemoteAddr(userIp); | |
| 141 | + request.addHeader(WebFrameworkUtils.HEADER_TERMINAL, TerminalEnum.H5.getTerminal()); | |
| 142 | + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); | |
| 143 | + // mock 方法(购物车) | |
| 144 | + when(cartService.getCartList(eq(userId), eq(asSet(1L, 2L)))).thenReturn(Arrays.asList( | |
| 145 | + new CartDO().setId(1L).setUserId(userId).setSpuId(11L).setSkuId(1L).setCount(3).setSelected(true), | |
| 146 | + new CartDO().setId(2L).setUserId(userId).setSpuId(21L).setSkuId(2L).setCount(4).setSelected(true))); | |
| 147 | + // mock 方法(价格计算) | |
| 148 | + TradePriceCalculateRespBO.OrderItem priceOrderItem01 = new TradePriceCalculateRespBO.OrderItem() | |
| 149 | + .setSpuId(11L).setSkuId(1L).setCount(3).setCartId(1L).setSelected(true).setSpuName("商品 1") | |
| 150 | + .setPrice(50).setDiscountPrice(20).setDeliveryPrice(10).setCouponPrice(10).setPointPrice(5) | |
| 151 | + .setVipPrice(0).setPayPrice(105); | |
| 152 | + TradePriceCalculateRespBO.OrderItem priceOrderItem02 = new TradePriceCalculateRespBO.OrderItem() | |
| 153 | + .setSpuId(21L).setSkuId(2L).setCount(4).setCartId(2L).setSelected(true).setSpuName("商品 2") | |
| 154 | + .setPrice(20).setDiscountPrice(10).setDeliveryPrice(10).setCouponPrice(20).setPointPrice(5) | |
| 155 | + .setVipPrice(0).setPayPrice(55); | |
| 156 | + TradePriceCalculateRespBO.Price price = new TradePriceCalculateRespBO.Price() | |
| 157 | + .setTotalPrice(230).setDiscountPrice(30).setDeliveryPrice(20).setCouponPrice(30) | |
| 158 | + .setPointPrice(10).setVipPrice(0).setPayPrice(160); | |
| 159 | + when(tradePriceService.calculateOrderPrice(argThat(calculateReqBO -> { | |
| 160 | + assertEquals(userId, calculateReqBO.getUserId()); | |
| 161 | + assertEquals(101L, calculateReqBO.getCouponId()); | |
| 162 | + assertEquals(2, calculateReqBO.getItems().size()); | |
| 163 | + assertEquals(1L, calculateReqBO.getItems().get(0).getSkuId()); | |
| 164 | + assertEquals(3, calculateReqBO.getItems().get(0).getCount()); | |
| 165 | + assertEquals(2L, calculateReqBO.getItems().get(1).getSkuId()); | |
| 166 | + assertEquals(4, calculateReqBO.getItems().get(1).getCount()); | |
| 167 | + return true; | |
| 168 | + }))).thenReturn(new TradePriceCalculateRespBO().setType(TradeOrderTypeEnum.NORMAL.getType()) | |
| 169 | + .setPrice(price).setItems(Arrays.asList(priceOrderItem01, priceOrderItem02))); | |
| 170 | + // mock 方法(用户收件地址) | |
| 171 | + when(addressApi.getAddress(eq(10L), eq(userId))).thenReturn(new MemberAddressRespDTO().setId(10L) | |
| 172 | + .setUserId(userId).setName("芋艿").setMobile("15601691300").setAreaId(3306).setDetailAddress("土豆村")); | |
| 173 | + // mock 方法(创建支付单) | |
| 174 | + when(payOrderApi.createOrder(argThat(createReqDTO -> { | |
| 175 | + assertEquals("mall", createReqDTO.getAppKey()); | |
| 176 | + assertEquals(userIp, createReqDTO.getUserIp()); | |
| 177 | + assertEquals(userId, createReqDTO.getUserId()); | |
| 178 | + assertNotNull(createReqDTO.getMerchantOrderId()); // 由于 tradeOrderId 后生成,只能校验非空 | |
| 179 | + assertEquals("商品 1", createReqDTO.getSubject()); | |
| 180 | + assertEquals(160, createReqDTO.getPrice()); | |
| 181 | + assertNotNull(createReqDTO.getExpireTime()); | |
| 182 | + return true; | |
| 183 | + }))).thenReturn(1000L); | |
| 184 | + | |
| 185 | + // 调用 | |
| 186 | + TradeOrderDO order = tradeOrderUpdateService.createOrder(userId, reqVO); | |
| 187 | + // 断言 TradeOrderDO 订单 | |
| 188 | + List<TradeOrderDO> tradeOrderDOs = tradeOrderMapper.selectList(); | |
| 189 | + assertEquals(1, tradeOrderDOs.size()); | |
| 190 | + TradeOrderDO tradeOrderDO = tradeOrderDOs.get(0); | |
| 191 | + assertEquals(order.getId(), tradeOrderDO.getId()); | |
| 192 | + assertNotNull(tradeOrderDO.getNo()); | |
| 193 | + assertEquals(TradeOrderTypeEnum.NORMAL.getType(), tradeOrderDO.getType()); | |
| 194 | + assertEquals(TerminalEnum.H5.getTerminal(), tradeOrderDO.getTerminal()); | |
| 195 | + assertEquals(userId, tradeOrderDO.getUserId()); | |
| 196 | + assertEquals(userIp, tradeOrderDO.getUserIp()); | |
| 197 | + assertEquals(TradeOrderStatusEnum.UNPAID.getStatus(), tradeOrderDO.getStatus()); | |
| 198 | + assertEquals(7, tradeOrderDO.getProductCount()); | |
| 199 | + assertEquals("我是备注", tradeOrderDO.getUserRemark()); | |
| 200 | + assertFalse(tradeOrderDO.getPayStatus()); | |
| 201 | + assertNull(tradeOrderDO.getPayTime()); | |
| 202 | + assertEquals(1000L, tradeOrderDO.getPayOrderId()); | |
| 203 | + assertEquals(230, tradeOrderDO.getTotalPrice()); | |
| 204 | + assertEquals(30, tradeOrderDO.getDiscountPrice()); | |
| 205 | + assertEquals(20, tradeOrderDO.getDeliveryPrice()); | |
| 206 | + assertEquals(0, tradeOrderDO.getAdjustPrice()); | |
| 207 | + assertEquals(160, tradeOrderDO.getPayPrice()); | |
| 208 | + assertEquals(DeliveryTypeEnum.EXPRESS.getType(), tradeOrderDO.getDeliveryType()); | |
| 209 | + assertEquals("芋艿", tradeOrderDO.getReceiverName()); | |
| 210 | + assertEquals("15601691300", tradeOrderDO.getReceiverMobile()); | |
| 211 | + assertEquals(3306, tradeOrderDO.getReceiverAreaId()); | |
| 212 | + assertEquals("土豆村", tradeOrderDO.getReceiverDetailAddress()); | |
| 213 | + assertEquals(TradeOrderRefundStatusEnum.NONE.getStatus(), tradeOrderDO.getRefundStatus()); | |
| 214 | + assertEquals(0, tradeOrderDO.getRefundPrice()); | |
| 215 | + assertEquals(101L, tradeOrderDO.getCouponId()); | |
| 216 | + assertEquals(30, tradeOrderDO.getCouponPrice()); | |
| 217 | + assertEquals(10, tradeOrderDO.getPointPrice()); | |
| 218 | + // 断言 TradeOrderItemDO 订单项 | |
| 219 | + List<TradeOrderItemDO> tradeOrderItemDOs = tradeOrderItemMapper.selectListByOrderId(order.getId()); | |
| 220 | + assertEquals(2, tradeOrderItemDOs.size()); | |
| 221 | + assertOrderItem(tradeOrderItemDOs.get(0), order, priceOrderItem01); | |
| 222 | + assertOrderItem(tradeOrderItemDOs.get(1), order, priceOrderItem02); | |
| 223 | + // 断言调用 | |
| 224 | + verify(tradeOrderHandler).beforeOrderCreate(any(), any()); | |
| 225 | + verify(tradeOrderHandler).afterOrderCreate(any(), any()); | |
| 226 | + verify(cartService).deleteCart(eq(userId), eq(asSet(1L, 2L))); | |
| 227 | + } | |
| 228 | + | |
| 229 | + private static void assertOrderItem(TradeOrderItemDO orderItem, TradeOrderDO order, | |
| 230 | + TradePriceCalculateRespBO.OrderItem priceOrderItem) { | |
| 231 | + assertNotNull(orderItem.getId()); | |
| 232 | + assertEquals(order.getUserId(), orderItem.getUserId()); | |
| 233 | + assertEquals(order.getId(), orderItem.getOrderId()); | |
| 234 | + assertEquals(priceOrderItem.getSpuId(), orderItem.getSpuId()); | |
| 235 | + assertEquals(priceOrderItem.getSpuName(), orderItem.getSpuName()); // DONE 找不到spuName | |
| 236 | + assertEquals(priceOrderItem.getSkuId(), orderItem.getSkuId()); | |
| 237 | + assertEquals(priceOrderItem.getCartId(), orderItem.getCartId()); | |
| 238 | + assertEquals(priceOrderItem.getCount(), orderItem.getCount()); | |
| 239 | + assertEquals(priceOrderItem.getPrice(), orderItem.getPrice()); | |
| 240 | + assertEquals(priceOrderItem.getDiscountPrice(), orderItem.getDiscountPrice()); | |
| 241 | + assertEquals(priceOrderItem.getPayPrice(), orderItem.getPayPrice()); | |
| 242 | + assertEquals(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus(), orderItem.getAfterSaleStatus()); | |
| 243 | + assertFalse(orderItem.getCommentStatus()); | |
| 244 | + } | |
| 260 | 245 | |
| 261 | 246 | @Test |
| 262 | 247 | public void testUpdateOrderPaid() { |
| ... | ... | @@ -282,6 +267,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 282 | 267 | assertTrue(dbOrder.getPayStatus()); |
| 283 | 268 | assertNotNull(dbOrder.getPayTime()); |
| 284 | 269 | assertEquals(dbOrder.getPayChannelCode(), "wx_pub"); |
| 270 | + verify(tradeOrderHandler).afterPayOrder(argThat(o -> o.getId().equals(id)), anyList()); | |
| 285 | 271 | } |
| 286 | 272 | |
| 287 | 273 | @Test |
| ... | ... | @@ -294,15 +280,12 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 294 | 280 | o.setDeliveryType(DeliveryTypeEnum.EXPRESS.getType()); |
| 295 | 281 | }); |
| 296 | 282 | tradeOrderMapper.insert(order); |
| 297 | - | |
| 298 | - DeliveryExpressCreateReqVO expressCreateReqVO = new DeliveryExpressCreateReqVO(); | |
| 299 | - expressCreateReqVO.setCode("code").setName("Name").setLogo("logo").setSort(0).setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 300 | - Long deliveryExpressId = deliveryExpressService.createDeliveryExpress(expressCreateReqVO); | |
| 301 | 283 | // 准备参数 |
| 302 | 284 | TradeOrderDeliveryReqVO deliveryReqVO = new TradeOrderDeliveryReqVO().setId(1L) |
| 303 | - .setLogisticsId(deliveryExpressId).setLogisticsNo("100"); | |
| 304 | - | |
| 305 | - // mock 方法(支付单) | |
| 285 | + .setLogisticsId(20L).setLogisticsNo("100"); | |
| 286 | + // mock 方法(物流公司) | |
| 287 | + when(deliveryExpressService.validateDeliveryExpress(eq(20L))).thenReturn( | |
| 288 | + randomPojo(DeliveryExpressDO.class, o -> o.setId(20L))); | |
| 306 | 289 | |
| 307 | 290 | // 调用 |
| 308 | 291 | tradeOrderUpdateService.deliveryOrder(deliveryReqVO); |
| ... | ... | @@ -311,6 +294,9 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 311 | 294 | assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.DELIVERED.getStatus()); |
| 312 | 295 | assertPojoEquals(dbOrder, deliveryReqVO); |
| 313 | 296 | assertNotNull(dbOrder.getDeliveryTime()); |
| 297 | + // 断言调用(站内信) | |
| 298 | + verify(tradeMessageService).sendMessageWhenDeliveryOrder(argThat(reqBO -> | |
| 299 | + reqBO.getOrderId().equals(1L) && reqBO.getUserId().equals(order.getUserId()))); | |
| 314 | 300 | } |
| 315 | 301 | |
| 316 | 302 | @Test |
| ... | ... | @@ -324,7 +310,6 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 324 | 310 | // 准备参数 |
| 325 | 311 | Long id = 1L; |
| 326 | 312 | Long userId = 10L; |
| 327 | - // mock 方法(支付单) | |
| 328 | 313 | |
| 329 | 314 | // 调用 |
| 330 | 315 | tradeOrderUpdateService.receiveOrderByMember(userId, id); |
| ... | ... | @@ -332,6 +317,7 @@ public class TradeOrderUpdateServiceTest extends BaseDbUnitTest { |
| 332 | 317 | TradeOrderDO dbOrder = tradeOrderMapper.selectById(1L); |
| 333 | 318 | assertEquals(dbOrder.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus()); |
| 334 | 319 | assertNotNull(dbOrder.getReceiveTime()); |
| 320 | + verify(tradeOrderHandler).afterReceiveOrder(argThat(o -> o.getId().equals(id))); | |
| 335 | 321 | } |
| 336 | 322 | |
| 337 | 323 | } | ... | ... |
yudao-module-mall/yudao-module-trade/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -49,12 +49,15 @@ CREATE TABLE IF NOT EXISTS "trade_order" |
| 49 | 49 | "refund_point" int NULL, |
| 50 | 50 | "vip_price" int NULL, |
| 51 | 51 | "give_coupons_map" varchar NULL, |
| 52 | + "give_coupon_template_counts" varchar NULL, | |
| 53 | + "give_coupon_ids" varchar NULL, | |
| 52 | 54 | "seckill_activity_id" long NULL, |
| 53 | 55 | "bargain_activity_id" long NULL, |
| 54 | 56 | "bargain_record_id" long NULL, |
| 55 | 57 | "combination_activity_id" long NULL, |
| 56 | 58 | "combination_head_id" long NULL, |
| 57 | 59 | "combination_record_id" long NULL, |
| 60 | + "point_activity_id" bigint NULL, | |
| 58 | 61 | "creator" varchar DEFAULT '', |
| 59 | 62 | "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| 60 | 63 | "updater" varchar DEFAULT '', | ... | ... |
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletTransactionMapper.java
| ... | ... | @@ -39,7 +39,7 @@ public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransac |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | 41 | default Integer selectPriceSum(Long walletId, Integer type, LocalDateTime[] createTime) { |
| 42 | - // SQL sum 查询;支出流水的 price 为负值,汇总取正值(与钱包 total_expense 的正值语义一致) | |
| 42 | + // SQL sum 查询;支出流水的 price 为负值,取反后汇总为正值 | |
| 43 | 43 | String sumColumn = Objects.equals(type, TYPE_EXPENSE) ? "SUM(-price) AS priceSum" : "SUM(price) AS priceSum"; |
| 44 | 44 | List<Map<String, Object>> result = selectMaps(new QueryWrapperX<PayWalletTransactionDO>() |
| 45 | 45 | .select(sumColumn) | ... | ... |