|
|
@@ -0,0 +1,85 @@
|
|
|
+package com.etotem.cfc.controller;
|
|
|
+
|
|
|
+import com.etotem.cfc.dto.MembershipLevelDTO;
|
|
|
+import com.etotem.cfc.service.MembershipService;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.mockito.Mock;
|
|
|
+import org.mockito.MockitoAnnotations;
|
|
|
+import org.springframework.http.MediaType;
|
|
|
+import org.springframework.test.util.ReflectionTestUtils;
|
|
|
+import org.springframework.test.web.servlet.MockMvc;
|
|
|
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
|
|
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
|
|
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 契约级单测:POST /api/membership/plans 必须返回 levelOrder。
|
|
|
+ *
|
|
|
+ * <p>背景(2026-11-05 付费页显示 ¥0.00):升级页 upgrade.vue 自 dbd94c59b
|
|
|
+ * 起按接口返回的 {@code levelOrder} 比较等级高低({@code getLevelOrder})。
|
|
|
+ * 但 {@code getPricingPlans()} 手工组装 Map 时遗漏了 levelOrder,导致所有档位
|
|
|
+ * levelOrder 均为 undefined/0:免费用户 applyDefaultLevel 无法找到更高档位,
|
|
|
+ * selectedLevel 回落为 FREE,下一步进入 pay 页时按 FREE 取价 = 0 分,
|
|
|
+ * 付费页显示 ¥0.00,下单被后端以「该会员方案暂未开放购买」拒绝。</p>
|
|
|
+ *
|
|
|
+ * <p>测试隔离:MockMvc standalone + mock MembershipService,无 Spring 上下文 / 无 DB。</p>
|
|
|
+ */
|
|
|
+class MembershipControllerPlansLevelOrderTest {
|
|
|
+
|
|
|
+ private static final String URL = "/api/membership/plans";
|
|
|
+
|
|
|
+ @Mock
|
|
|
+ private MembershipService membershipService;
|
|
|
+
|
|
|
+ private MockMvc mockMvc;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ MockitoAnnotations.openMocks(this);
|
|
|
+ MembershipController controller = new MembershipController();
|
|
|
+ ReflectionTestUtils.setField(controller, "membershipService", membershipService);
|
|
|
+ mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ private MembershipLevelDTO level(String code, int levelOrder, int monthly, int yearly) {
|
|
|
+ MembershipLevelDTO dto = new MembershipLevelDTO();
|
|
|
+ dto.setLevelCode(code);
|
|
|
+ dto.setLevelName(code + "-name");
|
|
|
+ dto.setLevelOrder(levelOrder);
|
|
|
+ dto.setPriceMonthly(monthly);
|
|
|
+ dto.setPriceQuarterly(0);
|
|
|
+ dto.setPriceYearly(yearly);
|
|
|
+ return dto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ void plans_返回每个可购档位的levelOrder() throws Exception {
|
|
|
+ List<MembershipLevelDTO> levels = Arrays.asList(
|
|
|
+ level("FREE", 1, 0, 0),
|
|
|
+ level("FAMILY", 3, 19800, 198000),
|
|
|
+ level("PREMIUM", 4, 131400, 1316800),
|
|
|
+ level("BASIC", 2, 990, 0),
|
|
|
+ level("LIFETIME", 5, 0, 0),
|
|
|
+ level("PROVIDER", 1, 13140, 131400)
|
|
|
+ );
|
|
|
+ when(membershipService.getAllLevels()).thenReturn(levels);
|
|
|
+
|
|
|
+ mockMvc.perform(post(URL).contentType(MediaType.APPLICATION_JSON))
|
|
|
+ .andExpect(status().isOk())
|
|
|
+ .andExpect(jsonPath("$.code").value(200))
|
|
|
+ // LIFETIME / PROVIDER 不可购,仍应被过滤
|
|
|
+ .andExpect(jsonPath("$.data.length()").value(4))
|
|
|
+ // 免费用户升级页依赖该字段判断下一档;缺失会导致选档回落 FREE、付费页显示 0
|
|
|
+ .andExpect(jsonPath("$.data[0].levelOrder").value(1)) // FREE
|
|
|
+ .andExpect(jsonPath("$.data[1].levelOrder").value(3)) // FAMILY
|
|
|
+ .andExpect(jsonPath("$.data[2].levelOrder").value(4)) // PREMIUM
|
|
|
+ .andExpect(jsonPath("$.data[3].levelOrder").value(2)); // BASIC
|
|
|
+ }
|
|
|
+}
|