TrialQuotaConfigTest.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package com.xuekairui.gateway;
  2. import com.fasterxml.jackson.databind.JsonNode;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import org.junit.jupiter.api.*;
  5. import org.springframework.http.MediaType;
  6. import org.springframework.test.web.servlet.MvcResult;
  7. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
  8. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
  9. /**
  10. * 体验配额配置接口测试
  11. * 覆盖:用户端获取活跃配置、运营端CRUD管理、权限校验
  12. */
  13. @DisplayName("体验配额配置接口测试")
  14. @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
  15. class TrialQuotaConfigTest extends GatewayBaseTest {
  16. private static final ObjectMapper mapper = new ObjectMapper();
  17. /** 动态捕获的配置 ID,避免依赖 auto_increment 顺序 */
  18. private static Long createdConfigId;
  19. private static Long activityConfigId;
  20. private static Long deleteConfigId;
  21. private String adminToken() {
  22. return generateAccessToken(100L, "13900000000", "ADMIN");
  23. }
  24. // ==================== 用户端:获取活跃配置 ====================
  25. @Test
  26. @Order(1)
  27. @DisplayName("GET /api/trial-quota/active - 获取活跃体验配额配置(公开接口)")
  28. void getActiveConfig_shouldReturnConfig() throws Exception {
  29. mockMvc.perform(get("/api/trial-quota/active"))
  30. .andExpect(status().isOk())
  31. .andExpect(jsonPath("$.code").isNumber());
  32. }
  33. // ==================== 运营端:查询配置列表 ====================
  34. @Test
  35. @Order(10)
  36. @DisplayName("GET /api/admin/trial-quota/configs - 查询配置列表")
  37. void listConfigs_shouldReturnList() throws Exception {
  38. mockMvc.perform(get("/api/admin/trial-quota/configs")
  39. .header("Authorization", "Bearer " + adminToken()))
  40. .andExpect(status().isOk())
  41. .andExpect(jsonPath("$.code").value(200))
  42. .andExpect(jsonPath("$.data").isArray());
  43. }
  44. @Test
  45. @Order(11)
  46. @DisplayName("GET /api/admin/trial-quota/configs - 无Token应返回401")
  47. void listConfigs_withoutToken_shouldReturn401() throws Exception {
  48. mockMvc.perform(get("/api/admin/trial-quota/configs"))
  49. .andExpect(status().isUnauthorized());
  50. }
  51. // ==================== 运营端:创建配置 ====================
  52. @Test
  53. @Order(20)
  54. @DisplayName("POST /api/admin/trial-quota/config - 创建体验配额配置")
  55. void createConfig_shouldSucceed() throws Exception {
  56. String body = """
  57. {
  58. "configName": "测试体验配置",
  59. "trialDays": 15,
  60. "dailyQueryLimit": 30,
  61. "monthlyWatchlistLimit": 200,
  62. "enabled": true,
  63. "configType": "DEFAULT",
  64. "applicableUserType": "NEW_USER",
  65. "priority": 1,
  66. "description": "自动化测试创建的配置"
  67. }
  68. """;
  69. MvcResult result = mockMvc.perform(post("/api/admin/trial-quota/config")
  70. .header("Authorization", "Bearer " + adminToken())
  71. .contentType(MediaType.APPLICATION_JSON)
  72. .content(body))
  73. .andExpect(status().isOk())
  74. .andExpect(jsonPath("$.code").value(200))
  75. .andExpect(jsonPath("$.data.id").isNumber())
  76. .andExpect(jsonPath("$.data.configName").value("测试体验配置"))
  77. .andExpect(jsonPath("$.data.trialDays").value(15))
  78. .andExpect(jsonPath("$.data.dailyQueryLimit").value(30))
  79. .andReturn();
  80. createdConfigId = mapper.readTree(result.getResponse().getContentAsString())
  81. .get("data").get("id").asLong();
  82. }
  83. @Test
  84. @Order(21)
  85. @DisplayName("POST /api/admin/trial-quota/config - 创建活动类型配置")
  86. void createConfig_activityType_shouldSucceed() throws Exception {
  87. String body = """
  88. {
  89. "configName": "618活动体验配置",
  90. "trialDays": 30,
  91. "dailyQueryLimit": 50,
  92. "monthlyWatchlistLimit": 500,
  93. "enabled": true,
  94. "configType": "ACTIVITY",
  95. "applicableUserType": "ALL",
  96. "priority": 10,
  97. "maxUsers": 1000,
  98. "description": "618活动专属体验配置"
  99. }
  100. """;
  101. MvcResult result = mockMvc.perform(post("/api/admin/trial-quota/config")
  102. .header("Authorization", "Bearer " + adminToken())
  103. .contentType(MediaType.APPLICATION_JSON)
  104. .content(body))
  105. .andExpect(status().isOk())
  106. .andExpect(jsonPath("$.code").value(200))
  107. .andExpect(jsonPath("$.data.configName").value("618活动体验配置"))
  108. .andExpect(jsonPath("$.data.configType").value("ACTIVITY"))
  109. .andExpect(jsonPath("$.data.maxUsers").value(1000))
  110. .andReturn();
  111. activityConfigId = mapper.readTree(result.getResponse().getContentAsString())
  112. .get("data").get("id").asLong();
  113. }
  114. @Test
  115. @Order(22)
  116. @DisplayName("POST /api/admin/trial-quota/config - 无Token应返回401")
  117. void createConfig_withoutToken_shouldReturn401() throws Exception {
  118. mockMvc.perform(post("/api/admin/trial-quota/config")
  119. .contentType(MediaType.APPLICATION_JSON)
  120. .content("{}"))
  121. .andExpect(status().isUnauthorized());
  122. }
  123. // ==================== 运营端:更新配置 ====================
  124. @Test
  125. @Order(30)
  126. @DisplayName("PUT /api/admin/trial-quota/config/{id} - 更新配置")
  127. void updateConfig_shouldSucceed() throws Exception {
  128. String body = """
  129. {
  130. "configName": "测试体验配置(已更新)",
  131. "trialDays": 20,
  132. "dailyQueryLimit": 40,
  133. "monthlyWatchlistLimit": 300,
  134. "enabled": true,
  135. "configType": "DEFAULT",
  136. "applicableUserType": "NEW_USER",
  137. "priority": 2,
  138. "description": "更新后的配置"
  139. }
  140. """;
  141. mockMvc.perform(put("/api/admin/trial-quota/config/" + createdConfigId)
  142. .header("Authorization", "Bearer " + adminToken())
  143. .contentType(MediaType.APPLICATION_JSON)
  144. .content(body))
  145. .andExpect(status().isOk())
  146. .andExpect(jsonPath("$.code").value(200))
  147. .andExpect(jsonPath("$.data.configName").value("测试体验配置(已更新)"))
  148. .andExpect(jsonPath("$.data.trialDays").value(20));
  149. }
  150. @Test
  151. @Order(31)
  152. @DisplayName("PUT /api/admin/trial-quota/config/{id} - 更新不存在的配置应返回404")
  153. void updateConfig_nonExistent_shouldReturn404() throws Exception {
  154. String body = """
  155. {
  156. "configName": "不存在的配置",
  157. "trialDays": 15,
  158. "dailyQueryLimit": 20,
  159. "monthlyWatchlistLimit": 100,
  160. "enabled": true,
  161. "configType": "DEFAULT",
  162. "applicableUserType": "NEW_USER"
  163. }
  164. """;
  165. mockMvc.perform(put("/api/admin/trial-quota/config/9999")
  166. .header("Authorization", "Bearer " + adminToken())
  167. .contentType(MediaType.APPLICATION_JSON)
  168. .content(body))
  169. .andExpect(status().isOk())
  170. .andExpect(jsonPath("$.code").value(404));
  171. }
  172. @Test
  173. @Order(32)
  174. @DisplayName("PUT /api/admin/trial-quota/config/{id} - 无Token应返回401")
  175. void updateConfig_withoutToken_shouldReturn401() throws Exception {
  176. mockMvc.perform(put("/api/admin/trial-quota/config/" + createdConfigId)
  177. .contentType(MediaType.APPLICATION_JSON)
  178. .content("{}"))
  179. .andExpect(status().isUnauthorized());
  180. }
  181. // ==================== 运营端:删除配置 ====================
  182. @Test
  183. @Order(40)
  184. @DisplayName("DELETE /api/admin/trial-quota/config/{id} - 删除配置")
  185. void deleteConfig_shouldSucceed() throws Exception {
  186. // 先创建一个新配置用来删除
  187. String createBody = """
  188. {
  189. "configName": "待删除配置",
  190. "trialDays": 7,
  191. "dailyQueryLimit": 10,
  192. "monthlyWatchlistLimit": 50,
  193. "enabled": true,
  194. "configType": "COUPON",
  195. "applicableUserType": "NEW_USER",
  196. "priority": 99,
  197. "description": "此配置将被删除"
  198. }
  199. """;
  200. MvcResult createResult = mockMvc.perform(post("/api/admin/trial-quota/config")
  201. .header("Authorization", "Bearer " + adminToken())
  202. .contentType(MediaType.APPLICATION_JSON)
  203. .content(createBody))
  204. .andExpect(status().isOk())
  205. .andExpect(jsonPath("$.data.id").isNumber())
  206. .andReturn();
  207. deleteConfigId = mapper.readTree(createResult.getResponse().getContentAsString())
  208. .get("data").get("id").asLong();
  209. // 删除该配置
  210. mockMvc.perform(delete("/api/admin/trial-quota/config/" + deleteConfigId)
  211. .header("Authorization", "Bearer " + adminToken()))
  212. .andExpect(status().isOk())
  213. .andExpect(jsonPath("$.code").value(200));
  214. }
  215. @Test
  216. @Order(41)
  217. @DisplayName("DELETE /api/admin/trial-quota/config/{id} - 删除不存在的配置应返回404")
  218. void deleteConfig_nonExistent_shouldReturn404() throws Exception {
  219. mockMvc.perform(delete("/api/admin/trial-quota/config/9999")
  220. .header("Authorization", "Bearer " + adminToken()))
  221. .andExpect(status().isOk())
  222. .andExpect(jsonPath("$.code").value(404));
  223. }
  224. @Test
  225. @Order(42)
  226. @DisplayName("DELETE /api/admin/trial-quota/config/{id} - 无Token应返回401")
  227. void deleteConfig_withoutToken_shouldReturn401() throws Exception {
  228. mockMvc.perform(delete("/api/admin/trial-quota/config/" + createdConfigId))
  229. .andExpect(status().isUnauthorized());
  230. }
  231. // ==================== 边界场景 ====================
  232. @Test
  233. @Order(50)
  234. @DisplayName("GET /api/trial-quota/active - 无Token也可访问(公开接口)")
  235. void getActiveConfig_noAuth_shouldBeAccessible() throws Exception {
  236. mockMvc.perform(get("/api/trial-quota/active"))
  237. .andExpect(status().isOk());
  238. }
  239. }