|
|
@@ -0,0 +1,249 @@
|
|
|
+package com.etotem.cfc.controller;
|
|
|
+
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
+import com.etotem.cfc.service.HealthPlanService;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import static org.junit.jupiter.api.Assertions.*;
|
|
|
+import static org.mockito.Mockito.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * HL-05/R5+R6 产品多选一购买任务契约测试(B-114 覆盖缺口闭环):
|
|
|
+ * - POST /api/health-plan/product-options(productOptions)
|
|
|
+ * - POST /api/health-plan/product-select(productSelect)
|
|
|
+ * - POST /api/health-plan/purchase-complete(purchaseComplete)
|
|
|
+ *
|
|
|
+ * 覆盖:参数校验 / familyId 归属校验 / Service 异常映射 / 成功分支 / 幂等复用。
|
|
|
+ * 与既有 HealthPlanDeleteControllerTest 风格一致(mock Service + 反射注入)。
|
|
|
+ */
|
|
|
+class HealthPlanProductSelectionContractTest {
|
|
|
+
|
|
|
+ private HealthPlanController userController;
|
|
|
+ private HealthPlanService planService;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() throws Exception {
|
|
|
+ userController = new HealthPlanController();
|
|
|
+ planService = mock(HealthPlanService.class);
|
|
|
+ set("healthPlanService", userController, planService);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void set(String name, Object target, Object v) throws Exception {
|
|
|
+ Field f = target.getClass().getDeclaredField(name);
|
|
|
+ f.setAccessible(true);
|
|
|
+ f.set(target, v);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Object> params(Object planId, Object familyId) {
|
|
|
+ Map<String, Object> p = new HashMap<>();
|
|
|
+ if (planId != null) p.put("planId", planId);
|
|
|
+ if (familyId != null) p.put("familyId", familyId);
|
|
|
+ return p;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== product-options ====================
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productOptions_planId为空返回错误() {
|
|
|
+ Result<?> r = userController.productOptions(params(null, 10L), 1L, 10L);
|
|
|
+ assertEquals("planId不能为空", r.getMessage());
|
|
|
+ verify(planService, never()).getPlanProductGroups(any(), any(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productOptions_familyId为空返回错误() {
|
|
|
+ Result<?> r = userController.productOptions(params(99L, null), 1L, null);
|
|
|
+ assertEquals("familyId不能为空", r.getMessage());
|
|
|
+ verify(planService, never()).getPlanProductGroups(any(), any(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productOptions_service抛异常映射为业务错误() {
|
|
|
+ when(planService.getPlanProductGroups(99L, 10L, 1L))
|
|
|
+ .thenThrow(new IllegalStateException("方案不存在"));
|
|
|
+ Result<?> r = userController.productOptions(params(99L, 10L), 1L, 10L);
|
|
|
+ assertEquals(500, r.getCode());
|
|
|
+ assertEquals("方案不存在", r.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productOptions_归属不匹配返回无权查看() {
|
|
|
+ when(planService.getPlanProductGroups(99L, 20L, 1L)).thenReturn(null);
|
|
|
+ Result<?> r = userController.productOptions(params(99L, 20L), 1L, 20L);
|
|
|
+ assertEquals("无权查看该方案产品", r.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productOptions_成功返回产品分组() {
|
|
|
+ List<Map<String, Object>> groups = new ArrayList<>();
|
|
|
+ Map<String, Object> g = new HashMap<>();
|
|
|
+ g.put("category", "营养补充");
|
|
|
+ g.put("dimensionCode", "body");
|
|
|
+ List<Map<String, Object>> options = new ArrayList<>();
|
|
|
+ Map<String, Object> o = new HashMap<>();
|
|
|
+ o.put("productId", 1001L);
|
|
|
+ o.put("name", "蛋白粉");
|
|
|
+ options.add(o);
|
|
|
+ g.put("options", options);
|
|
|
+ groups.add(g);
|
|
|
+ when(planService.getPlanProductGroups(99L, 10L, 1L)).thenReturn(groups);
|
|
|
+
|
|
|
+ Result<?> r = userController.productOptions(params(99L, 10L), 1L, 10L);
|
|
|
+ assertEquals(200, r.getCode());
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<Map<String, Object>> data = (List<Map<String, Object>>) r.getData();
|
|
|
+ assertEquals(1, data.size());
|
|
|
+ assertEquals("营养补充", data.get(0).get("category"));
|
|
|
+ assertEquals(1, ((List<?>) data.get(0).get("options")).size());
|
|
|
+ // 每类多选一:选项数上限 3(PRODUCT_OPTIONS_PER_GROUP 契约)
|
|
|
+ assertTrue(((List<?>) data.get(0).get("options")).size() <= 3,
|
|
|
+ "每个品类最多给出 3 个候选(多选一契约)");
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== product-select ====================
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productSelect_planId为空返回错误() {
|
|
|
+ Result<?> r = userController.productSelect(params(null, 10L), 1L, 10L);
|
|
|
+ assertEquals("planId不能为空", r.getMessage());
|
|
|
+ verify(planService, never()).confirmProductSelection(any(), any(), any(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productSelect_selections为空返回错误() {
|
|
|
+ Map<String, Object> p = params(99L, 10L);
|
|
|
+ p.put("selections", new ArrayList<>());
|
|
|
+ Result<?> r = userController.productSelect(p, 1L, 10L);
|
|
|
+ assertEquals("selections不能为空", r.getMessage());
|
|
|
+ verify(planService, never()).confirmProductSelection(any(), any(), any(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productSelect_service抛异常映射为业务错误() {
|
|
|
+ Map<String, Object> sel = new HashMap<>();
|
|
|
+ sel.put("category", "营养补充");
|
|
|
+ sel.put("productId", 1001L);
|
|
|
+ sel.put("quantity", 1);
|
|
|
+ Map<String, Object> p = params(99L, 10L);
|
|
|
+ List<Map<String, Object>> selections = new ArrayList<>();
|
|
|
+ selections.add(sel);
|
|
|
+ p.put("selections", selections);
|
|
|
+ when(planService.confirmProductSelection(99L, 10L, 1L, selections))
|
|
|
+ .thenThrow(new IllegalStateException("请先为每类产品选择一项"));
|
|
|
+ Result<?> r = userController.productSelect(p, 1L, 10L);
|
|
|
+ assertEquals(500, r.getCode());
|
|
|
+ assertEquals("请先为每类产品选择一项", r.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productSelect_归属不匹配返回无权操作() {
|
|
|
+ Map<String, Object> sel = new HashMap<>();
|
|
|
+ sel.put("category", "营养补充");
|
|
|
+ Map<String, Object> p = params(99L, 20L);
|
|
|
+ List<Map<String, Object>> selections = new ArrayList<>();
|
|
|
+ selections.add(sel);
|
|
|
+ p.put("selections", selections);
|
|
|
+ when(planService.confirmProductSelection(99L, 20L, 1L, selections)).thenReturn(null);
|
|
|
+ Result<?> r = userController.productSelect(p, 1L, 20L);
|
|
|
+ assertEquals("无权操作该方案", r.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void productSelect_成功返回购买任务与购物车() {
|
|
|
+ Map<String, Object> sel = new HashMap<>();
|
|
|
+ sel.put("category", "营养补充");
|
|
|
+ sel.put("productId", 1001L);
|
|
|
+ sel.put("quantity", 2);
|
|
|
+ Map<String, Object> p = params(99L, 10L);
|
|
|
+ List<Map<String, Object>> selections = new ArrayList<>();
|
|
|
+ selections.add(sel);
|
|
|
+ p.put("selections", selections);
|
|
|
+
|
|
|
+ Map<String, Object> task = new HashMap<>();
|
|
|
+ task.put("id", 5001L);
|
|
|
+ task.put("title", "购买:蛋白粉");
|
|
|
+ task.put("category", "购买任务");
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("planId", 99L);
|
|
|
+ List<Map<String, Object>> purchaseTasks = new ArrayList<>();
|
|
|
+ purchaseTasks.add(task);
|
|
|
+ data.put("purchaseTasks", purchaseTasks);
|
|
|
+ Map<String, Object> cart = new HashMap<>();
|
|
|
+ cart.put("productId", 1001L);
|
|
|
+ cart.put("quantity", 2);
|
|
|
+ List<Map<String, Object>> cartItems = new ArrayList<>();
|
|
|
+ cartItems.add(cart);
|
|
|
+ data.put("cartItems", cartItems);
|
|
|
+ data.put("cartAddCount", 1);
|
|
|
+
|
|
|
+ when(planService.confirmProductSelection(99L, 10L, 1L, selections)).thenReturn(data);
|
|
|
+ Result<?> r = userController.productSelect(p, 1L, 10L);
|
|
|
+ assertEquals(200, r.getCode());
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<String, Object> d = (Map<String, Object>) r.getData();
|
|
|
+ assertEquals(99L, d.get("planId"));
|
|
|
+ assertEquals(1, ((List<?>) d.get("purchaseTasks")).size());
|
|
|
+ assertEquals("购买:蛋白粉", ((Map<?, ?>) ((List<?>) d.get("purchaseTasks")).get(0)).get("title"));
|
|
|
+ assertEquals(1, d.get("cartAddCount"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== purchase-complete ====================
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void purchaseComplete_planId为空返回错误() {
|
|
|
+ Result<?> r = userController.purchaseComplete(params(null, 10L), 1L, 10L);
|
|
|
+ assertEquals("planId不能为空", r.getMessage());
|
|
|
+ verify(planService, never()).completePurchaseAndGenerateTasks(any(), any(), any());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void purchaseComplete_购买任务不属于该方案抛异常() {
|
|
|
+ Map<String, Object> p = params(99L, 10L);
|
|
|
+ p.put("purchaseTaskId", 777L);
|
|
|
+ when(planService.completePurchaseAndGenerateTasks(99L, 10L, 777L))
|
|
|
+ .thenThrow(new IllegalStateException("购买任务不存在或不属于该方案"));
|
|
|
+ Result<?> r = userController.purchaseComplete(p, 1L, 10L);
|
|
|
+ assertEquals(500, r.getCode());
|
|
|
+ assertEquals("购买任务不存在或不属于该方案", r.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void purchaseComplete_归属不匹配返回无权操作() {
|
|
|
+ Map<String, Object> p = params(99L, 20L);
|
|
|
+ p.put("purchaseTaskId", 5001L);
|
|
|
+ when(planService.completePurchaseAndGenerateTasks(99L, 20L, 5001L)).thenReturn(null);
|
|
|
+ Result<?> r = userController.purchaseComplete(p, 1L, 20L);
|
|
|
+ assertEquals("无权操作该方案", r.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void purchaseComplete_成功生成执行任务() {
|
|
|
+ Map<String, Object> task = new HashMap<>();
|
|
|
+ task.put("id", 6001L);
|
|
|
+ task.put("title", "执行:每日补剂");
|
|
|
+ task.put("category", "执行任务");
|
|
|
+ Map<String, Object> data = new HashMap<>();
|
|
|
+ data.put("planId", 99L);
|
|
|
+ List<Map<String, Object>> executionTasks = new ArrayList<>();
|
|
|
+ executionTasks.add(task);
|
|
|
+ data.put("executionTasks", executionTasks);
|
|
|
+ data.put("generatedCount", 1);
|
|
|
+
|
|
|
+ Map<String, Object> p = params(99L, 10L);
|
|
|
+ p.put("purchaseTaskId", 5001L);
|
|
|
+ when(planService.completePurchaseAndGenerateTasks(99L, 10L, 5001L)).thenReturn(data);
|
|
|
+ Result<?> r = userController.purchaseComplete(p, 1L, 10L);
|
|
|
+ assertEquals(200, r.getCode());
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ Map<String, Object> d = (Map<String, Object>) r.getData();
|
|
|
+ assertEquals(1, ((List<?>) d.get("executionTasks")).size());
|
|
|
+ assertEquals(1, d.get("generatedCount"));
|
|
|
+ }
|
|
|
+}
|