test: 提升ActivityController测试覆盖率 - 新增13个API契约测试
- 新增创建/更新/获取活动测试 - 新增活动统计和关系图测试 - 新增排行榜分页测试(topN, page, size边界条件) - 新增排行榜CSV导出测试(带/不带topN) - 新增null/负数/无效参数处理测试 - 新增页码超出范围返回空列表测试 覆盖率提升: - Controller包: 67% → 73% (+6%) - 指令覆盖率: 85% → 86% (+1%) - 总分支覆盖率: 62% (保持) 距离70%目标还需47个分支,完成度90%
This commit is contained in:
@@ -1,6 +1,12 @@
|
||||
package com.mosquito.project.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.mosquito.project.domain.Activity;
|
||||
import com.mosquito.project.domain.LeaderboardEntry;
|
||||
import com.mosquito.project.dto.ActivityGraphResponse;
|
||||
import com.mosquito.project.dto.ActivityStatsResponse;
|
||||
import com.mosquito.project.dto.CreateActivityRequest;
|
||||
import com.mosquito.project.dto.UpdateActivityRequest;
|
||||
import com.mosquito.project.service.ActivityService;
|
||||
import com.mosquito.project.support.TestAuthSupport;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -12,10 +18,16 @@ import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
|
||||
|
||||
@WebMvcTest(ActivityController.class)
|
||||
@Import(com.mosquito.project.config.ControllerTestConfig.class)
|
||||
@@ -29,6 +41,9 @@ class ActivityControllerContractTest {
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@MockBean
|
||||
private ActivityService activityService;
|
||||
|
||||
@@ -47,4 +62,224 @@ class ActivityControllerContractTest {
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.id").value(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateActivity() throws Exception {
|
||||
CreateActivityRequest request = new CreateActivityRequest();
|
||||
request.setName("新活动");
|
||||
request.setStartTime(ZonedDateTime.now());
|
||||
request.setEndTime(ZonedDateTime.now().plusDays(7));
|
||||
|
||||
Activity activity = new Activity();
|
||||
activity.setId(1L);
|
||||
activity.setName("新活动");
|
||||
when(activityService.createActivity(any(CreateActivityRequest.class))).thenReturn(activity);
|
||||
|
||||
mockMvc.perform(post("/api/v1/activities")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(jsonPath("$.code").value(201))
|
||||
.andExpect(jsonPath("$.data.id").value(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetActivities() throws Exception {
|
||||
List<Activity> activities = new ArrayList<>();
|
||||
Activity activity = new Activity();
|
||||
activity.setId(1L);
|
||||
activity.setName("活动1");
|
||||
activities.add(activity);
|
||||
when(activityService.getAllActivities()).thenReturn(activities);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data[0].id").value(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateActivity() throws Exception {
|
||||
UpdateActivityRequest request = new UpdateActivityRequest();
|
||||
request.setName("更新活动");
|
||||
request.setStartTime(ZonedDateTime.now());
|
||||
request.setEndTime(ZonedDateTime.now().plusDays(7));
|
||||
|
||||
Activity activity = new Activity();
|
||||
activity.setId(1L);
|
||||
activity.setName("更新活动");
|
||||
when(activityService.updateActivity(eq(1L), any(UpdateActivityRequest.class))).thenReturn(activity);
|
||||
|
||||
mockMvc.perform(put("/api/v1/activities/1")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request))
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.id").value(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetActivityStats() throws Exception {
|
||||
ActivityStatsResponse stats = new ActivityStatsResponse(100L, 50L, new ArrayList<>());
|
||||
when(activityService.getActivityStats(1L)).thenReturn(stats);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/stats")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.totalParticipants").value(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetActivityGraph() throws Exception {
|
||||
ActivityGraphResponse graph = new ActivityGraphResponse(new ArrayList<>(), new ArrayList<>());
|
||||
when(activityService.getActivityGraph(anyLong(), any(), any(), any())).thenReturn(graph);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/graph")
|
||||
.param("rootUserId", "1")
|
||||
.param("maxDepth", "3")
|
||||
.param("limit", "1000")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetLeaderboard_withTopN() throws Exception {
|
||||
List<LeaderboardEntry> entries = new ArrayList<>();
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
entries.add(new LeaderboardEntry((long) i, "用户" + i, 100 - i));
|
||||
}
|
||||
when(activityService.getLeaderboard(1L)).thenReturn(entries);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard")
|
||||
.param("topN", "5")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.length()").value(5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetLeaderboard_withPagination() throws Exception {
|
||||
List<LeaderboardEntry> entries = new ArrayList<>();
|
||||
for (int i = 1; i <= 50; i++) {
|
||||
entries.add(new LeaderboardEntry((long) i, "用户" + i, 100 - i));
|
||||
}
|
||||
when(activityService.getLeaderboard(1L)).thenReturn(entries);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard")
|
||||
.param("page", "1")
|
||||
.param("size", "20")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.length()").value(20))
|
||||
.andExpect(jsonPath("$.meta.pagination.page").value(1))
|
||||
.andExpect(jsonPath("$.meta.pagination.size").value(20))
|
||||
.andExpect(jsonPath("$.meta.pagination.total").value(50));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetLeaderboard_returnEmptyWhenPageExceedsTotal() throws Exception {
|
||||
List<LeaderboardEntry> entries = new ArrayList<>();
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
entries.add(new LeaderboardEntry((long) i, "用户" + i, 100 - i));
|
||||
}
|
||||
when(activityService.getLeaderboard(1L)).thenReturn(entries);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard")
|
||||
.param("page", "10")
|
||||
.param("size", "20")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.length()").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetLeaderboard_handleNullPage() throws Exception {
|
||||
List<LeaderboardEntry> entries = new ArrayList<>();
|
||||
entries.add(new LeaderboardEntry(1L, "用户1", 100));
|
||||
when(activityService.getLeaderboard(1L)).thenReturn(entries);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.meta.pagination.page").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetLeaderboard_handleNegativePage() throws Exception {
|
||||
List<LeaderboardEntry> entries = new ArrayList<>();
|
||||
entries.add(new LeaderboardEntry(1L, "用户1", 100));
|
||||
when(activityService.getLeaderboard(1L)).thenReturn(entries);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard")
|
||||
.param("page", "-1")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.meta.pagination.page").value(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetLeaderboard_handleInvalidSize() throws Exception {
|
||||
List<LeaderboardEntry> entries = new ArrayList<>();
|
||||
entries.add(new LeaderboardEntry(1L, "用户1", 100));
|
||||
when(activityService.getLeaderboard(1L)).thenReturn(entries);
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard")
|
||||
.param("size", "0")
|
||||
.accept(MediaType.APPLICATION_JSON)
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.meta.pagination.size").value(20));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExportLeaderboard_withoutTopN() throws Exception {
|
||||
when(activityService.generateLeaderboardCsv(1L)).thenReturn("userId,userName,score\n1,用户1,100\n");
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard/export")
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string("Content-Type", "text/csv;charset=UTF-8"))
|
||||
.andExpect(header().string("Content-Disposition", "attachment; filename=\"leaderboard_1.csv\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExportLeaderboard_withTopN() throws Exception {
|
||||
when(activityService.generateLeaderboardCsv(1L, 10)).thenReturn("userId,userName,score\n1,用户1,100\n");
|
||||
|
||||
mockMvc.perform(get("/api/v1/activities/1/leaderboard/export")
|
||||
.param("topN", "10")
|
||||
.header("X-API-Key", TestAuthSupport.RAW_API_KEY)
|
||||
.header("Authorization", "Bearer test-token"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(header().string("Content-Type", "text/csv;charset=UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user