|
|
@@ -0,0 +1,198 @@
|
|
|
+package com.etotem.cfc.unit;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
|
|
+import com.etotem.cfc.entity.Product;
|
|
|
+import com.etotem.cfc.entity.ProductComboItem;
|
|
|
+import com.etotem.cfc.entity.ProductSku;
|
|
|
+import com.etotem.cfc.mapper.ProductComboItemMapper;
|
|
|
+import com.etotem.cfc.mapper.ProductMapper;
|
|
|
+import com.etotem.cfc.mapper.ProductSkuMapper;
|
|
|
+import com.etotem.cfc.service.ComboService;
|
|
|
+import org.apache.ibatis.builder.MapperBuilderAssistant;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
+import org.springframework.test.util.ReflectionTestUtils;
|
|
|
+
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
+import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import org.mockito.InOrder;
|
|
|
+import static org.mockito.Mockito.inOrder;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.never;
|
|
|
+import static org.mockito.Mockito.times;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 套餐内容管理服务单测(套餐=独立商品,内容=子商品SKU×数量,挂套餐SKU级)。
|
|
|
+ */
|
|
|
+class ComboServiceTest {
|
|
|
+
|
|
|
+ private ComboService service;
|
|
|
+ private ProductComboItemMapper comboItemMapper;
|
|
|
+ private ProductSkuMapper productSkuMapper;
|
|
|
+ private ProductMapper productMapper;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ // LambdaQueryWrapper 解析列名依赖 MyBatis-Plus TableInfo 缓存(与 ProductSkuComboServiceTest 同约定)
|
|
|
+ MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
|
|
|
+ TableInfoHelper.initTableInfo(assistant, ProductComboItem.class);
|
|
|
+ TableInfoHelper.initTableInfo(assistant, ProductSku.class);
|
|
|
+ service = new ComboService();
|
|
|
+ comboItemMapper = mock(ProductComboItemMapper.class);
|
|
|
+ productSkuMapper = mock(ProductSkuMapper.class);
|
|
|
+ productMapper = mock(ProductMapper.class);
|
|
|
+ ReflectionTestUtils.setField(service, "comboItemMapper", comboItemMapper);
|
|
|
+ ReflectionTestUtils.setField(service, "productSkuMapper", productSkuMapper);
|
|
|
+ ReflectionTestUtils.setField(service, "productMapper", productMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ private ProductSku sku(Long id, Long productId) {
|
|
|
+ ProductSku s = new ProductSku();
|
|
|
+ s.setId(id);
|
|
|
+ s.setProductId(productId);
|
|
|
+ return s;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Object> item(Long childProductId, Long childSkuId, Object quantity) {
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
+ if (childProductId != null) m.put("childProductId", childProductId);
|
|
|
+ if (childSkuId != null) m.put("childSkuId", childSkuId);
|
|
|
+ if (quantity != null) m.put("quantity", quantity);
|
|
|
+ return m;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_deletesOldRowsThenInsertsNew() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(sku(9L, 1L));
|
|
|
+ when(productSkuMapper.selectById(100L)).thenReturn(sku(100L, 10L));
|
|
|
+
|
|
|
+ service.saveComboItems(9L, Collections.singletonList(item(10L, 100L, 2)));
|
|
|
+
|
|
|
+ InOrder inOrder = inOrder(comboItemMapper);
|
|
|
+ inOrder.verify(comboItemMapper).delete(any());
|
|
|
+ inOrder.verify(comboItemMapper).insert(any(ProductComboItem.class));
|
|
|
+ verify(comboItemMapper, times(1)).insert(any(ProductComboItem.class));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_insertedRowCarriesComboSkuIdChildAndQty() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(sku(9L, 1L));
|
|
|
+ when(productSkuMapper.selectById(100L)).thenReturn(sku(100L, 10L));
|
|
|
+
|
|
|
+ service.saveComboItems(9L, Collections.singletonList(item(10L, 100L, 3)));
|
|
|
+
|
|
|
+ ArgumentCaptor<ProductComboItem> captor = ArgumentCaptor.forClass(ProductComboItem.class);
|
|
|
+ verify(comboItemMapper).insert(captor.capture());
|
|
|
+ ProductComboItem row = captor.getValue();
|
|
|
+ assertEquals(Long.valueOf(9L), row.getComboSkuId());
|
|
|
+ assertEquals(Long.valueOf(10L), row.getChildProductId());
|
|
|
+ assertEquals(Long.valueOf(100L), row.getChildSkuId());
|
|
|
+ assertEquals(Integer.valueOf(3), row.getQuantity());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_quantityMissing_throwsAndInsertsNothing() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(sku(9L, 1L));
|
|
|
+ when(productSkuMapper.selectById(100L)).thenReturn(sku(100L, 10L));
|
|
|
+
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> service.saveComboItems(9L, Collections.singletonList(item(10L, 100L, null))));
|
|
|
+ verify(comboItemMapper, never()).insert(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_quantityNotPositive_throwsAndInsertsNothing() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(sku(9L, 1L));
|
|
|
+ when(productSkuMapper.selectById(100L)).thenReturn(sku(100L, 10L));
|
|
|
+
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> service.saveComboItems(9L, Collections.singletonList(item(10L, 100L, 0))));
|
|
|
+ verify(comboItemMapper, never()).insert(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_childSkuMissing_throwsAndInsertsNothing() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(sku(9L, 1L));
|
|
|
+
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> service.saveComboItems(9L, Collections.singletonList(item(10L, null, 1))));
|
|
|
+ verify(comboItemMapper, never()).insert(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_comboSkuNotFound_throwsAndInsertsNothing() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(null);
|
|
|
+
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> service.saveComboItems(9L, Collections.singletonList(item(10L, 100L, 1))));
|
|
|
+ verify(comboItemMapper, never()).insert(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_childSkuNotFound_throwsAndInsertsNothing() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(sku(9L, 1L));
|
|
|
+ when(productSkuMapper.selectById(100L)).thenReturn(null);
|
|
|
+
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> service.saveComboItems(9L, Collections.singletonList(item(10L, 100L, 1))));
|
|
|
+ verify(comboItemMapper, never()).insert(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void saveComboItems_childProductMismatchWithChildSku_throws() {
|
|
|
+ when(productSkuMapper.selectById(9L)).thenReturn(sku(9L, 1L));
|
|
|
+ when(productSkuMapper.selectById(100L)).thenReturn(sku(100L, 10L));
|
|
|
+
|
|
|
+ assertThrows(IllegalArgumentException.class,
|
|
|
+ () -> service.saveComboItems(9L, Collections.singletonList(item(99L, 100L, 1))));
|
|
|
+ verify(comboItemMapper, never()).insert(any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void listComboItems_fillsChildDisplayFields() {
|
|
|
+ ProductComboItem row = new ProductComboItem();
|
|
|
+ row.setId(1L);
|
|
|
+ row.setComboSkuId(9L);
|
|
|
+ row.setChildProductId(10L);
|
|
|
+ row.setChildSkuId(100L);
|
|
|
+ row.setQuantity(2);
|
|
|
+ when(comboItemMapper.selectList(any())).thenReturn(Collections.singletonList(row));
|
|
|
+
|
|
|
+ Product child = new Product();
|
|
|
+ child.setId(10L);
|
|
|
+ child.setName("子商品A");
|
|
|
+ child.setCoverImage("http://img/a.png");
|
|
|
+ child.setPrice(5000);
|
|
|
+ when(productMapper.selectBatchIds(any())).thenReturn(Collections.singletonList(child));
|
|
|
+
|
|
|
+ ProductSku childSku = sku(100L, 10L);
|
|
|
+ childSku.setSpecs("红色/L");
|
|
|
+ when(productSkuMapper.selectBatchIds(any())).thenReturn(Collections.singletonList(childSku));
|
|
|
+
|
|
|
+ List<ProductComboItem> items = service.listComboItems(9L);
|
|
|
+
|
|
|
+ assertEquals(1, items.size());
|
|
|
+ ProductComboItem got = items.get(0);
|
|
|
+ assertEquals("子商品A", got.getChildProductName());
|
|
|
+ assertEquals("http://img/a.png", got.getChildProductImage());
|
|
|
+ assertEquals(Integer.valueOf(5000), got.getChildProductPrice());
|
|
|
+ assertEquals("红色/L", got.getChildSkuSpecs());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void listComboItems_emptyResult_returnsEmptyList() {
|
|
|
+ when(comboItemMapper.selectList(any())).thenReturn(Collections.emptyList());
|
|
|
+
|
|
|
+ assertEquals(0, service.listComboItems(9L).size());
|
|
|
+ }
|
|
|
+}
|